File indexing completed on 2023-09-24 09:38:43

0001 /*
0002     SPDX-FileCopyrightText: 2013-2014 Jan Grulich <jgrulich@redhat.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "handler.h"
0008 #include "configuration.h"
0009 #include "connectioneditordialog.h"
0010 #include "plasma_nm_libs.h"
0011 #include "uiutils.h"
0012 
0013 #include <NetworkManagerQt/AccessPoint>
0014 #include <NetworkManagerQt/ActiveConnection>
0015 #include <NetworkManagerQt/GsmSetting>
0016 #include <NetworkManagerQt/Ipv4Setting>
0017 #include <NetworkManagerQt/Manager>
0018 #include <NetworkManagerQt/Setting>
0019 #include <NetworkManagerQt/WiredDevice>
0020 #include <NetworkManagerQt/WiredSetting>
0021 #include <NetworkManagerQt/WirelessDevice>
0022 #include <NetworkManagerQt/WirelessSetting>
0023 
0024 #include <libnm/nm-vpn-plugin-info.h>
0025 
0026 #include <ModemManagerQt/Manager>
0027 #include <ModemManagerQt/ModemDevice>
0028 
0029 #include <QDBusError>
0030 #include <QDBusMetaType>
0031 #include <QDBusPendingReply>
0032 #include <QFile>
0033 #include <QIcon>
0034 
0035 #include <KIO/OpenUrlJob>
0036 #include <KLocalizedString>
0037 #include <KNotification>
0038 #include <KOSRelease>
0039 #include <KPluginMetaData>
0040 #include <KProcess>
0041 #include <KUser>
0042 #include <KWallet>
0043 #include <KWindowSystem>
0044 
0045 #include <nm-client.h>
0046 
0047 #define AGENT_SERVICE "org.kde.kded5"
0048 #define AGENT_PATH "/modules/networkmanagement"
0049 #define AGENT_IFACE "org.kde.plasmanetworkmanagement"
0050 
0051 // 10 seconds
0052 #define NM_REQUESTSCAN_LIMIT_RATE 10000
0053 
0054 Handler::Handler(QObject *parent)
0055     : QObject(parent)
0056     , m_tmpWirelessEnabled(NetworkManager::isWirelessEnabled())
0057     , m_tmpWwanEnabled(NetworkManager::isWwanEnabled())
0058 {
0059     QDBusConnection::sessionBus().connect(QStringLiteral(AGENT_SERVICE),
0060                                           QStringLiteral(AGENT_PATH),
0061                                           QStringLiteral(AGENT_IFACE),
0062                                           QStringLiteral("secretsError"),
0063                                           this,
0064                                           SLOT(secretAgentError(QString, QString)));
0065 
0066     if (!Configuration::self().hotspotConnectionPath().isEmpty()) {
0067         NetworkManager::ActiveConnection::Ptr hotspot = NetworkManager::findActiveConnection(Configuration::self().hotspotConnectionPath());
0068         if (!hotspot) {
0069             Configuration::self().setHotspotConnectionPath(QString());
0070         }
0071     }
0072 
0073     m_hotspotSupported = checkHotspotSupported();
0074 
0075     if (NetworkManager::checkVersion(1, 16, 0)) {
0076         connect(NetworkManager::notifier(), &NetworkManager::Notifier::primaryConnectionTypeChanged, this, &Handler::primaryConnectionTypeChanged);
0077     }
0078 }
0079 
0080 Handler::~Handler() = default;
0081 
0082 void Handler::activateConnection(const QString &connection, const QString &device, const QString &specificObject)
0083 {
0084     NetworkManager::Connection::Ptr con = NetworkManager::findConnection(connection);
0085 
0086     if (!con) {
0087         qCWarning(PLASMA_NM_LIBS_LOG) << "Not possible to activate this connection";
0088         return;
0089     }
0090 
0091     if (con->settings()->connectionType() == NetworkManager::ConnectionSettings::Vpn) {
0092         NetworkManager::VpnSetting::Ptr vpnSetting = con->settings()->setting(NetworkManager::Setting::Vpn).staticCast<NetworkManager::VpnSetting>();
0093         if (vpnSetting) {
0094             qCDebug(PLASMA_NM_LIBS_LOG) << "Checking VPN" << con->name() << "type:" << vpnSetting->serviceType();
0095 
0096             // Check missing plasma-nm VPN plugin
0097 
0098             const auto filter = [vpnSetting](const KPluginMetaData &md) -> bool {
0099                 return md.value(QStringLiteral("X-NetworkManager-Services")) == vpnSetting->serviceType();
0100             };
0101 
0102             const QVector<KPluginMetaData> plasmaNmPlugins = KPluginMetaData::findPlugins(QStringLiteral("plasma/network/vpn"), filter);
0103 
0104             const QString pluginBaseName = vpnSetting->serviceType().remove(QLatin1String("org.freedesktop.NetworkManager."));
0105 
0106             if (plasmaNmPlugins.empty()) {
0107                 qCWarning(PLASMA_NM_LIBS_LOG) << "VPN" << vpnSetting->serviceType() << "not found, skipping";
0108                 auto notification = new KNotification(QStringLiteral("MissingVpnPlugin"), KNotification::Persistent, this);
0109                 notification->setComponentName(QStringLiteral("networkmanagement"));
0110                 notification->setTitle(con->name());
0111                 notification->setText(i18n("Plasma is missing support for '%1' VPN connections.", pluginBaseName));
0112                 notification->setIconName(QStringLiteral("dialog-error"));
0113                 notification->setActions({i18n("Report Bug")});
0114                 connect(notification, &KNotification::action1Activated, this, [notification] {
0115                     auto *job = new KIO::OpenUrlJob(QUrl(KOSRelease().bugReportUrl()));
0116                     job->setStartupId(notification->xdgActivationToken().toUtf8());
0117                     job->start();
0118                 });
0119 
0120                 notification->sendEvent();
0121 
0122                 return;
0123             }
0124 
0125             // Check missing NetworkManager VPN plugin
0126             GSList *networkManagerPlugins = nullptr;
0127             networkManagerPlugins = nm_vpn_plugin_info_list_load();
0128 
0129             NMVpnPluginInfo *plugin_info = nm_vpn_plugin_info_list_find_by_service(networkManagerPlugins, vpnSetting->serviceType().toStdString().c_str());
0130 
0131             if (!plugin_info) {
0132                 qCWarning(PLASMA_NM_LIBS_LOG) << "VPN" << vpnSetting->serviceType() << "not found, skipping";
0133                 auto notification = new KNotification(QStringLiteral("MissingVpnPlugin"), KNotification::Persistent, this);
0134                 notification->setComponentName(QStringLiteral("networkmanagement"));
0135                 notification->setTitle(con->name());
0136                 notification->setText(i18n("NetworkManager is missing support for '%1' VPN connections.", pluginBaseName));
0137                 notification->setIconName(QStringLiteral("dialog-error"));
0138                 notification->setActions({i18n("Install")});
0139 
0140                 connect(notification, &KNotification::action1Activated, this, [notification, pluginBaseName] {
0141                     auto *job = new KIO::OpenUrlJob(QUrl(QStringLiteral("appstream:network-manager-") + pluginBaseName));
0142                     job->setStartupId(notification->xdgActivationToken().toUtf8());
0143                     job->start();
0144                 });
0145 
0146                 notification->sendEvent();
0147                 return;
0148             }
0149         }
0150     }
0151 
0152     if (con->settings()->connectionType() == NetworkManager::ConnectionSettings::Gsm) {
0153         NetworkManager::ModemDevice::Ptr nmModemDevice = NetworkManager::findNetworkInterface(device).objectCast<NetworkManager::ModemDevice>();
0154         if (nmModemDevice) {
0155             ModemManager::ModemDevice::Ptr mmModemDevice = ModemManager::findModemDevice(nmModemDevice->udi());
0156             if (mmModemDevice) {
0157                 ModemManager::Modem::Ptr modem = mmModemDevice->interface(ModemManager::ModemDevice::ModemInterface).objectCast<ModemManager::Modem>();
0158                 NetworkManager::GsmSetting::Ptr gsmSetting = con->settings()->setting(NetworkManager::Setting::Gsm).staticCast<NetworkManager::GsmSetting>();
0159                 if (gsmSetting && gsmSetting->pinFlags() == NetworkManager::Setting::NotSaved && modem && modem->unlockRequired() > MM_MODEM_LOCK_NONE) {
0160                     QDBusInterface managerIface(QStringLiteral("org.kde.plasmanetworkmanagement"),
0161                                                 QStringLiteral("/org/kde/plasmanetworkmanagement"),
0162                                                 QStringLiteral("org.kde.plasmanetworkmanagement"),
0163                                                 QDBusConnection::sessionBus(),
0164                                                 this);
0165                     managerIface.call(QStringLiteral("unlockModem"), mmModemDevice->uni());
0166                     connect(modem.data(), &ModemManager::Modem::unlockRequiredChanged, this, &Handler::unlockRequiredChanged);
0167                     m_tmpConnectionPath = connection;
0168                     m_tmpDevicePath = device;
0169                     m_tmpSpecificPath = specificObject;
0170                     return;
0171                 }
0172             }
0173         }
0174     }
0175 
0176     QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::activateConnection(connection, device, specificObject);
0177     auto watcher = new QDBusPendingCallWatcher(reply, this);
0178     watcher->setProperty("action", Handler::ActivateConnection);
0179     watcher->setProperty("connection", con->name());
0180     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0181 }
0182 
0183 void Handler::requestWifiCode(const QString &connectionPath, const QString &ssid, int _securityType, const QString &connectionName)
0184 {
0185     if (!m_requestWifiCodeWatcher.isNull()) {
0186         delete m_requestWifiCodeWatcher;
0187     }
0188 
0189     auto securityType = static_cast<NetworkManager::WirelessSecurityType>(_securityType);
0190 
0191     QString ret = QStringLiteral("WIFI:S:") + ssid + QLatin1Char(';');
0192     if (securityType != NetworkManager::NoneSecurity) {
0193         switch (securityType) {
0194         case NetworkManager::NoneSecurity:
0195             break;
0196         case NetworkManager::StaticWep:
0197             ret += QStringLiteral("T:WEP;");
0198             break;
0199         case NetworkManager::WpaPsk:
0200         case NetworkManager::Wpa2Psk:
0201             ret += QStringLiteral("T:WPA;");
0202             break;
0203         case NetworkManager::SAE:
0204             ret += QStringLiteral("T:SAE;");
0205             break;
0206         default:
0207         case NetworkManager::DynamicWep:
0208         case NetworkManager::WpaEap:
0209         case NetworkManager::Wpa2Eap:
0210         case NetworkManager::Wpa3SuiteB192:
0211         case NetworkManager::Leap:
0212             Q_EMIT wifiCodeReceived(QString(), connectionName);
0213             return;
0214         }
0215     }
0216 
0217     NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(connectionPath);
0218     if (!connection) {
0219         Q_EMIT wifiCodeReceived(QString(), connectionName);
0220         return;
0221     }
0222 
0223     const auto key = QStringLiteral("802-11-wireless-security");
0224     auto reply = connection->secrets(key);
0225     m_requestWifiCodeWatcher = new QDBusPendingCallWatcher(reply, this);
0226     m_requestWifiCodeWatcher->setProperty("key", key);
0227     m_requestWifiCodeWatcher->setProperty("ret", ret);
0228     m_requestWifiCodeWatcher->setProperty("securityType", static_cast<int>(securityType));
0229     m_requestWifiCodeWatcher->setProperty("connectionName", connectionName);
0230     connect(m_requestWifiCodeWatcher, &QDBusPendingCallWatcher::finished, this, &Handler::slotRequestWifiCode);
0231 }
0232 
0233 void Handler::addAndActivateConnection(const QString &device, const QString &specificObject, const QString &password)
0234 {
0235     NetworkManager::AccessPoint::Ptr ap;
0236     NetworkManager::WirelessDevice::Ptr wifiDev;
0237     for (const NetworkManager::Device::Ptr &dev : NetworkManager::networkInterfaces()) {
0238         if (dev->type() == NetworkManager::Device::Wifi) {
0239             wifiDev = dev.objectCast<NetworkManager::WirelessDevice>();
0240             ap = wifiDev->findAccessPoint(specificObject);
0241             if (ap) {
0242                 break;
0243             }
0244         }
0245     }
0246 
0247     if (!ap) {
0248         return;
0249     }
0250 
0251     NetworkManager::ConnectionSettings::Ptr settings =
0252         NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Wireless));
0253     settings->setId(ap->ssid());
0254     settings->setUuid(NetworkManager::ConnectionSettings::createNewUuid());
0255     settings->setAutoconnect(true);
0256 
0257     UiUtils::setConnectionDefaultPermissions(settings);
0258 
0259     NetworkManager::WirelessSetting::Ptr wifiSetting = settings->setting(NetworkManager::Setting::Wireless).dynamicCast<NetworkManager::WirelessSetting>();
0260     wifiSetting->setInitialized(true);
0261     wifiSetting = settings->setting(NetworkManager::Setting::Wireless).dynamicCast<NetworkManager::WirelessSetting>();
0262     wifiSetting->setSsid(ap->ssid().toUtf8());
0263     if (ap->mode() == NetworkManager::AccessPoint::Adhoc) {
0264         wifiSetting->setMode(NetworkManager::WirelessSetting::Adhoc);
0265     }
0266     NetworkManager::WirelessSecuritySetting::Ptr wifiSecurity =
0267         settings->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast<NetworkManager::WirelessSecuritySetting>();
0268 
0269     NetworkManager::WirelessSecurityType securityType = NetworkManager::findBestWirelessSecurity(wifiDev->wirelessCapabilities(),
0270                                                                                                  true,
0271                                                                                                  (ap->mode() == NetworkManager::AccessPoint::Adhoc),
0272                                                                                                  ap->capabilities(),
0273                                                                                                  ap->wpaFlags(),
0274                                                                                                  ap->rsnFlags());
0275 
0276     if (securityType != NetworkManager::NoneSecurity) {
0277         wifiSecurity->setInitialized(true);
0278         wifiSetting->setSecurity(QStringLiteral("802-11-wireless-security"));
0279     }
0280 
0281     if (securityType == NetworkManager::Leap //
0282         || securityType == NetworkManager::DynamicWep //
0283         || securityType == NetworkManager::Wpa3SuiteB192 //
0284         || securityType == NetworkManager::Wpa2Eap //
0285         || securityType == NetworkManager::WpaEap) {
0286         if (securityType == NetworkManager::DynamicWep || securityType == NetworkManager::Leap) {
0287             wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::Ieee8021x);
0288             if (securityType == NetworkManager::Leap) {
0289                 wifiSecurity->setAuthAlg(NetworkManager::WirelessSecuritySetting::Leap);
0290             }
0291         } else if (securityType == NetworkManager::Wpa3SuiteB192) {
0292             wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaEapSuiteB192);
0293         } else {
0294             wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaEap);
0295         }
0296         m_tmpConnectionUuid = settings->uuid();
0297         m_tmpDevicePath = device;
0298         m_tmpSpecificPath = specificObject;
0299 
0300         QPointer<ConnectionEditorDialog> editor = new ConnectionEditorDialog(settings);
0301         editor->setAttribute(Qt::WA_DeleteOnClose);
0302         editor->show();
0303         KWindowSystem::setState(editor->winId(), NET::KeepAbove);
0304         connect(editor.data(), &ConnectionEditorDialog::accepted, [editor, device, specificObject, this]() { //
0305             addAndActivateConnectionDBus(editor->setting(), device, specificObject);
0306         });
0307         editor->setModal(true);
0308         editor->show();
0309     } else {
0310         if (securityType == NetworkManager::StaticWep) {
0311             wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::Wep);
0312             wifiSecurity->setWepKey0(password);
0313         } else {
0314             if (ap->mode() == NetworkManager::AccessPoint::Adhoc) {
0315                 wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaNone);
0316             } else if (securityType == NetworkManager::SAE) {
0317                 wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::SAE);
0318             } else {
0319                 wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaPsk);
0320             }
0321             wifiSecurity->setPsk(password);
0322         }
0323         addAndActivateConnectionDBus(settings->toMap(), device, specificObject);
0324     }
0325 
0326     settings.clear();
0327 }
0328 
0329 void Handler::addConnection(const NMVariantMapMap &map)
0330 {
0331     QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::addConnection(map);
0332     auto watcher = new QDBusPendingCallWatcher(reply, this);
0333     watcher->setProperty("action", AddConnection);
0334     watcher->setProperty("connection", map.value(QStringLiteral("connection")).value(QStringLiteral("id")));
0335     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0336 }
0337 
0338 struct AddConnectionData {
0339     QString id;
0340     Handler *handler;
0341 };
0342 
0343 void add_connection_cb(GObject *client, GAsyncResult *result, gpointer user_data)
0344 {
0345     AddConnectionData *data = static_cast<AddConnectionData *>(user_data);
0346 
0347     GError *error = nullptr;
0348     NMRemoteConnection *connection = nm_client_add_connection2_finish(NM_CLIENT(client), result, NULL, &error);
0349 
0350     if (error) {
0351         KNotification *notification = new KNotification(QStringLiteral("FailedToAddConnection"), KNotification::CloseOnTimeout, data->handler);
0352         notification->setTitle(i18n("Failed to add connection %1", data->id));
0353         notification->setComponentName(QStringLiteral("networkmanagement"));
0354         notification->setText(QString::fromUtf8(error->message));
0355         notification->setIconName(QStringLiteral("dialog-warning"));
0356         notification->sendEvent();
0357 
0358         g_error_free(error);
0359     } else {
0360         KNotification *notification = new KNotification(QStringLiteral("ConnectionAdded"), KNotification::CloseOnTimeout, data->handler);
0361         notification->setText(i18n("Connection %1 has been added", data->id));
0362         notification->setComponentName(QStringLiteral("networkmanagement"));
0363         notification->setTitle(data->id);
0364         notification->setIconName(QStringLiteral("dialog-information"));
0365         notification->sendEvent();
0366 
0367         g_object_unref(connection);
0368     }
0369 
0370     delete data;
0371 }
0372 
0373 void Handler::addConnection(NMConnection *connection)
0374 {
0375     NMClient *client = nm_client_new(nullptr, nullptr);
0376 
0377     AddConnectionData *userData = new AddConnectionData{QString::fromUtf8(nm_connection_get_id(connection)), this};
0378 
0379     nm_client_add_connection2(client,
0380                               nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL),
0381                               NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK,
0382                               nullptr,
0383                               true,
0384                               nullptr,
0385                               add_connection_cb,
0386                               userData);
0387 }
0388 
0389 void Handler::addAndActivateConnectionDBus(const NMVariantMapMap &map, const QString &device, const QString &specificObject)
0390 {
0391     QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::addAndActivateConnection(map, device, specificObject);
0392     auto watcher = new QDBusPendingCallWatcher(reply, this);
0393     watcher->setProperty("action", AddAndActivateConnection);
0394     watcher->setProperty("connection", map.value(QStringLiteral("connection")).value(QStringLiteral("id")));
0395     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0396 }
0397 
0398 void Handler::deactivateConnection(const QString &connection, const QString &device)
0399 {
0400     NetworkManager::Connection::Ptr con = NetworkManager::findConnection(connection);
0401 
0402     if (!con) {
0403         qCWarning(PLASMA_NM_LIBS_LOG) << "Not possible to deactivate this connection";
0404         return;
0405     }
0406 
0407     QDBusPendingReply<> reply;
0408     for (const NetworkManager::ActiveConnection::Ptr &active : NetworkManager::activeConnections()) {
0409         if (active->uuid() == con->uuid() && ((!active->devices().isEmpty() && active->devices().first() == device) || active->vpn())) {
0410             if (active->vpn()) {
0411                 reply = NetworkManager::deactivateConnection(active->path());
0412             } else {
0413                 NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(active->devices().first());
0414                 if (device) {
0415                     reply = device->disconnectInterface();
0416                 }
0417             }
0418         }
0419     }
0420 
0421     auto watcher = new QDBusPendingCallWatcher(reply, this);
0422     watcher->setProperty("action", Handler::DeactivateConnection);
0423     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0424 }
0425 
0426 void Handler::disconnectAll()
0427 {
0428     for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0429         device->disconnectInterface();
0430     }
0431 }
0432 
0433 void Handler::enableAirplaneMode(bool enable)
0434 {
0435     if (enable) {
0436         m_tmpWirelessEnabled = NetworkManager::isWirelessEnabled();
0437         m_tmpWwanEnabled = NetworkManager::isWwanEnabled();
0438         enableBluetooth(false);
0439         enableWireless(false);
0440         enableWwan(false);
0441     } else {
0442         enableBluetooth(true);
0443         if (m_tmpWirelessEnabled) {
0444             enableWireless(true);
0445         }
0446         if (m_tmpWwanEnabled) {
0447             enableWwan(true);
0448         }
0449     }
0450 }
0451 
0452 template<typename T>
0453 void makeDBusCall(const QDBusMessage &message, QObject *context, std::function<void(QDBusPendingReply<T>)> func)
0454 {
0455     QDBusPendingReply<T> reply = QDBusConnection::systemBus().asyncCall(message);
0456     auto watcher = new QDBusPendingCallWatcher(reply, context);
0457     QObject::connect(watcher, &QDBusPendingCallWatcher::finished, context, [func](QDBusPendingCallWatcher *watcher) {
0458         const QDBusPendingReply<T> reply = *watcher;
0459         if (!reply.isValid()) {
0460             qCWarning(PLASMA_NM_LIBS_LOG) << reply.error().message();
0461             return;
0462         }
0463         func(reply);
0464         watcher->deleteLater();
0465     });
0466 }
0467 
0468 void setBluetoothEnabled(const QString &path, bool enabled)
0469 {
0470     QDBusMessage message =
0471         QDBusMessage::createMethodCall(QStringLiteral("org.bluez"), path, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Set"));
0472     QList<QVariant> arguments;
0473     arguments << QLatin1String("org.bluez.Adapter1");
0474     arguments << QLatin1String("Powered");
0475     arguments << QVariant::fromValue(QDBusVariant(QVariant(enabled)));
0476     message.setArguments(arguments);
0477     QDBusConnection::systemBus().asyncCall(message);
0478 }
0479 
0480 void Handler::enableBluetooth(bool enable)
0481 {
0482     qDBusRegisterMetaType<QMap<QDBusObjectPath, NMVariantMapMap>>();
0483 
0484     const QDBusMessage getObjects = QDBusMessage::createMethodCall("org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
0485 
0486     makeDBusCall<QMap<QDBusObjectPath, NMVariantMapMap>>(getObjects, this, [enable, this](const auto reply) {
0487         for (const QDBusObjectPath &path : reply.value().keys()) {
0488             const QString objPath = path.path();
0489             qCDebug(PLASMA_NM_LIBS_LOG) << "inspecting path" << objPath;
0490             const QStringList interfaces = reply.value().value(path).keys();
0491             qCDebug(PLASMA_NM_LIBS_LOG) << "interfaces:" << interfaces;
0492 
0493             if (!interfaces.contains(QStringLiteral("org.bluez.Adapter1"))) {
0494                 continue;
0495             }
0496 
0497             // We need to check previous state first
0498             if (!enable) {
0499                 QDBusMessage getPowered = QDBusMessage::createMethodCall("org.bluez", objPath, "org.freedesktop.DBus.Properties", "Get");
0500                 const QList<QVariant> arguments{QLatin1String("org.bluez.Adapter1"), QLatin1String("Powered")};
0501                 getPowered.setArguments(arguments);
0502 
0503                 makeDBusCall<QVariant>(getPowered, this, [objPath, this](const auto reply) {
0504                     m_bluetoothAdapters.insert(objPath, reply.value().toBool());
0505                     setBluetoothEnabled(objPath, false);
0506                 });
0507             } else if (m_bluetoothAdapters.value(objPath)) {
0508                 setBluetoothEnabled(objPath, true);
0509             }
0510         }
0511     });
0512 }
0513 
0514 void Handler::enableNetworking(bool enable)
0515 {
0516     NetworkManager::setNetworkingEnabled(enable);
0517 }
0518 
0519 void Handler::enableWireless(bool enable)
0520 {
0521     NetworkManager::setWirelessEnabled(enable);
0522 }
0523 
0524 void Handler::enableWwan(bool enable)
0525 {
0526     NetworkManager::setWwanEnabled(enable);
0527 }
0528 
0529 void Handler::removeConnection(const QString &connection)
0530 {
0531     NetworkManager::Connection::Ptr con = NetworkManager::findConnection(connection);
0532 
0533     if (!con || con->uuid().isEmpty()) {
0534         qCWarning(PLASMA_NM_LIBS_LOG) << "Not possible to remove connection " << connection;
0535         return;
0536     }
0537 
0538     // Remove slave connections
0539     for (const NetworkManager::Connection::Ptr &connection : NetworkManager::listConnections()) {
0540         NetworkManager::ConnectionSettings::Ptr settings = connection->settings();
0541         if (settings->master() == con->uuid()) {
0542             connection->remove();
0543         }
0544     }
0545 
0546     QDBusPendingReply<> reply = con->remove();
0547     auto watcher = new QDBusPendingCallWatcher(reply, this);
0548     watcher->setProperty("action", Handler::RemoveConnection);
0549     watcher->setProperty("connection", con->name());
0550     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0551 }
0552 
0553 void Handler::updateConnection(const NetworkManager::Connection::Ptr &connection, const NMVariantMapMap &map)
0554 {
0555     QDBusPendingReply<> reply = connection->update(map);
0556     auto watcher = new QDBusPendingCallWatcher(reply, this);
0557     watcher->setProperty("action", UpdateConnection);
0558     watcher->setProperty("connection", connection->name());
0559     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0560 }
0561 
0562 void Handler::requestScan(const QString &interface)
0563 {
0564     for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0565         if (device->type() == NetworkManager::Device::Wifi) {
0566             NetworkManager::WirelessDevice::Ptr wifiDevice = device.objectCast<NetworkManager::WirelessDevice>();
0567 
0568             if (wifiDevice && wifiDevice->state() != NetworkManager::WirelessDevice::Unavailable) {
0569                 if (!interface.isEmpty() && interface != wifiDevice->interfaceName()) {
0570                     continue;
0571                 }
0572 
0573                 if (!checkRequestScanRateLimit(wifiDevice)) {
0574                     QDateTime now = QDateTime::currentDateTime();
0575                     // for NM < 1.12, lastScan is not available
0576                     QDateTime lastScan = wifiDevice->lastScan();
0577                     QDateTime lastRequestScan = wifiDevice->lastRequestScan();
0578                     // Compute the next time we can run a scan
0579                     int timeout = NM_REQUESTSCAN_LIMIT_RATE;
0580                     if (lastScan.isValid() && lastScan.msecsTo(now) < NM_REQUESTSCAN_LIMIT_RATE) {
0581                         timeout = NM_REQUESTSCAN_LIMIT_RATE - lastScan.msecsTo(now);
0582                     } else if (lastRequestScan.isValid() && lastRequestScan.msecsTo(now) < NM_REQUESTSCAN_LIMIT_RATE) {
0583                         timeout = NM_REQUESTSCAN_LIMIT_RATE - lastRequestScan.msecsTo(now);
0584                     }
0585                     qCDebug(PLASMA_NM_LIBS_LOG) << "Rescheduling a request scan for" << wifiDevice->interfaceName() << "in" << timeout;
0586                     scheduleRequestScan(wifiDevice->interfaceName(), timeout);
0587 
0588                     if (!interface.isEmpty()) {
0589                         return;
0590                     }
0591                     continue;
0592                 } else if (m_wirelessScanRetryTimer.contains(interface)) {
0593                     m_wirelessScanRetryTimer.value(interface)->stop();
0594                     delete m_wirelessScanRetryTimer.take(interface);
0595                 }
0596 
0597                 qCDebug(PLASMA_NM_LIBS_LOG) << "Requesting wifi scan on device" << wifiDevice->interfaceName();
0598                 incrementScansCount();
0599                 QDBusPendingReply<> reply = wifiDevice->requestScan();
0600                 auto watcher = new QDBusPendingCallWatcher(reply, this);
0601                 watcher->setProperty("action", Handler::RequestScan);
0602                 watcher->setProperty("interface", wifiDevice->interfaceName());
0603                 connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0604             }
0605         }
0606     }
0607 }
0608 
0609 void Handler::incrementScansCount()
0610 {
0611     m_ongoingScansCount += 1;
0612     if (m_ongoingScansCount == 1) {
0613         Q_EMIT scanningChanged();
0614     }
0615 }
0616 
0617 void Handler::decrementScansCount()
0618 {
0619     if (m_ongoingScansCount == 0) {
0620         qCDebug(PLASMA_NM_LIBS_LOG) << "Extra decrementScansCount() called";
0621         return;
0622     }
0623     m_ongoingScansCount -= 1;
0624     if (m_ongoingScansCount == 0) {
0625         Q_EMIT scanningChanged();
0626     }
0627 }
0628 
0629 void Handler::createHotspot()
0630 {
0631     bool foundInactive = false;
0632     bool useApMode = false;
0633     NetworkManager::WirelessDevice::Ptr wifiDev;
0634 
0635     NetworkManager::ConnectionSettings::Ptr connectionSettings;
0636     connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Wireless));
0637 
0638     NetworkManager::WirelessSetting::Ptr wifiSetting =
0639         connectionSettings->setting(NetworkManager::Setting::Wireless).dynamicCast<NetworkManager::WirelessSetting>();
0640     wifiSetting->setMode(NetworkManager::WirelessSetting::Adhoc);
0641     wifiSetting->setSsid(Configuration::self().hotspotName().toUtf8());
0642 
0643     for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0644         if (device->type() == NetworkManager::Device::Wifi) {
0645             wifiDev = device.objectCast<NetworkManager::WirelessDevice>();
0646             if (wifiDev) {
0647                 if (!wifiDev->isActive()) {
0648                     foundInactive = true;
0649                 } else {
0650                     // Prefer previous device if it was inactive
0651                     if (foundInactive) {
0652                         break;
0653                     }
0654                 }
0655 
0656                 if (wifiDev->wirelessCapabilities().testFlag(NetworkManager::WirelessDevice::ApCap)) {
0657                     useApMode = true;
0658                 }
0659 
0660                 // We prefer inactive wireless card with AP capabilities
0661                 if (foundInactive && useApMode) {
0662                     break;
0663                 }
0664             }
0665         }
0666     }
0667 
0668     if (!wifiDev) {
0669         qCWarning(PLASMA_NM_LIBS_LOG) << "Failed to create hotspot: missing wireless device";
0670         return;
0671     }
0672 
0673     wifiSetting->setInitialized(true);
0674     wifiSetting->setMode(useApMode ? NetworkManager::WirelessSetting::Ap : NetworkManager::WirelessSetting::Adhoc);
0675 
0676     if (!Configuration::self().hotspotPassword().isEmpty()) {
0677         NetworkManager::WirelessSecuritySetting::Ptr wifiSecurity =
0678             connectionSettings->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast<NetworkManager::WirelessSecuritySetting>();
0679         wifiSecurity->setInitialized(true);
0680 
0681         if (useApMode) {
0682             // Use WPA2
0683             wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaPsk);
0684             wifiSecurity->setPsk(Configuration::self().hotspotPassword());
0685             wifiSecurity->setPskFlags(NetworkManager::Setting::AgentOwned);
0686         } else {
0687             // Use WEP
0688             wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::Wep);
0689             wifiSecurity->setWepKeyType(NetworkManager::WirelessSecuritySetting::Passphrase);
0690             wifiSecurity->setWepTxKeyindex(0);
0691             wifiSecurity->setWepKey0(Configuration::self().hotspotPassword());
0692             wifiSecurity->setWepKeyFlags(NetworkManager::Setting::AgentOwned);
0693             wifiSecurity->setAuthAlg(NetworkManager::WirelessSecuritySetting::Open);
0694         }
0695     }
0696 
0697     NetworkManager::Ipv4Setting::Ptr ipv4Setting = connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast<NetworkManager::Ipv4Setting>();
0698     ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Shared);
0699     ipv4Setting->setInitialized(true);
0700 
0701     connectionSettings->setId(Configuration::self().hotspotName());
0702     connectionSettings->setAutoconnect(false);
0703     connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid());
0704 
0705     const QVariantMap options = {{QLatin1String("persist"), QLatin1String("volatile")}};
0706 
0707     QDBusPendingReply<QDBusObjectPath, QDBusObjectPath, QVariantMap> reply =
0708         NetworkManager::addAndActivateConnection2(connectionSettings->toMap(), wifiDev->uni(), QString(), options);
0709     auto watcher = new QDBusPendingCallWatcher(reply, this);
0710     watcher->setProperty("action", Handler::CreateHotspot);
0711     watcher->setProperty("connection", Configuration::self().hotspotName());
0712     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0713     connect(watcher, &QDBusPendingCallWatcher::finished, this, QOverload<QDBusPendingCallWatcher *>::of(&Handler::hotspotCreated));
0714 }
0715 
0716 void Handler::stopHotspot()
0717 {
0718     const QString activeConnectionPath = Configuration::self().hotspotConnectionPath();
0719 
0720     if (activeConnectionPath.isEmpty()) {
0721         return;
0722     }
0723 
0724     NetworkManager::ActiveConnection::Ptr hotspot = NetworkManager::findActiveConnection(activeConnectionPath);
0725 
0726     if (!hotspot) {
0727         return;
0728     }
0729 
0730     NetworkManager::deactivateConnection(activeConnectionPath);
0731     Configuration::self().setHotspotConnectionPath(QString());
0732 
0733     Q_EMIT hotspotDisabled();
0734 }
0735 
0736 bool Handler::checkRequestScanRateLimit(const NetworkManager::WirelessDevice::Ptr &wifiDevice)
0737 {
0738     QDateTime now = QDateTime::currentDateTime();
0739     QDateTime lastScan = wifiDevice->lastScan();
0740     QDateTime lastRequestScan = wifiDevice->lastRequestScan();
0741 
0742     // if the last scan finished within the last 10 seconds
0743     bool ret = lastScan.isValid() && lastScan.msecsTo(now) < NM_REQUESTSCAN_LIMIT_RATE;
0744     // or if the last Request was sent within the last 10 seconds
0745     ret |= lastRequestScan.isValid() && lastRequestScan.msecsTo(now) < NM_REQUESTSCAN_LIMIT_RATE;
0746     // skip the request scan
0747     if (ret) {
0748         qCDebug(PLASMA_NM_LIBS_LOG) << "Last scan finished" << lastScan.msecsTo(now) << "ms ago and last request scan was sent" //
0749                                     << lastRequestScan.msecsTo(now) << "ms ago, Skipping scanning interface:" << wifiDevice->interfaceName();
0750         return false;
0751     }
0752     return true;
0753 }
0754 
0755 bool Handler::checkHotspotSupported()
0756 {
0757     if (NetworkManager::checkVersion(1, 16, 0)) {
0758         bool unusedWifiFound = false;
0759         bool wifiFound = false;
0760 
0761         for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0762             if (device->type() == NetworkManager::Device::Wifi) {
0763                 wifiFound = true;
0764 
0765                 NetworkManager::WirelessDevice::Ptr wifiDev = device.objectCast<NetworkManager::WirelessDevice>();
0766                 if (wifiDev && !wifiDev->isActive()) {
0767                     unusedWifiFound = true;
0768                 }
0769             }
0770         }
0771 
0772         if (!wifiFound) {
0773             return false;
0774         }
0775 
0776         if (unusedWifiFound) {
0777             return true;
0778         }
0779 
0780         // Check if the primary connection which is used for internet connectivity is not using WiFi
0781         if (NetworkManager::primaryConnectionType() != NetworkManager::ConnectionSettings::Wireless) {
0782             return true;
0783         }
0784     }
0785 
0786     return false;
0787 }
0788 
0789 void Handler::scheduleRequestScan(const QString &interface, int timeout)
0790 {
0791     QTimer *timer;
0792     if (!m_wirelessScanRetryTimer.contains(interface)) {
0793         // create a timer for the interface
0794         timer = new QTimer();
0795         timer->setSingleShot(true);
0796         m_wirelessScanRetryTimer.insert(interface, timer);
0797         auto retryAction = [this, interface]() {
0798             requestScan(interface);
0799         };
0800         connect(timer, &QTimer::timeout, this, retryAction);
0801     } else {
0802         // set the new value for an existing timer
0803         timer = m_wirelessScanRetryTimer.value(interface);
0804         if (timer->isActive()) {
0805             timer->stop();
0806         }
0807     }
0808 
0809     // +1 ms is added to avoid having the scan being rejetted by nm
0810     // because it is run at the exact last millisecond of the requestScan threshold
0811     timer->setInterval(timeout + 1);
0812     timer->start();
0813 }
0814 
0815 void Handler::scanRequestFailed(const QString &interface)
0816 {
0817     scheduleRequestScan(interface, 2000);
0818 }
0819 
0820 void Handler::secretAgentError(const QString &connectionPath, const QString &message)
0821 {
0822     // If the password was wrong, forget it
0823     removeConnection(connectionPath);
0824     Q_EMIT connectionActivationFailed(connectionPath, message);
0825 }
0826 
0827 void Handler::replyFinished(QDBusPendingCallWatcher *watcher)
0828 {
0829     QDBusPendingReply<> reply = *watcher;
0830     if (reply.isError() || !reply.isValid()) {
0831         KNotification *notification = nullptr;
0832         QString error = reply.error().message();
0833         Handler::HandlerAction action = (Handler::HandlerAction)watcher->property("action").toUInt();
0834         switch (action) {
0835         case Handler::ActivateConnection:
0836             notification = new KNotification(QStringLiteral("FailedToActivateConnection"), KNotification::CloseOnTimeout, this);
0837             notification->setTitle(i18n("Failed to activate %1", watcher->property("connection").toString()));
0838             break;
0839         case Handler::AddAndActivateConnection:
0840             notification = new KNotification(QStringLiteral("FailedToAddConnection"), KNotification::CloseOnTimeout, this);
0841             notification->setTitle(i18n("Failed to add %1", watcher->property("connection").toString()));
0842             break;
0843         case Handler::AddConnection:
0844             notification = new KNotification(QStringLiteral("FailedToAddConnection"), KNotification::CloseOnTimeout, this);
0845             notification->setTitle(i18n("Failed to add connection %1", watcher->property("connection").toString()));
0846             break;
0847         case Handler::DeactivateConnection:
0848             notification = new KNotification(QStringLiteral("FailedToDeactivateConnection"), KNotification::CloseOnTimeout, this);
0849             notification->setTitle(i18n("Failed to deactivate %1", watcher->property("connection").toString()));
0850             break;
0851         case Handler::RemoveConnection:
0852             notification = new KNotification(QStringLiteral("FailedToRemoveConnection"), KNotification::CloseOnTimeout, this);
0853             notification->setTitle(i18n("Failed to remove %1", watcher->property("connection").toString()));
0854             break;
0855         case Handler::UpdateConnection:
0856             notification = new KNotification(QStringLiteral("FailedToUpdateConnection"), KNotification::CloseOnTimeout, this);
0857             notification->setTitle(i18n("Failed to update connection %1", watcher->property("connection").toString()));
0858             break;
0859         case Handler::RequestScan: {
0860             const QString interface = watcher->property("interface").toString();
0861             qCWarning(PLASMA_NM_LIBS_LOG) << "Wireless scan on" << interface << "failed:" << error;
0862             scanRequestFailed(interface);
0863             decrementScansCount();
0864             break;
0865         }
0866         case Handler::CreateHotspot:
0867             notification = new KNotification(QStringLiteral("FailedToCreateHotspot"), KNotification::CloseOnTimeout, this);
0868             notification->setTitle(i18n("Failed to create hotspot %1", watcher->property("connection").toString()));
0869             break;
0870         default:
0871             break;
0872         }
0873 
0874         if (notification) {
0875             notification->setComponentName(QStringLiteral("networkmanagement"));
0876             notification->setText(error);
0877             notification->setIconName(QStringLiteral("dialog-warning"));
0878             notification->sendEvent();
0879         }
0880     } else {
0881         KNotification *notification = nullptr;
0882         Handler::HandlerAction action = (Handler::HandlerAction)watcher->property("action").toUInt();
0883 
0884         switch (action) {
0885         case Handler::AddConnection:
0886             notification = new KNotification(QStringLiteral("ConnectionAdded"), KNotification::CloseOnTimeout, this);
0887             notification->setText(i18n("Connection %1 has been added", watcher->property("connection").toString()));
0888             break;
0889         case Handler::RemoveConnection:
0890             notification = new KNotification(QStringLiteral("ConnectionRemoved"), KNotification::CloseOnTimeout, this);
0891             notification->setText(i18n("Connection %1 has been removed", watcher->property("connection").toString()));
0892             break;
0893         case Handler::UpdateConnection:
0894             notification = new KNotification(QStringLiteral("ConnectionUpdated"), KNotification::CloseOnTimeout, this);
0895             notification->setText(i18n("Connection %1 has been updated", watcher->property("connection").toString()));
0896             break;
0897         case Handler::RequestScan:
0898             qCDebug(PLASMA_NM_LIBS_LOG) << "Wireless scan on" << watcher->property("interface").toString() << "succeeded";
0899             decrementScansCount();
0900             break;
0901         default:
0902             break;
0903         }
0904 
0905         if (notification) {
0906             notification->setComponentName(QStringLiteral("networkmanagement"));
0907             notification->setTitle(watcher->property("connection").toString());
0908             notification->setIconName(QStringLiteral("dialog-information"));
0909             notification->sendEvent();
0910         }
0911     }
0912 
0913     watcher->deleteLater();
0914 }
0915 
0916 void Handler::hotspotCreated(QDBusPendingCallWatcher *watcher)
0917 {
0918     QDBusPendingReply<QDBusObjectPath, QDBusObjectPath, QVariantMap> reply = *watcher;
0919 
0920     if (!reply.isError() && reply.isValid()) {
0921         const QString activeConnectionPath = reply.argumentAt(1).value<QDBusObjectPath>().path();
0922 
0923         if (activeConnectionPath.isEmpty()) {
0924             return;
0925         }
0926 
0927         Configuration::self().setHotspotConnectionPath(activeConnectionPath);
0928 
0929         NetworkManager::ActiveConnection::Ptr hotspot = NetworkManager::findActiveConnection(activeConnectionPath);
0930 
0931         if (!hotspot) {
0932             return;
0933         }
0934 
0935         connect(hotspot.data(), &NetworkManager::ActiveConnection::stateChanged, [=](NetworkManager::ActiveConnection::State state) {
0936             if (state > NetworkManager::ActiveConnection::Activated) {
0937                 Configuration::self().setHotspotConnectionPath(QString());
0938                 Q_EMIT hotspotDisabled();
0939             }
0940         });
0941 
0942         Q_EMIT hotspotCreated();
0943     }
0944 }
0945 
0946 void Handler::primaryConnectionTypeChanged(NetworkManager::ConnectionSettings::ConnectionType type)
0947 {
0948     Q_UNUSED(type)
0949     m_hotspotSupported = checkHotspotSupported();
0950     Q_EMIT hotspotSupportedChanged(m_hotspotSupported);
0951 }
0952 
0953 void Handler::unlockRequiredChanged(MMModemLock modemLock)
0954 {
0955     if (modemLock == MM_MODEM_LOCK_NONE) {
0956         activateConnection(m_tmpConnectionPath, m_tmpDevicePath, m_tmpSpecificPath);
0957     }
0958 }
0959 
0960 void Handler::slotRequestWifiCode(QDBusPendingCallWatcher *watcher)
0961 {
0962     watcher->deleteLater();
0963 
0964     QString ret = watcher->property("ret").toString();
0965     const QString connectionName = watcher->property("connectionName").toString();
0966     QDBusPendingReply<NMVariantMapMap> reply = *watcher;
0967     if (!reply.isValid() || reply.isError()) {
0968         Q_EMIT wifiCodeReceived(ret % QLatin1Char(';'), connectionName);
0969         return;
0970     }
0971 
0972     const auto secret = reply.argumentAt<0>()[watcher->property("key").toString()];
0973     QString pass;
0974     switch (static_cast<NetworkManager::WirelessSecurityType>(watcher->property("securityType").toInt())) {
0975     case NetworkManager::NoneSecurity:
0976         break;
0977     case NetworkManager::WpaPsk:
0978     case NetworkManager::Wpa2Psk:
0979     case NetworkManager::SAE:
0980         pass = secret[QStringLiteral("psk")].toString();
0981         break;
0982     default:
0983         Q_EMIT wifiCodeReceived(QString(), connectionName);
0984         return;
0985     }
0986     if (!pass.isEmpty()) {
0987         ret += QStringLiteral("P:") % pass % QLatin1Char(';');
0988     }
0989 
0990     Q_EMIT wifiCodeReceived(ret % QLatin1Char(';'), connectionName);
0991 }