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"