File indexing completed on 2024-12-08 10:59:26

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         KWindowSystem::forceActiveWindow(editor->winId());
0305         connect(editor.data(), &ConnectionEditorDialog::accepted, [editor, device, specificObject, this]() { //
0306             addAndActivateConnectionDBus(editor->setting(), device, specificObject);
0307         });
0308         editor->setModal(true);
0309         editor->show();
0310     } else {
0311         if (securityType == NetworkManager::StaticWep) {
0312             wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::Wep);
0313             wifiSecurity->setWepKey0(password);
0314         } else {
0315             if (ap->mode() == NetworkManager::AccessPoint::Adhoc) {
0316                 wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaNone);
0317             } else if (securityType == NetworkManager::SAE) {
0318                 wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::SAE);
0319             } else {
0320                 wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaPsk);
0321             }
0322             wifiSecurity->setPsk(password);
0323         }
0324         addAndActivateConnectionDBus(settings->toMap(), device, specificObject);
0325     }
0326 
0327     settings.clear();
0328 }
0329 
0330 void Handler::addConnection(const NMVariantMapMap &map)
0331 {
0332     QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::addConnection(map);
0333     auto watcher = new QDBusPendingCallWatcher(reply, this);
0334     watcher->setProperty("action", AddConnection);
0335     watcher->setProperty("connection", map.value(QStringLiteral("connection")).value(QStringLiteral("id")));
0336     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0337 }
0338 
0339 struct AddConnectionData {
0340     QString id;
0341     Handler *handler;
0342 };
0343 
0344 void add_connection_cb(GObject *client, GAsyncResult *result, gpointer user_data)
0345 {
0346     AddConnectionData *data = static_cast<AddConnectionData *>(user_data);
0347 
0348     GError *error = nullptr;
0349     NMRemoteConnection *connection = nm_client_add_connection2_finish(NM_CLIENT(client), result, NULL, &error);
0350 
0351     if (error) {
0352         KNotification *notification = new KNotification(QStringLiteral("FailedToAddConnection"), KNotification::CloseOnTimeout, data->handler);
0353         notification->setTitle(i18n("Failed to add connection %1", data->id));
0354         notification->setComponentName(QStringLiteral("networkmanagement"));
0355         notification->setText(QString::fromUtf8(error->message));
0356         notification->setIconName(QStringLiteral("dialog-warning"));
0357         notification->sendEvent();
0358 
0359         g_error_free(error);
0360     } else {
0361         KNotification *notification = new KNotification(QStringLiteral("ConnectionAdded"), KNotification::CloseOnTimeout, data->handler);
0362         notification->setText(i18n("Connection %1 has been added", data->id));
0363         notification->setComponentName(QStringLiteral("networkmanagement"));
0364         notification->setTitle(data->id);
0365         notification->setIconName(QStringLiteral("dialog-information"));
0366         notification->sendEvent();
0367 
0368         g_object_unref(connection);
0369     }
0370 
0371     delete data;
0372 }
0373 
0374 void Handler::addConnection(NMConnection *connection)
0375 {
0376     NMClient *client = nm_client_new(nullptr, nullptr);
0377 
0378     AddConnectionData *userData = new AddConnectionData{QString::fromUtf8(nm_connection_get_id(connection)), this};
0379 
0380     nm_client_add_connection2(client,
0381                               nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL),
0382                               NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK,
0383                               nullptr,
0384                               true,
0385                               nullptr,
0386                               add_connection_cb,
0387                               userData);
0388 }
0389 
0390 void Handler::addAndActivateConnectionDBus(const NMVariantMapMap &map, const QString &device, const QString &specificObject)
0391 {
0392     QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::addAndActivateConnection(map, device, specificObject);
0393     auto watcher = new QDBusPendingCallWatcher(reply, this);
0394     watcher->setProperty("action", AddAndActivateConnection);
0395     watcher->setProperty("connection", map.value(QStringLiteral("connection")).value(QStringLiteral("id")));
0396     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0397 }
0398 
0399 void Handler::deactivateConnection(const QString &connection, const QString &device)
0400 {
0401     NetworkManager::Connection::Ptr con = NetworkManager::findConnection(connection);
0402 
0403     if (!con) {
0404         qCWarning(PLASMA_NM_LIBS_LOG) << "Not possible to deactivate this connection";
0405         return;
0406     }
0407 
0408     QDBusPendingReply<> reply;
0409     for (const NetworkManager::ActiveConnection::Ptr &active : NetworkManager::activeConnections()) {
0410         if (active->uuid() == con->uuid() && ((!active->devices().isEmpty() && active->devices().first() == device) || active->vpn())) {
0411             if (active->vpn()) {
0412                 reply = NetworkManager::deactivateConnection(active->path());
0413             } else {
0414                 NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(active->devices().first());
0415                 if (device) {
0416                     reply = device->disconnectInterface();
0417                 }
0418             }
0419         }
0420     }
0421 
0422     auto watcher = new QDBusPendingCallWatcher(reply, this);
0423     watcher->setProperty("action", Handler::DeactivateConnection);
0424     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0425 }
0426 
0427 void Handler::disconnectAll()
0428 {
0429     for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0430         device->disconnectInterface();
0431     }
0432 }
0433 
0434 void Handler::enableAirplaneMode(bool enable)
0435 {
0436     if (enable) {
0437         m_tmpWirelessEnabled = NetworkManager::isWirelessEnabled();
0438         m_tmpWwanEnabled = NetworkManager::isWwanEnabled();
0439         enableBluetooth(false);
0440         enableWireless(false);
0441         enableWwan(false);
0442     } else {
0443         enableBluetooth(true);
0444         if (m_tmpWirelessEnabled) {
0445             enableWireless(true);
0446         }
0447         if (m_tmpWwanEnabled) {
0448             enableWwan(true);
0449         }
0450     }
0451 }
0452 
0453 template<typename T>
0454 void makeDBusCall(const QDBusMessage &message, QObject *context, std::function<void(QDBusPendingReply<T>)> func)
0455 {
0456     QDBusPendingReply<T> reply = QDBusConnection::systemBus().asyncCall(message);
0457     auto watcher = new QDBusPendingCallWatcher(reply, context);
0458     QObject::connect(watcher, &QDBusPendingCallWatcher::finished, context, [func](QDBusPendingCallWatcher *watcher) {
0459         const QDBusPendingReply<T> reply = *watcher;
0460         if (!reply.isValid()) {
0461             qCWarning(PLASMA_NM_LIBS_LOG) << reply.error().message();
0462             return;
0463         }
0464         func(reply);
0465         watcher->deleteLater();
0466     });
0467 }
0468 
0469 void setBluetoothEnabled(const QString &path, bool enabled)
0470 {
0471     QDBusMessage message =
0472         QDBusMessage::createMethodCall(QStringLiteral("org.bluez"), path, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Set"));
0473     QList<QVariant> arguments;
0474     arguments << QLatin1String("org.bluez.Adapter1");
0475     arguments << QLatin1String("Powered");
0476     arguments << QVariant::fromValue(QDBusVariant(QVariant(enabled)));
0477     message.setArguments(arguments);
0478     QDBusConnection::systemBus().asyncCall(message);
0479 }
0480 
0481 void Handler::enableBluetooth(bool enable)
0482 {
0483     qDBusRegisterMetaType<QMap<QDBusObjectPath, NMVariantMapMap>>();
0484 
0485     const QDBusMessage getObjects = QDBusMessage::createMethodCall("org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
0486 
0487     makeDBusCall<QMap<QDBusObjectPath, NMVariantMapMap>>(getObjects, this, [enable, this](const auto reply) {
0488         for (const QDBusObjectPath &path : reply.value().keys()) {
0489             const QString objPath = path.path();
0490             qCDebug(PLASMA_NM_LIBS_LOG) << "inspecting path" << objPath;
0491             const QStringList interfaces = reply.value().value(path).keys();
0492             qCDebug(PLASMA_NM_LIBS_LOG) << "interfaces:" << interfaces;
0493 
0494             if (!interfaces.contains(QStringLiteral("org.bluez.Adapter1"))) {
0495                 continue;
0496             }
0497 
0498             // We need to check previous state first
0499             if (!enable) {
0500                 QDBusMessage getPowered = QDBusMessage::createMethodCall("org.bluez", objPath, "org.freedesktop.DBus.Properties", "Get");
0501                 const QList<QVariant> arguments{QLatin1String("org.bluez.Adapter1"), QLatin1String("Powered")};
0502                 getPowered.setArguments(arguments);
0503 
0504                 makeDBusCall<QVariant>(getPowered, this, [objPath, this](const auto reply) {
0505                     m_bluetoothAdapters.insert(objPath, reply.value().toBool());
0506                     setBluetoothEnabled(objPath, false);
0507                 });
0508             } else if (m_bluetoothAdapters.value(objPath)) {
0509                 setBluetoothEnabled(objPath, true);
0510             }
0511         }
0512     });
0513 }
0514 
0515 void Handler::enableNetworking(bool enable)
0516 {
0517     NetworkManager::setNetworkingEnabled(enable);
0518 }
0519 
0520 void Handler::enableWireless(bool enable)
0521 {
0522     NetworkManager::setWirelessEnabled(enable);
0523 }
0524 
0525 void Handler::enableWwan(bool enable)
0526 {
0527     NetworkManager::setWwanEnabled(enable);
0528 }
0529 
0530 void Handler::removeConnection(const QString &connection)
0531 {
0532     NetworkManager::Connection::Ptr con = NetworkManager::findConnection(connection);
0533 
0534     if (!con || con->uuid().isEmpty()) {
0535         qCWarning(PLASMA_NM_LIBS_LOG) << "Not possible to remove connection " << connection;
0536         return;
0537     }
0538 
0539     // Remove slave connections
0540     for (const NetworkManager::Connection::Ptr &connection : NetworkManager::listConnections()) {
0541         NetworkManager::ConnectionSettings::Ptr settings = connection->settings();
0542         if (settings->master() == con->uuid()) {
0543             connection->remove();
0544         }
0545     }
0546 
0547     QDBusPendingReply<> reply = con->remove();
0548     auto watcher = new QDBusPendingCallWatcher(reply, this);
0549     watcher->setProperty("action", Handler::RemoveConnection);
0550     watcher->setProperty("connection", con->name());
0551     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0552 }
0553 
0554 void Handler::updateConnection(const NetworkManager::Connection::Ptr &connection, const NMVariantMapMap &map)
0555 {
0556     QDBusPendingReply<> reply = connection->update(map);
0557     auto watcher = new QDBusPendingCallWatcher(reply, this);
0558     watcher->setProperty("action", UpdateConnection);
0559     watcher->setProperty("connection", connection->name());
0560     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0561 }
0562 
0563 void Handler::requestScan(const QString &interface)
0564 {
0565     for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0566         if (device->type() == NetworkManager::Device::Wifi) {
0567             NetworkManager::WirelessDevice::Ptr wifiDevice = device.objectCast<NetworkManager::WirelessDevice>();
0568 
0569             if (wifiDevice && wifiDevice->state() != NetworkManager::WirelessDevice::Unavailable) {
0570                 if (!interface.isEmpty() && interface != wifiDevice->interfaceName()) {
0571                     continue;
0572                 }
0573 
0574                 if (!checkRequestScanRateLimit(wifiDevice)) {
0575                     QDateTime now = QDateTime::currentDateTime();
0576                     // for NM < 1.12, lastScan is not available
0577                     QDateTime lastScan = wifiDevice->lastScan();
0578                     QDateTime lastRequestScan = wifiDevice->lastRequestScan();
0579                     // Compute the next time we can run a scan
0580                     int timeout = NM_REQUESTSCAN_LIMIT_RATE;
0581                     if (lastScan.isValid() && lastScan.msecsTo(now) < NM_REQUESTSCAN_LIMIT_RATE) {
0582                         timeout = NM_REQUESTSCAN_LIMIT_RATE - lastScan.msecsTo(now);
0583                     } else if (lastRequestScan.isValid() && lastRequestScan.msecsTo(now) < NM_REQUESTSCAN_LIMIT_RATE) {
0584                         timeout = NM_REQUESTSCAN_LIMIT_RATE - lastRequestScan.msecsTo(now);
0585                     }
0586                     qCDebug(PLASMA_NM_LIBS_LOG) << "Rescheduling a request scan for" << wifiDevice->interfaceName() << "in" << timeout;
0587                     scheduleRequestScan(wifiDevice->interfaceName(), timeout);
0588 
0589                     if (!interface.isEmpty()) {
0590                         return;
0591                     }
0592                     continue;
0593                 } else if (m_wirelessScanRetryTimer.contains(interface)) {
0594                     m_wirelessScanRetryTimer.value(interface)->stop();
0595                     delete m_wirelessScanRetryTimer.take(interface);
0596                 }
0597 
0598                 qCDebug(PLASMA_NM_LIBS_LOG) << "Requesting wifi scan on device" << wifiDevice->interfaceName();
0599                 incrementScansCount();
0600                 QDBusPendingReply<> reply = wifiDevice->requestScan();
0601                 auto watcher = new QDBusPendingCallWatcher(reply, this);
0602                 watcher->setProperty("action", Handler::RequestScan);
0603                 watcher->setProperty("interface", wifiDevice->interfaceName());
0604                 connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0605             }
0606         }
0607     }
0608 }
0609 
0610 void Handler::incrementScansCount()
0611 {
0612     m_ongoingScansCount += 1;
0613     if (m_ongoingScansCount == 1) {
0614         Q_EMIT scanningChanged();
0615     }
0616 }
0617 
0618 void Handler::decrementScansCount()
0619 {
0620     if (m_ongoingScansCount == 0) {
0621         qCDebug(PLASMA_NM_LIBS_LOG) << "Extra decrementScansCount() called";
0622         return;
0623     }
0624     m_ongoingScansCount -= 1;
0625     if (m_ongoingScansCount == 0) {
0626         Q_EMIT scanningChanged();
0627     }
0628 }
0629 
0630 void Handler::createHotspot()
0631 {
0632     bool foundInactive = false;
0633     bool useApMode = false;
0634     NetworkManager::WirelessDevice::Ptr wifiDev;
0635 
0636     NetworkManager::ConnectionSettings::Ptr connectionSettings;
0637     connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Wireless));
0638 
0639     NetworkManager::WirelessSetting::Ptr wifiSetting =
0640         connectionSettings->setting(NetworkManager::Setting::Wireless).dynamicCast<NetworkManager::WirelessSetting>();
0641     wifiSetting->setMode(NetworkManager::WirelessSetting::Adhoc);
0642     wifiSetting->setSsid(Configuration::self().hotspotName().toUtf8());
0643 
0644     for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0645         if (device->type() == NetworkManager::Device::Wifi) {
0646             wifiDev = device.objectCast<NetworkManager::WirelessDevice>();
0647             if (wifiDev) {
0648                 if (!wifiDev->isActive()) {
0649                     foundInactive = true;
0650                 } else {
0651                     // Prefer previous device if it was inactive
0652                     if (foundInactive) {
0653                         break;
0654                     }
0655                 }
0656 
0657                 if (wifiDev->wirelessCapabilities().testFlag(NetworkManager::WirelessDevice::ApCap)) {
0658                     useApMode = true;
0659                 }
0660 
0661                 // We prefer inactive wireless card with AP capabilities
0662                 if (foundInactive && useApMode) {
0663                     break;
0664                 }
0665             }
0666         }
0667     }
0668 
0669     if (!wifiDev) {
0670         qCWarning(PLASMA_NM_LIBS_LOG) << "Failed to create hotspot: missing wireless device";
0671         return;
0672     }
0673 
0674     wifiSetting->setInitialized(true);
0675     wifiSetting->setMode(useApMode ? NetworkManager::WirelessSetting::Ap : NetworkManager::WirelessSetting::Adhoc);
0676 
0677     if (!Configuration::self().hotspotPassword().isEmpty()) {
0678         NetworkManager::WirelessSecuritySetting::Ptr wifiSecurity =
0679             connectionSettings->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast<NetworkManager::WirelessSecuritySetting>();
0680         wifiSecurity->setInitialized(true);
0681 
0682         if (useApMode) {
0683             // Use WPA2
0684             wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaPsk);
0685             wifiSecurity->setPsk(Configuration::self().hotspotPassword());
0686             wifiSecurity->setPskFlags(NetworkManager::Setting::AgentOwned);
0687         } else {
0688             // Use WEP
0689             wifiSecurity->setKeyMgmt(NetworkManager::WirelessSecuritySetting::Wep);
0690             wifiSecurity->setWepKeyType(NetworkManager::WirelessSecuritySetting::Passphrase);
0691             wifiSecurity->setWepTxKeyindex(0);
0692             wifiSecurity->setWepKey0(Configuration::self().hotspotPassword());
0693             wifiSecurity->setWepKeyFlags(NetworkManager::Setting::AgentOwned);
0694             wifiSecurity->setAuthAlg(NetworkManager::WirelessSecuritySetting::Open);
0695         }
0696     }
0697 
0698     NetworkManager::Ipv4Setting::Ptr ipv4Setting = connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast<NetworkManager::Ipv4Setting>();
0699     ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Shared);
0700     ipv4Setting->setInitialized(true);
0701 
0702     connectionSettings->setId(Configuration::self().hotspotName());
0703     connectionSettings->setAutoconnect(false);
0704     connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid());
0705 
0706     const QVariantMap options = {{QLatin1String("persist"), QLatin1String("volatile")}};
0707 
0708     QDBusPendingReply<QDBusObjectPath, QDBusObjectPath, QVariantMap> reply =
0709         NetworkManager::addAndActivateConnection2(connectionSettings->toMap(), wifiDev->uni(), QString(), options);
0710     auto watcher = new QDBusPendingCallWatcher(reply, this);
0711     watcher->setProperty("action", Handler::CreateHotspot);
0712     watcher->setProperty("connection", Configuration::self().hotspotName());
0713     connect(watcher, &QDBusPendingCallWatcher::finished, this, &Handler::replyFinished);
0714     connect(watcher, &QDBusPendingCallWatcher::finished, this, QOverload<QDBusPendingCallWatcher *>::of(&Handler::hotspotCreated));
0715 }
0716 
0717 void Handler::stopHotspot()
0718 {
0719     const QString activeConnectionPath = Configuration::self().hotspotConnectionPath();
0720 
0721     if (activeConnectionPath.isEmpty()) {
0722         return;
0723     }
0724 
0725     NetworkManager::ActiveConnection::Ptr hotspot = NetworkManager::findActiveConnection(activeConnectionPath);
0726 
0727     if (!hotspot) {
0728         return;
0729     }
0730 
0731     NetworkManager::deactivateConnection(activeConnectionPath);
0732     Configuration::self().setHotspotConnectionPath(QString());
0733 
0734     Q_EMIT hotspotDisabled();
0735 }
0736 
0737 bool Handler::checkRequestScanRateLimit(const NetworkManager::WirelessDevice::Ptr &wifiDevice)
0738 {
0739     QDateTime now = QDateTime::currentDateTime();
0740     QDateTime lastScan = wifiDevice->lastScan();
0741     QDateTime lastRequestScan = wifiDevice->lastRequestScan();
0742 
0743     // if the last scan finished within the last 10 seconds
0744     bool ret = lastScan.isValid() && lastScan.msecsTo(now) < NM_REQUESTSCAN_LIMIT_RATE;
0745     // or if the last Request was sent within the last 10 seconds
0746     ret |= lastRequestScan.isValid() && lastRequestScan.msecsTo(now) < NM_REQUESTSCAN_LIMIT_RATE;
0747     // skip the request scan
0748     if (ret) {
0749         qCDebug(PLASMA_NM_LIBS_LOG) << "Last scan finished" << lastScan.msecsTo(now) << "ms ago and last request scan was sent" //
0750                                     << lastRequestScan.msecsTo(now) << "ms ago, Skipping scanning interface:" << wifiDevice->interfaceName();
0751         return false;
0752     }
0753     return true;
0754 }
0755 
0756 bool Handler::checkHotspotSupported()
0757 {
0758     if (NetworkManager::checkVersion(1, 16, 0)) {
0759         bool unusedWifiFound = false;
0760         bool wifiFound = false;
0761 
0762         for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
0763             if (device->type() == NetworkManager::Device::Wifi) {
0764                 wifiFound = true;
0765 
0766                 NetworkManager::WirelessDevice::Ptr wifiDev = device.objectCast<NetworkManager::WirelessDevice>();
0767                 if (wifiDev && !wifiDev->isActive()) {
0768                     unusedWifiFound = true;
0769                 }
0770             }
0771         }
0772 
0773         if (!wifiFound) {
0774             return false;
0775         }
0776 
0777         if (unusedWifiFound) {
0778             return true;
0779         }
0780 
0781         // Check if the primary connection which is used for internet connectivity is not using WiFi
0782         if (NetworkManager::primaryConnectionType() != NetworkManager::ConnectionSettings::Wireless) {
0783             return true;
0784         }
0785     }
0786 
0787     return false;
0788 }
0789 
0790 void Handler::scheduleRequestScan(const QString &interface, int timeout)
0791 {
0792     QTimer *timer;
0793     if (!m_wirelessScanRetryTimer.contains(interface)) {
0794         // create a timer for the interface
0795         timer = new QTimer();
0796         timer->setSingleShot(true);
0797         m_wirelessScanRetryTimer.insert(interface, timer);
0798         auto retryAction = [this, interface]() {
0799             requestScan(interface);
0800         };
0801         connect(timer, &QTimer::timeout, this, retryAction);
0802     } else {
0803         // set the new value for an existing timer
0804         timer = m_wirelessScanRetryTimer.value(interface);
0805         if (timer->isActive()) {
0806             timer->stop();
0807         }
0808     }
0809 
0810     // +1 ms is added to avoid having the scan being rejetted by nm
0811     // because it is run at the exact last millisecond of the requestScan threshold
0812     timer->setInterval(timeout + 1);
0813     timer->start();
0814 }
0815 
0816 void Handler::scanRequestFailed(const QString &interface)
0817 {
0818     scheduleRequestScan(interface, 2000);
0819 }
0820 
0821 void Handler::secretAgentError(const QString &connectionPath, const QString &message)
0822 {
0823     // If the password was wrong, forget it
0824     removeConnection(connectionPath);
0825     Q_EMIT connectionActivationFailed(connectionPath, message);
0826 }
0827 
0828 void Handler::replyFinished(QDBusPendingCallWatcher *watcher)
0829 {
0830     QDBusPendingReply<> reply = *watcher;
0831     if (reply.isError() || !reply.isValid()) {
0832         KNotification *notification = nullptr;
0833         QString error = reply.error().message();
0834         Handler::HandlerAction action = (Handler::HandlerAction)watcher->property("action").toUInt();
0835         switch (action) {
0836         case Handler::ActivateConnection:
0837             notification = new KNotification(QStringLiteral("FailedToActivateConnection"), KNotification::CloseOnTimeout, this);
0838             notification->setTitle(i18n("Failed to activate %1", watcher->property("connection").toString()));
0839             break;
0840         case Handler::AddAndActivateConnection:
0841             notification = new KNotification(QStringLiteral("FailedToAddConnection"), KNotification::CloseOnTimeout, this);
0842             notification->setTitle(i18n("Failed to add %1", watcher->property("connection").toString()));
0843             break;
0844         case Handler::AddConnection:
0845             notification = new KNotification(QStringLiteral("FailedToAddConnection"), KNotification::CloseOnTimeout, this);
0846             notification->setTitle(i18n("Failed to add connection %1", watcher->property("connection").toString()));
0847             break;
0848         case Handler::DeactivateConnection:
0849             notification = new KNotification(QStringLiteral("FailedToDeactivateConnection"), KNotification::CloseOnTimeout, this);
0850             notification->setTitle(i18n("Failed to deactivate %1", watcher->property("connection").toString()));
0851             break;
0852         case Handler::RemoveConnection:
0853             notification = new KNotification(QStringLiteral("FailedToRemoveConnection"), KNotification::CloseOnTimeout, this);
0854             notification->setTitle(i18n("Failed to remove %1", watcher->property("connection").toString()));
0855             break;
0856         case Handler::UpdateConnection:
0857             notification = new KNotification(QStringLiteral("FailedToUpdateConnection"), KNotification::CloseOnTimeout, this);
0858             notification->setTitle(i18n("Failed to update connection %1", watcher->property("connection").toString()));
0859             break;
0860         case Handler::RequestScan: {
0861             const QString interface = watcher->property("interface").toString();
0862             qCWarning(PLASMA_NM_LIBS_LOG) << "Wireless scan on" << interface << "failed:" << error;
0863             scanRequestFailed(interface);
0864             decrementScansCount();
0865             break;
0866         }
0867         case Handler::CreateHotspot:
0868             notification = new KNotification(QStringLiteral("FailedToCreateHotspot"), KNotification::CloseOnTimeout, this);
0869             notification->setTitle(i18n("Failed to create hotspot %1", watcher->property("connection").toString()));
0870             break;
0871         default:
0872             break;
0873         }
0874 
0875         if (notification) {
0876             notification->setComponentName(QStringLiteral("networkmanagement"));
0877             notification->setText(error);
0878             notification->setIconName(QStringLiteral("dialog-warning"));
0879             notification->sendEvent();
0880         }
0881     } else {
0882         KNotification *notification = nullptr;
0883         Handler::HandlerAction action = (Handler::HandlerAction)watcher->property("action").toUInt();
0884 
0885         switch (action) {
0886         case Handler::AddConnection:
0887             notification = new KNotification(QStringLiteral("ConnectionAdded"), KNotification::CloseOnTimeout, this);
0888             notification->setText(i18n("Connection %1 has been added", watcher->property("connection").toString()));
0889             break;
0890         case Handler::RemoveConnection:
0891             notification = new KNotification(QStringLiteral("ConnectionRemoved"), KNotification::CloseOnTimeout, this);
0892             notification->setText(i18n("Connection %1 has been removed", watcher->property("connection").toString()));
0893             break;
0894         case Handler::UpdateConnection:
0895             notification = new KNotification(QStringLiteral("ConnectionUpdated"), KNotification::CloseOnTimeout, this);
0896             notification->setText(i18n("Connection %1 has been updated", watcher->property("connection").toString()));
0897             break;
0898         case Handler::RequestScan:
0899             qCDebug(PLASMA_NM_LIBS_LOG) << "Wireless scan on" << watcher->property("interface").toString() << "succeeded";
0900             decrementScansCount();
0901             break;
0902         default:
0903             break;
0904         }
0905 
0906         if (notification) {
0907             notification->setComponentName(QStringLiteral("networkmanagement"));
0908             notification->setTitle(watcher->property("connection").toString());
0909             notification->setIconName(QStringLiteral("dialog-information"));
0910             notification->sendEvent();
0911         }
0912     }
0913 
0914     watcher->deleteLater();
0915 }
0916 
0917 void Handler::hotspotCreated(QDBusPendingCallWatcher *watcher)
0918 {
0919     QDBusPendingReply<QDBusObjectPath, QDBusObjectPath, QVariantMap> reply = *watcher;
0920 
0921     if (!reply.isError() && reply.isValid()) {
0922         const QString activeConnectionPath = reply.argumentAt(1).value<QDBusObjectPath>().path();
0923 
0924         if (activeConnectionPath.isEmpty()) {
0925             return;
0926         }
0927 
0928         Configuration::self().setHotspotConnectionPath(activeConnectionPath);
0929 
0930         NetworkManager::ActiveConnection::Ptr hotspot = NetworkManager::findActiveConnection(activeConnectionPath);
0931 
0932         if (!hotspot) {
0933             return;
0934         }
0935 
0936         connect(hotspot.data(), &NetworkManager::ActiveConnection::stateChanged, [=](NetworkManager::ActiveConnection::State state) {
0937             if (state > NetworkManager::ActiveConnection::Activated) {
0938                 Configuration::self().setHotspotConnectionPath(QString());
0939                 Q_EMIT hotspotDisabled();
0940             }
0941         });
0942 
0943         Q_EMIT hotspotCreated();
0944     }
0945 }
0946 
0947 void Handler::primaryConnectionTypeChanged(NetworkManager::ConnectionSettings::ConnectionType type)
0948 {
0949     Q_UNUSED(type)
0950     m_hotspotSupported = checkHotspotSupported();
0951     Q_EMIT hotspotSupportedChanged(m_hotspotSupported);
0952 }
0953 
0954 void Handler::unlockRequiredChanged(MMModemLock modemLock)
0955 {
0956     if (modemLock == MM_MODEM_LOCK_NONE) {
0957         activateConnection(m_tmpConnectionPath, m_tmpDevicePath, m_tmpSpecificPath);
0958     }
0959 }
0960 
0961 void Handler::slotRequestWifiCode(QDBusPendingCallWatcher *watcher)
0962 {
0963     watcher->deleteLater();
0964 
0965     QString ret = watcher->property("ret").toString();
0966     const QString connectionName = watcher->property("connectionName").toString();
0967     QDBusPendingReply<NMVariantMapMap> reply = *watcher;
0968     if (!reply.isValid() || reply.isError()) {
0969         Q_EMIT wifiCodeReceived(ret % QLatin1Char(';'), connectionName);
0970         return;
0971     }
0972 
0973     const auto secret = reply.argumentAt<0>()[watcher->property("key").toString()];
0974     QString pass;
0975     switch (static_cast<NetworkManager::WirelessSecurityType>(watcher->property("securityType").toInt())) {
0976     case NetworkManager::NoneSecurity:
0977         break;
0978     case NetworkManager::WpaPsk:
0979     case NetworkManager::Wpa2Psk:
0980     case NetworkManager::SAE:
0981         pass = secret[QStringLiteral("psk")].toString();
0982         break;
0983     default:
0984         Q_EMIT wifiCodeReceived(QString(), connectionName);
0985         return;
0986     }
0987     if (!pass.isEmpty()) {
0988         ret += QStringLiteral("P:") % pass % QLatin1Char(';');
0989     }
0990 
0991     Q_EMIT wifiCodeReceived(ret % QLatin1Char(';'), connectionName);
0992 }