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