File indexing completed on 2024-04-21 04:56:46

0001 /**
0002  * SPDX-FileCopyrightText: 2014 Yuri Samoilenko <kinnalru@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include <QApplication>
0008 #include <QCommandLineOption>
0009 #include <QCommandLineParser>
0010 #include <QDBusMessage>
0011 #include <QIcon>
0012 #include <QProcess>
0013 #include <QSessionManager>
0014 #include <QStandardPaths>
0015 #include <QTimer>
0016 
0017 #ifdef Q_OS_WIN
0018 #include <Windows.h>
0019 #endif
0020 
0021 #include <KAboutData>
0022 #include <KDBusService>
0023 #include <KIO/Global>
0024 #include <KIO/JobTracker>
0025 #include <KLocalizedString>
0026 #include <KNotification>
0027 #include <KWindowSystem>
0028 
0029 #include <dbushelper.h>
0030 
0031 #include "core/backends/pairinghandler.h"
0032 #include "core/daemon.h"
0033 #include "core/device.h"
0034 #include "core/openconfig.h"
0035 #include "kdeconnect-version.h"
0036 #include "kdeconnectd_debug.h"
0037 
0038 class DesktopDaemon : public Daemon
0039 {
0040     Q_OBJECT
0041     Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.daemon")
0042 public:
0043     DesktopDaemon(QObject *parent = nullptr)
0044         : Daemon(parent)
0045     {
0046         qApp->setWindowIcon(QIcon(QStringLiteral(":/icons/kdeconnect/kdeconnect.png")));
0047     }
0048 
0049     void askPairingConfirmation(Device *device) override
0050     {
0051         KNotification *notification = new KNotification(QStringLiteral("pairingRequest"), KNotification::NotificationFlag::Persistent);
0052         QTimer::singleShot(PairingHandler::pairingTimeoutMsec, notification, &KNotification::close);
0053         notification->setIconName(QStringLiteral("dialog-information"));
0054         notification->setComponentName(QStringLiteral("kdeconnect"));
0055         notification->setTitle(QStringLiteral("KDE Connect"));
0056         notification->setText(
0057             i18n("Pairing request from %1\nKey: %2...", device->name().toHtmlEscaped(), QString::fromUtf8(device->verificationKey().left(8))));
0058         QString deviceId = device->id();
0059         auto openSettings = [deviceId, notification] {
0060             OpenConfig oc;
0061             oc.setXdgActivationToken(notification->xdgActivationToken());
0062             oc.openConfiguration(deviceId);
0063         };
0064 
0065 #if QT_VERSION_MAJOR == 6
0066         KNotificationAction *openSettingsAction = notification->addDefaultAction(i18n("Open"));
0067         connect(openSettingsAction, &KNotificationAction::activated, openSettings);
0068 
0069         KNotificationAction *acceptAction = notification->addAction(i18n("Accept"));
0070         connect(acceptAction, &KNotificationAction::activated, device, &Device::acceptPairing);
0071 
0072         KNotificationAction *rejectAction = notification->addAction(i18n("Reject"));
0073         connect(rejectAction, &KNotificationAction::activated, device, &Device::cancelPairing);
0074 
0075         KNotificationAction *viewKeyAction = notification->addAction(i18n("View key"));
0076         connect(viewKeyAction, &KNotificationAction::activated, openSettings);
0077 #else
0078         notification->setDefaultAction(i18n("Open"));
0079         notification->setActions(QStringList() << i18n("Accept") << i18n("Reject") << i18n("View key"));
0080         connect(notification, &KNotification::action1Activated, device, &Device::acceptPairing);
0081         connect(notification, &KNotification::action2Activated, device, &Device::cancelPairing);
0082         connect(notification, &KNotification::action3Activated, openSettings);
0083         connect(notification, &KNotification::activated, openSettings);
0084 #endif
0085 
0086         notification->sendEvent();
0087     }
0088 
0089     void reportError(const QString &title, const QString &description) override
0090     {
0091         qCWarning(KDECONNECT_DAEMON) << title << ":" << description;
0092         KNotification::event(KNotification::Error, title, description);
0093     }
0094 
0095     KJobTrackerInterface *jobTracker() override
0096     {
0097         return KIO::getJobTracker();
0098     }
0099 
0100     Q_SCRIPTABLE void sendSimpleNotification(const QString &eventId, const QString &title, const QString &text, const QString &iconName) override
0101     {
0102         KNotification *notification = new KNotification(eventId); // KNotification::Persistent
0103         notification->setIconName(iconName);
0104         notification->setComponentName(QStringLiteral("kdeconnect"));
0105         notification->setTitle(title);
0106         notification->setText(text);
0107         notification->sendEvent();
0108     }
0109 
0110     void quit() override
0111     {
0112         QApplication::quit();
0113     }
0114 };
0115 
0116 // Copied from plasma-workspace/libkworkspace/kworkspace.cpp
0117 static void detectPlatform(int argc, char **argv)
0118 {
0119     if (qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) {
0120         return;
0121     }
0122     for (int i = 0; i < argc; i++) {
0123         if (qstrcmp(argv[i], "-platform") == 0 || qstrcmp(argv[i], "--platform") == 0 || QByteArray(argv[i]).startsWith("-platform=")
0124             || QByteArray(argv[i]).startsWith("--platform=")) {
0125             return;
0126         }
0127     }
0128     const QByteArray sessionType = qgetenv("XDG_SESSION_TYPE");
0129     if (sessionType.isEmpty()) {
0130         return;
0131     }
0132     if (qstrcmp(sessionType.constData(), "wayland") == 0) {
0133         qputenv("QT_QPA_PLATFORM", "wayland");
0134     } else if (qstrcmp(sessionType.constData(), "x11") == 0) {
0135         qputenv("QT_QPA_PLATFORM", "xcb");
0136     }
0137 }
0138 
0139 int main(int argc, char *argv[])
0140 {
0141 #ifdef Q_OS_WIN
0142     // If ran from a console, redirect the output there
0143     if (AttachConsole(ATTACH_PARENT_PROCESS)) {
0144         freopen("CONOUT$", "w", stdout);
0145         freopen("CONOUT$", "w", stderr);
0146     }
0147 #endif
0148 
0149     detectPlatform(argc, argv);
0150     QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
0151     QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0152 #if QT_VERSION_MAJOR == 6
0153     QGuiApplication::setQuitLockEnabled(false);
0154 #endif
0155 
0156     QApplication app(argc, argv);
0157     KAboutData aboutData(QStringLiteral("kdeconnect.daemon"),
0158                          i18n("KDE Connect Daemon"),
0159                          QStringLiteral(KDECONNECT_VERSION_STRING),
0160                          i18n("KDE Connect Daemon"),
0161                          KAboutLicense::GPL);
0162     KAboutData::setApplicationData(aboutData);
0163     app.setQuitOnLastWindowClosed(false);
0164 
0165     QCommandLineParser parser;
0166     QCommandLineOption replaceOption({QStringLiteral("replace")}, i18n("Replace an existing instance"));
0167     parser.addOption(replaceOption);
0168 #ifdef Q_OS_MAC
0169     QCommandLineOption macosPrivateDBusOption({QStringLiteral("use-private-dbus")},
0170                                               i18n("Launch a private D-Bus daemon with kdeconnectd (macOS test-purpose only)"));
0171     parser.addOption(macosPrivateDBusOption);
0172 #endif
0173     aboutData.setupCommandLine(&parser);
0174 
0175     parser.process(app);
0176 #ifdef Q_OS_MAC
0177     if (parser.isSet(macosPrivateDBusOption)) {
0178         DBusHelper::launchDBusDaemon();
0179     }
0180 #endif
0181     aboutData.processCommandLine(&parser);
0182     if (parser.isSet(replaceOption)) {
0183         auto message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"),
0184                                                       QStringLiteral("/MainApplication"),
0185                                                       QStringLiteral("org.qtproject.Qt.QCoreApplication"),
0186                                                       QStringLiteral("quit"));
0187         QDBusConnection::sessionBus().call(message); // deliberately block until it's done, so we register the name after the app quits
0188     }
0189 
0190     KDBusService dbusService(KDBusService::Unique);
0191 
0192     DesktopDaemon daemon;
0193 
0194 #ifdef Q_OS_WIN
0195     // make sure indicator shows up in the tray whenever daemon is spawned
0196     QProcess::startDetached(QStringLiteral("kdeconnect-indicator.exe"), QStringList());
0197 #endif
0198 
0199     // kdeconnectd is autostarted, so disable session management to speed up startup
0200     auto disableSessionManagement = [](QSessionManager &sm) {
0201         sm.setRestartHint(QSessionManager::RestartNever);
0202     };
0203     QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement);
0204     QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement);
0205 
0206     qSetMessagePattern(QStringLiteral("%{time} %{category}: %{message}"));
0207 
0208     return app.exec();
0209 }
0210 
0211 #include "kdeconnectd.moc"