File indexing completed on 2024-05-12 17:08:31
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 /* widget */ nullptr, 0075 KNotification::Persistent, 0076 QStringLiteral("kded_bolt")); 0077 ntf->setActions({i18n("Authorize Now"), i18n("Authorize Permanently")}); 0078 mNotifiedDevices.insert(ntf, mPendingDevices); 0079 connect(ntf, &KNotification::action1Activated, this, [this, devices = mPendingDevices]() { 0080 authorizeDevices(sortDevices(devices), Authorize); 0081 }); 0082 connect(ntf, &KNotification::action2Activated, this, [this, devices = mPendingDevices]() { 0083 authorizeDevices(sortDevices(devices), Enroll); 0084 }); 0085 connect(ntf, &KNotification::closed, this, [this, ntf]() { 0086 mNotifiedDevices.remove(ntf); 0087 }); 0088 0089 mPendingDevices.clear(); 0090 } 0091 0092 KDEDBolt::BoltDeviceList KDEDBolt::sortDevices(const BoltDeviceList &devices) 0093 { 0094 QVector<QSharedPointer<Bolt::Device>> sorted; 0095 sorted.reserve(devices.size()); 0096 0097 // Sort the devices so that parents go before their children. Probably 0098 // fairly inefficient but there's rarely more than a couple of items. 0099 for (const auto &device : devices) { 0100 auto child = std::find_if(sorted.begin(), sorted.end(), [device](const auto &d) { 0101 return d->parent() == device->uid(); 0102 }); 0103 auto parent = std::find_if(sorted.begin(), child, [device](const auto &d) { 0104 return device->parent() == d->uid(); 0105 }); 0106 if (parent != sorted.end()) { 0107 ++parent; 0108 } 0109 sorted.insert(parent, device); 0110 } 0111 0112 return sorted; 0113 } 0114 0115 void KDEDBolt::authorizeDevices(BoltDeviceList devices, AuthMode mode) 0116 { 0117 if (devices.empty()) { 0118 return; 0119 } 0120 0121 const auto device = devices.takeFirst(); 0122 0123 const auto okCb = [this, devices, mode]() { 0124 authorizeDevices(std::move(devices), mode); 0125 }; 0126 const auto errCb = [device](const QString &error) { 0127 KNotification::event(QStringLiteral("deviceAuthError"), 0128 i18n("Thunderbolt Device Authorization Error"), 0129 i18n("Failed to authorize Thunderbolt device <b>%1</b>: %2", device->name().toHtmlEscaped(), error), 0130 /* icon */ QPixmap{}, 0131 /* parent */ nullptr, 0132 KNotification::CloseOnTimeout, 0133 QStringLiteral("kded_bolt")); 0134 }; 0135 if (mode == Enroll) { 0136 mManager.enrollDevice(device->uid(), Bolt::Policy::Auto, Bolt::Auth::Boot | Bolt::Auth::NoKey, okCb, errCb); 0137 } else { 0138 device->authorize(Bolt::Auth::Boot | Bolt::Auth::NoKey, okCb, errCb); 0139 } 0140 }