KIO

kemailclientlauncherjob.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2021 David Faure <faure@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "kemailclientlauncherjob.h"
9
10#include <KApplicationTrader>
11#include <KConfigGroup>
12#include <KLocalizedString>
13#include <KMacroExpander>
14#include <KService>
15#include <KSharedConfig>
16#include <KShell>
17#include <QProcessEnvironment>
18#include <QUrlQuery>
19
20#include "desktopexecparser.h"
21#include <KIO/ApplicationLauncherJob>
22#include <KIO/CommandLauncherJob>
23
24#ifdef Q_OS_WIN
25#include <windows.h> // Must be included before shellapi.h
26
27#include <shellapi.h>
28#endif
29
30class KEMailClientLauncherJobPrivate
31{
32public:
33 QStringList m_to;
34 QStringList m_cc;
35 QStringList m_bcc;
36 QString m_subject;
37 QString m_body;
38 QList<QUrl> m_attachments;
39
40 QByteArray m_startupId;
41};
42
44 : KJob(parent)
45 , d(new KEMailClientLauncherJobPrivate)
46{
47}
48
50
52{
53 d->m_to = to;
54}
55
57{
58 d->m_cc = cc;
59}
60
62{
63 d->m_bcc = bcc;
64}
65
67{
68 d->m_subject = subject;
69}
70
72{
73 d->m_body = body;
74}
75
77{
78 d->m_attachments = urls;
79}
80
82{
83 d->m_startupId = startupId;
84}
85
87{
88#ifndef Q_OS_WIN
89 KService::Ptr service = KApplicationTrader::preferredService(QStringLiteral("x-scheme-handler/mailto"));
90 if (!service) {
91 setError(KJob::UserDefinedError);
92 setErrorText(i18n("No mail client found"));
93 emitDelayedResult();
94 return;
95 }
96 const QString entryPath = service->entryPath().toLower();
97 if (entryPath.contains(QLatin1String("thunderbird")) || entryPath.contains(QLatin1String("dovecot"))) {
99 auto *subjob = new KIO::CommandLauncherJob(exec, thunderbirdArguments(), this);
100 subjob->setStartupId(d->m_startupId);
102 subjob->start();
103 } else {
104 auto *subjob = new KIO::ApplicationLauncherJob(service, this);
105 subjob->setUrls({mailToUrl()});
106 subjob->setStartupId(d->m_startupId);
108 subjob->start();
109 }
110#else
111 const QString url = mailToUrl().toString();
112 const QString sOpen = QStringLiteral("open");
113 ShellExecuteW(0, (LPCWSTR)sOpen.utf16(), (LPCWSTR)url.utf16(), 0, 0, SW_NORMAL);
114 emitDelayedResult();
115#endif
116}
117
118void KEMailClientLauncherJob::emitDelayedResult()
119{
120 // Use delayed invocation so the caller has time to connect to the signal
122}
123
124QUrl KEMailClientLauncherJob::mailToUrl() const
125{
126 QUrl url;
128 for (const QString &to : std::as_const(d->m_to)) {
129 if (url.path().isEmpty()) {
130 url.setPath(to);
131 } else {
132 query.addQueryItem(QStringLiteral("to"), to);
133 }
134 }
135 for (const QString &cc : std::as_const(d->m_cc)) {
136 query.addQueryItem(QStringLiteral("cc"), cc);
137 }
138 for (const QString &bcc : std::as_const(d->m_bcc)) {
139 query.addQueryItem(QStringLiteral("bcc"), bcc);
140 }
141 for (const QUrl &url : std::as_const(d->m_attachments)) {
142 query.addQueryItem(QStringLiteral("attach"), url.toString());
143 }
144 if (!d->m_subject.isEmpty()) {
145 query.addQueryItem(QStringLiteral("subject"), d->m_subject);
146 }
147 if (!d->m_body.isEmpty()) {
148 query.addQueryItem(QStringLiteral("body"), d->m_body);
149 }
150 url.setQuery(query);
151 if (!url.path().isEmpty() || url.hasQuery()) {
152 url.setScheme(QStringLiteral("mailto"));
153 }
154 return url;
155}
156
157QStringList KEMailClientLauncherJob::thunderbirdArguments() const
158{
159 // Thunderbird supports mailto URLs, but refuses attachments for security reasons
160 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1613425)
161 // It however supports a "command-line" syntax (also used by xdg-email)
162 // which includes attachments.
163 QString arg;
164 const QChar quote = QLatin1Char('\'');
165 auto addString = [&](const char *token, const QString &str) {
166 if (!str.isEmpty()) {
167 arg += QLatin1String(token) + quote + str + quote;
168 }
169 };
170 auto addList = [&](const char *token, const QStringList &list) {
171 if (!list.isEmpty()) {
172 arg += QLatin1String(token) + quote + list.join(QLatin1Char(',')) + quote;
173 }
174 };
175 addList(",to=", d->m_to);
176 addList(",cc=", d->m_cc);
177 addList(",bcc=", d->m_bcc);
178 addList(",attachment=", QUrl::toStringList(d->m_attachments));
179 addString(",subject=", d->m_subject);
180 addString(",body=", d->m_body);
181
182 QStringList resultArgs{QLatin1String("-compose")};
183 if (!arg.isEmpty()) {
184 resultArgs.push_back(arg.mid(1)); // remove first comma
185 }
186 return resultArgs;
187}
188
189#include "moc_kemailclientlauncherjob.cpp"
void setBcc(const QStringList &bcc)
Sets the email address(es) that will be used in the Bcc field for the email.
void setAttachments(const QList< QUrl > &urls)
Sets attachments for the email.
void start() override
Starts the job.
void setSubject(const QString &subject)
Sets the subject for the email.
KEMailClientLauncherJob(QObject *parent=nullptr)
Creates a KEMailClientLauncherJob.
void setCc(const QStringList &cc)
Sets the email address(es) that will be used in the CC field for the email.
~KEMailClientLauncherJob() override
Destructor.
void setStartupId(const QByteArray &startupId)
Sets the platform-specific startup id of the mail client launch.
void setTo(const QStringList &to)
Sets the email address(es) that will be used in the To field for the email.
void setBody(const QString &body)
Sets the body for the email.
ApplicationLauncherJob runs an application and watches it while running.
CommandLauncherJob runs a command and watches it while running.
static QString executableName(const QString &execLine)
Given a full command line (e.g. the Exec= line from a .desktop file), extract the name of the executa...
bool exec()
void setErrorText(const QString &errorText)
void emitResult()
void result(KJob *job)
void setError(int errorCode)
QString exec() const
QString entryPath() const
QString i18n(const char *text, const TYPE &arg...)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
KSERVICE_EXPORT KService::Ptr preferredService(const QString &mimeType)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
Returns a list of directories associated with this file-class.
bool isEmpty() const const
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString mid(int position, int n) const const
QString toLower() const const
const ushort * utf16() const const
QString join(const QString &separator) const const
QueuedConnection
bool hasQuery() const const
QString path(QUrl::ComponentFormattingOptions options) const const
void setPath(const QString &path, QUrl::ParsingMode mode)
void setQuery(const QString &query, QUrl::ParsingMode mode)
void setScheme(const QString &scheme)
QString toString(QUrl::FormattingOptions options) const const
QStringList toStringList(const QList< QUrl > &urls, QUrl::FormattingOptions options)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:45:23 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.