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