File indexing completed on 2024-05-12 05:36:52

0001 /*
0002  * SPDX-FileCopyrightText: 2018-2019 Daniel Vrátil <dvratil@kde.org>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "kded_bolt.h"
0008 #include "kded_bolt_debug.h"
0009 
0010 #include "device.h"
0011 
0012 #include <QPointer>
0013 
0014 #include <KLocalizedString>
0015 #include <KNotification>
0016 
0017 #include <chrono>
0018 
0019 using namespace std::chrono_literals;
0020 
0021 KDEDBolt::KDEDBolt(QObject *parent, const QVariantList &)
0022     : KDEDModule(parent)
0023 {
0024     if (!mManager.isAvailable()) {
0025         qCInfo(log_kded_bolt, "Couldn't connect to Bolt DBus daemon");
0026         return;
0027     }
0028 
0029     mPendingDeviceTimer.setSingleShot(true);
0030     mPendingDeviceTimer.setInterval(500ms);
0031     connect(&mPendingDeviceTimer, &QTimer::timeout, this, &KDEDBolt::notify);
0032 
0033     connect(&mManager, &Bolt::Manager::deviceAdded, this, [this](const QSharedPointer<Bolt::Device> &device) {
0034         // Already authorized, nothing else to do here
0035         if (device->status() == Bolt::Status::Authorized) {
0036             return;
0037         }
0038 
0039         mPendingDevices.append(device);
0040         mPendingDeviceTimer.start();
0041     });
0042     connect(&mManager, &Bolt::Manager::deviceRemoved, this, [this](const QSharedPointer<Bolt::Device> &device) {
0043         // Check if maybe the device is in pending or currently active
0044         // notification, remove it if so.
0045         mPendingDevices.removeOne(device);
0046         Q_ASSERT(!mPendingDevices.removeOne(device));
0047 
0048         for (auto it = mNotifiedDevices.begin(), end = mNotifiedDevices.end(); it != end; ++it) {
0049             if (it->contains(device)) {
0050                 auto devices = *it;
0051                 devices.removeOne(device);
0052                 mPendingDevices += devices;
0053                 mPendingDeviceTimer.start();
0054             }
0055             it.key()->close();
0056         }
0057     });
0058 }
0059 
0060 KDEDBolt::~KDEDBolt()
0061 {
0062 }
0063 
0064 void KDEDBolt::notify()
0065 {
0066     auto ntf = KNotification::event(QStringLiteral("unauthorizedDeviceConnected"),
0067                                     i18n("New Thunderbolt Device Detected"),
0068                                     mPendingDevices.size() == 1 ? i18n("Unauthorized Thunderbolt device <b>%1</b> was detected. Do you want to authorize it?",
0069                                                                        mPendingDevices.front()->name())
0070                                                                 : i18np("%1 unauthorized Thunderbolt device was detected. Do you want to authorize it?",
0071                                                                         "%1 unauthorized Thunderbolt devices were detected. Do you want to authorize them?",
0072                                                                         mPendingDevices.size()),
0073                                     /*icon*/ QPixmap{},
0074                                     KNotification::Persistent,
0075                                     QStringLiteral("kded_bolt"));
0076     mNotifiedDevices.insert(ntf, mPendingDevices);
0077 
0078     auto authorizeNowAction = ntf->addAction(i18n("Authorize Now"));
0079     connect(authorizeNowAction, &KNotificationAction::activated, this, [this, devices = mPendingDevices]() {
0080         authorizeDevices(sortDevices(devices), Authorize);
0081     });
0082 
0083     auto authorizePermanentlyAction = ntf->addAction(i18n("Authorize Permanently"));
0084     connect(authorizePermanentlyAction, &KNotificationAction::activated, this, [this, devices = mPendingDevices]() {
0085         authorizeDevices(sortDevices(devices), Enroll);
0086     });
0087 
0088     connect(ntf, &KNotification::closed, this, [this, ntf]() {
0089         mNotifiedDevices.remove(ntf);
0090     });
0091 
0092     mPendingDevices.clear();
0093 }
0094 
0095 KDEDBolt::BoltDeviceList KDEDBolt::sortDevices(const BoltDeviceList &devices)
0096 {
0097     QList<QSharedPointer<Bolt::Device>> sorted;
0098     sorted.reserve(devices.size());
0099 
0100     // Sort the devices so that parents go before their children. Probably
0101     // fairly inefficient but there's rarely more than a couple of items.
0102     for (const auto &device : devices) {
0103         auto child = std::find_if(sorted.begin(), sorted.end(), [device](const auto &d) {
0104             return d->parent() == device->uid();
0105         });
0106         auto parent = std::find_if(sorted.begin(), child, [device](const auto &d) {
0107             return device->parent() == d->uid();
0108         });
0109         if (parent != sorted.end()) {
0110             ++parent;
0111         }
0112         sorted.insert(parent, device);
0113     }
0114 
0115     return sorted;
0116 }
0117 
0118 void KDEDBolt::authorizeDevices(BoltDeviceList devices, AuthMode mode)
0119 {
0120     if (devices.empty()) {
0121         return;
0122     }
0123 
0124     const auto device = devices.takeFirst();
0125 
0126     const auto okCb = [this, devices, mode]() {
0127         authorizeDevices(std::move(devices), mode);
0128     };
0129     const auto errCb = [device](const QString &error) {
0130         KNotification::event(QStringLiteral("deviceAuthError"),
0131                              i18n("Thunderbolt Device Authorization Error"),
0132                              i18n("Failed to authorize Thunderbolt device <b>%1</b>: %2", device->name().toHtmlEscaped(), error),
0133                              /* icon */ QPixmap{},
0134                              KNotification::CloseOnTimeout,
0135                              QStringLiteral("kded_bolt"));
0136     };
0137     if (mode == Enroll) {
0138         mManager.enrollDevice(device->uid(), Bolt::Policy::Auto, Bolt::Auth::Boot | Bolt::Auth::NoKey, okCb, errCb);
0139     } else {
0140         device->authorize(Bolt::Auth::Boot | Bolt::Auth::NoKey, okCb, errCb);
0141     }
0142 }
0143 
0144 #include "moc_kded_bolt.cpp"