File indexing completed on 2024-04-28 15:29:48

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2005 Brad Hards <bradh@frogmouth.net>
0004     SPDX-FileCopyrightText: 2006 Thiago Macieira <thiago@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "ktoolinvocation.h"
0010 #ifdef QT_DBUS_LIB
0011 #include "klauncher_iface.h"
0012 #include <KDEInitInterface>
0013 #endif
0014 #include "kservice.h"
0015 #include <KLocalizedString>
0016 
0017 #include <QCoreApplication>
0018 #include <QDebug>
0019 #include <QStandardPaths>
0020 #include <QThread>
0021 #include <QUrl>
0022 #include <QUrlQuery>
0023 
0024 #include <errno.h> // for EINVAL
0025 
0026 class KToolInvocationSingleton
0027 {
0028 public:
0029     KToolInvocation instance;
0030 };
0031 
0032 Q_GLOBAL_STATIC(KToolInvocationSingleton, s_self)
0033 
0034 KToolInvocation *KToolInvocation::self()
0035 {
0036     return &s_self()->instance;
0037 }
0038 
0039 KToolInvocation::KToolInvocation()
0040     : QObject(nullptr)
0041     , d(nullptr)
0042 {
0043 }
0044 
0045 KToolInvocation::~KToolInvocation()
0046 {
0047 }
0048 
0049 static void printError(const QString &text, QString *error)
0050 {
0051     if (error) {
0052         *error = text;
0053     } else {
0054         qWarning() << text;
0055     }
0056 }
0057 
0058 bool KToolInvocation::isMainThreadActive(QString *error)
0059 {
0060     if (QCoreApplication::instance() && QCoreApplication::instance()->thread() != QThread::currentThread()) {
0061         printError(i18n("Function must be called from the main thread."), error);
0062         return false;
0063     }
0064 
0065     return true;
0066 }
0067 
0068 int KToolInvocation::startServiceInternal(const char *_function,
0069                                           const QString &_name,
0070                                           const QStringList &URLs,
0071                                           QString *error,
0072                                           QString *serviceName,
0073                                           int *pid,
0074                                           const QByteArray &startup_id,
0075                                           bool noWait,
0076                                           const QString &workdir,
0077                                           const QStringList &envs)
0078 {
0079 #ifdef QT_DBUS_LIB
0080     QString function = QLatin1String(_function);
0081     KToolInvocation::ensureKdeinitRunning();
0082     QDBusMessage msg =
0083         QDBusMessage::createMethodCall(QStringLiteral("org.kde.klauncher5"), QStringLiteral("/KLauncher"), QStringLiteral("org.kde.KLauncher"), function);
0084     msg << _name << URLs;
0085     if (function == QLatin1String("kdeinit_exec_with_workdir")) {
0086         msg << workdir;
0087     }
0088     // make sure there is id, so that user timestamp exists
0089     QByteArray s = startup_id;
0090     QStringList envCopy(envs);
0091     Q_EMIT kapplication_hook(envCopy, s);
0092     msg << envCopy;
0093     msg << QString::fromLatin1(s);
0094     if (!function.startsWith(QLatin1String("kdeinit_exec"))) {
0095         msg << noWait;
0096     }
0097 
0098     QDBusMessage reply = QDBusConnection::sessionBus().call(msg, QDBus::Block, INT_MAX);
0099     if (reply.type() != QDBusMessage::ReplyMessage) {
0100         QDBusReply<QString> replyObj(reply);
0101         if (replyObj.error().type() == QDBusError::NoReply) {
0102             printError(i18n("Error launching %1. Either KLauncher is not running anymore, or it failed to start the application.", _name), error);
0103         } else {
0104             const QString rpl = reply.arguments().count() > 0 ? reply.arguments().at(0).toString() : reply.errorMessage();
0105             printError(i18n("KLauncher could not be reached via D-Bus. Error when calling %1:\n%2\n", function, rpl), error);
0106         }
0107         // qDebug() << reply;
0108         return EINVAL;
0109     }
0110 
0111     if (noWait) {
0112         return 0;
0113     }
0114 
0115     Q_ASSERT(reply.arguments().count() == 4);
0116     if (serviceName) {
0117         *serviceName = reply.arguments().at(1).toString();
0118     }
0119     if (error) {
0120         *error = reply.arguments().at(2).toString();
0121     }
0122     if (pid) {
0123         *pid = reply.arguments().at(3).toInt();
0124     }
0125     return reply.arguments().at(0).toInt();
0126 #else
0127     return ENOTSUP;
0128 #endif
0129 }
0130 
0131 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0132 int KToolInvocation::startServiceByName(const QString &_name,
0133                                         const QString &URL,
0134                                         QString *error,
0135                                         QString *serviceName,
0136                                         int *pid,
0137                                         const QByteArray &startup_id,
0138                                         bool noWait)
0139 {
0140     if (!isMainThreadActive(error)) {
0141         return EINVAL;
0142     }
0143 
0144     QStringList URLs;
0145     if (!URL.isEmpty()) {
0146         URLs.append(URL);
0147     }
0148     return self()->startServiceInternal("start_service_by_name", _name, URLs, error, serviceName, pid, startup_id, noWait);
0149 }
0150 #endif
0151 
0152 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0153 int KToolInvocation::startServiceByName(const QString &_name,
0154                                         const QStringList &URLs,
0155                                         QString *error,
0156                                         QString *serviceName,
0157                                         int *pid,
0158                                         const QByteArray &startup_id,
0159                                         bool noWait)
0160 {
0161     if (!isMainThreadActive(error)) {
0162         return EINVAL;
0163     }
0164 
0165     return self()->startServiceInternal("start_service_by_name", _name, URLs, error, serviceName, pid, startup_id, noWait);
0166 }
0167 #endif
0168 
0169 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 82)
0170 int KToolInvocation::startServiceByDesktopPath(const QString &_name,
0171                                                const QString &URL,
0172                                                QString *error,
0173                                                QString *serviceName,
0174                                                int *pid,
0175                                                const QByteArray &startup_id,
0176                                                bool noWait)
0177 {
0178     if (!isMainThreadActive(error)) {
0179         return EINVAL;
0180     }
0181 
0182     QStringList URLs;
0183     if (!URL.isEmpty()) {
0184         URLs.append(URL);
0185     }
0186     return self()->startServiceInternal("start_service_by_desktop_path", _name, URLs, error, serviceName, pid, startup_id, noWait);
0187 }
0188 #endif
0189 
0190 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 82)
0191 int KToolInvocation::startServiceByDesktopPath(const QString &_name,
0192                                                const QStringList &URLs,
0193                                                QString *error,
0194                                                QString *serviceName,
0195                                                int *pid,
0196                                                const QByteArray &startup_id,
0197                                                bool noWait)
0198 {
0199     if (!isMainThreadActive(error)) {
0200         return EINVAL;
0201     }
0202 
0203     return self()->startServiceInternal("start_service_by_desktop_path", _name, URLs, error, serviceName, pid, startup_id, noWait);
0204 }
0205 #endif
0206 
0207 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 82)
0208 int KToolInvocation::startServiceByDesktopName(const QString &_name,
0209                                                const QString &URL,
0210                                                QString *error,
0211                                                QString *serviceName,
0212                                                int *pid,
0213                                                const QByteArray &startup_id,
0214                                                bool noWait)
0215 {
0216     if (!isMainThreadActive(error)) {
0217         return EINVAL;
0218     }
0219 
0220     QStringList URLs;
0221     if (!URL.isEmpty()) {
0222         URLs.append(URL);
0223     }
0224     return self()->startServiceInternal("start_service_by_desktop_name", _name, URLs, error, serviceName, pid, startup_id, noWait);
0225 }
0226 #endif
0227 
0228 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 82)
0229 int KToolInvocation::startServiceByDesktopName(const QString &_name,
0230                                                const QStringList &URLs,
0231                                                QString *error,
0232                                                QString *serviceName,
0233                                                int *pid,
0234                                                const QByteArray &startup_id,
0235                                                bool noWait)
0236 {
0237     if (!isMainThreadActive(error)) {
0238         return EINVAL;
0239     }
0240 
0241     return self()->startServiceInternal("start_service_by_desktop_name", _name, URLs, error, serviceName, pid, startup_id, noWait);
0242 }
0243 #endif
0244 
0245 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 83)
0246 int KToolInvocation::kdeinitExec(const QString &name, const QStringList &args, QString *error, int *pid, const QByteArray &startup_id)
0247 {
0248     if (!isMainThreadActive(error)) {
0249         return EINVAL;
0250     }
0251 
0252     return self()->startServiceInternal("kdeinit_exec", name, args, error, nullptr, pid, startup_id, false);
0253 }
0254 #endif
0255 
0256 int KToolInvocation::kdeinitExecWait(const QString &name, const QStringList &args, QString *error, int *pid, const QByteArray &startup_id)
0257 {
0258     if (!isMainThreadActive(error)) {
0259         return EINVAL;
0260     }
0261 
0262     return self()->startServiceInternal("kdeinit_exec_wait", name, args, error, nullptr, pid, startup_id, false);
0263 }
0264 
0265 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0266 void KToolInvocation::invokeMailer(const QString &address, const QString &subject, const QByteArray &startup_id)
0267 {
0268     if (!isMainThreadActive()) {
0269         return;
0270     }
0271 
0272     invokeMailer(address, QString(), QString(), subject, QString(), QString(), QStringList(), startup_id);
0273 }
0274 #endif
0275 
0276 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0277 void KToolInvocation::invokeMailer(const QUrl &mailtoURL, const QByteArray &startup_id, bool allowAttachments)
0278 {
0279     if (!isMainThreadActive()) {
0280         return;
0281     }
0282 
0283     QString address = mailtoURL.path();
0284     QString subject;
0285     QString cc;
0286     QString bcc;
0287     QString body;
0288 
0289     QList<QPair<QString, QString>> queryItems = QUrlQuery(mailtoURL).queryItems();
0290     const QChar comma = QChar::fromLatin1(',');
0291     QStringList attachURLs;
0292     for (int i = 0; i < queryItems.count(); ++i) {
0293         const QString q = queryItems.at(i).first.toLower();
0294         const QString value = queryItems.at(i).second;
0295         if (q == QLatin1String("subject")) {
0296             subject = value;
0297         } else if (q == QLatin1String("cc")) {
0298             cc = cc.isEmpty() ? value : cc + comma + value;
0299         } else if (q == QLatin1String("bcc")) {
0300             bcc = bcc.isEmpty() ? value : bcc + comma + value;
0301         } else if (q == QLatin1String("body")) {
0302             body = value;
0303         } else if (allowAttachments && q == QLatin1String("attach")) {
0304             attachURLs.push_back(value);
0305         } else if (allowAttachments && q == QLatin1String("attachment")) {
0306             attachURLs.push_back(value);
0307         } else if (q == QLatin1String("to")) {
0308             address = address.isEmpty() ? value : address + comma + value;
0309         }
0310     }
0311 
0312     invokeMailer(address, cc, bcc, subject, body, QString(), attachURLs, startup_id);
0313 }
0314 #endif
0315 
0316 void KToolInvocation::ensureKdeinitRunning()
0317 {
0318 #ifdef QT_DBUS_LIB
0319     KDEInitInterface::ensureKdeinitRunning();
0320 #endif
0321 }
0322 
0323 #include "moc_ktoolinvocation.cpp"