#include "NetworkManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include typedef QMap NMConnectionSettings; static const QString LOG_FILE = "/tmp/calamares-wifi.log"; static constexpr uint NmDeviceTypeWifi = 2; static constexpr uint NmApFlagPrivacy = 0x1; static constexpr uint NmKeyMgmtPsk = 0x100; static constexpr uint NmKeyMgmt8021x = 0x200; static constexpr uint NmKeyMgmtSae = 0x400; static constexpr int NmPmfRequired = 3; static void wifiLog(const QString& message) { QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"); QString logLine = QString("[%1] %2\n").arg(timestamp, message); QFile file(LOG_FILE); if (file.open(QIODevice::Append | QIODevice::Text)) { QTextStream stream(&file); stream << logLine; file.close(); } qDebug().noquote() << "[WiFi Module]" << message; } NetworkManager::NetworkManager(QObject* parent) : QObject(parent) { qDBusRegisterMetaType(); QFile file(LOG_FILE); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream stream(&file); stream << "=== Calamares WiFi Module Log ===\n"; file.close(); } wifiLog("NetworkManager initialized"); scan(); updateConnectionState(); QTimer* timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &NetworkManager::updateConnectionState); timer->start(3000); } bool NetworkManager::isConnected() const { return m_connected; } void NetworkManager::updateConnectionState() { QDBusInterface nm( "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager", QDBusConnection::systemBus() ); if (!nm.isValid()) { return; } bool newState = false; QDBusObjectPath primary = nm.property("PrimaryConnection").value(); if (primary.path() != "/" && !primary.path().isEmpty()) { newState = true; } if (!newState) { QDBusReply> devices = nm.call("GetDevices"); if (devices.isValid()) { for (const auto& dev : devices.value()) { QDBusInterface devProps( "org.freedesktop.NetworkManager", dev.path(), "org.freedesktop.DBus.Properties", QDBusConnection::systemBus() ); QDBusReply typeReply = devProps.call( "Get", "org.freedesktop.NetworkManager.Device", "DeviceType" ); if (typeReply.isValid() && typeReply.value().toUInt() == 2) { QDBusReply stateReply = devProps.call( "Get", "org.freedesktop.NetworkManager.Device", "State" ); if (stateReply.isValid()) { uint state = stateReply.value().toUInt(); wifiLog(QString("WiFi device state: %1").arg(state)); if (state == 100) { newState = true; break; } } } } } } if (newState != m_connected) { m_connected = newState; wifiLog(QString("Connection state changed to: %1").arg(newState ? "connected" : "disconnected")); emit connectionChanged(); } } void NetworkManager::scan() { wifiLog("Starting scan..."); QDBusInterface nm( "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager", QDBusConnection::systemBus() ); if (!nm.isValid()) { wifiLog("NetworkManager DBus interface not valid!"); emit networksChanged(); return; } QDBusReply> devices = nm.call("GetDevices"); if (!devices.isValid()) { wifiLog(QString("GetDevices failed: %1").arg(devices.error().message())); emit networksChanged(); return; } wifiLog(QString("Found %1 devices").arg(devices.value().size())); bool foundWireless = false; for (const auto& dev : devices.value()) { QDBusInterface devProps( "org.freedesktop.NetworkManager", dev.path(), "org.freedesktop.DBus.Properties", QDBusConnection::systemBus() ); QDBusReply typeReply = devProps.call( "Get", "org.freedesktop.NetworkManager.Device", "DeviceType" ); uint deviceType = typeReply.isValid() ? typeReply.value().toUInt() : 0; wifiLog(QString("Device %1 type: %2").arg(dev.path()).arg(deviceType)); if (deviceType != NmDeviceTypeWifi) continue; foundWireless = true; wifiLog(QString("Found wireless device: %1").arg(dev.path())); QDBusInterface wirelessIf( "org.freedesktop.NetworkManager", dev.path(), "org.freedesktop.NetworkManager.Device.Wireless", QDBusConnection::systemBus() ); QDBusMessage reply = wirelessIf.call("RequestScan", QVariantMap()); if (reply.type() == QDBusMessage::ErrorMessage) { wifiLog(QString("Scan request failed: %1").arg(reply.errorMessage())); } else { wifiLog("Scan request sent successfully"); } } if (!foundWireless) { wifiLog("No wireless devices found!"); } QTimer::singleShot(2000, this, &NetworkManager::fetchAccessPoints); } void NetworkManager::fetchAccessPoints() { wifiLog("Fetching access points..."); QDBusInterface nm( "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager", QDBusConnection::systemBus() ); if (!nm.isValid()) { wifiLog("NM interface not valid in fetchAccessPoints"); m_accessPoints.clear(); emit networksChanged(); return; } QDBusReply> devices = nm.call("GetDevices"); if (!devices.isValid()) { wifiLog("GetDevices failed in fetchAccessPoints"); m_accessPoints.clear(); emit networksChanged(); return; } QMap strongest; for (const auto& dev : devices.value()) { QDBusInterface devProps( "org.freedesktop.NetworkManager", dev.path(), "org.freedesktop.DBus.Properties", QDBusConnection::systemBus() ); QDBusReply typeReply = devProps.call( "Get", "org.freedesktop.NetworkManager.Device", "DeviceType" ); if (!typeReply.isValid() || typeReply.value().toUInt() != NmDeviceTypeWifi) continue; QDBusInterface wirelessIf( "org.freedesktop.NetworkManager", dev.path(), "org.freedesktop.NetworkManager.Device.Wireless", QDBusConnection::systemBus() ); QDBusReply> aps = wirelessIf.call("GetAccessPoints"); if (!aps.isValid()) { wifiLog(QString("GetAccessPoints failed: %1").arg(aps.error().message())); continue; } wifiLog(QString("Found %1 access points on device %2").arg(aps.value().size()).arg(dev.path())); for (const auto& ap : aps.value()) { QDBusInterface apIf( "org.freedesktop.NetworkManager", ap.path(), "org.freedesktop.NetworkManager.AccessPoint", QDBusConnection::systemBus() ); const QString ssid = QString::fromUtf8(apIf.property("Ssid").toByteArray()); if (ssid.isEmpty()) continue; const uint frequency = apIf.property("Frequency").toUInt(); const int strength = apIf.property("Strength").toInt(); const bool secured = (apIf.property("Flags").toUInt() & NmApFlagPrivacy) != 0 || apIf.property("WpaFlags").toUInt() != 0 || apIf.property("RsnFlags").toUInt() != 0; wifiLog(QString("Found SSID: %1 (%2 MHz, %3%)").arg(ssid).arg(frequency).arg(strength)); if (strongest.contains(ssid) && strongest[ssid].value("strength").toInt() >= strength) continue; QVariantMap item; item.insert("ssid", ssid); item.insert("strength", strength); item.insert("secured", secured); item.insert("band", frequency > 5900 ? QStringLiteral("6 GHz") : frequency > 4000 ? QStringLiteral("5 GHz") : QStringLiteral("2.4 GHz")); strongest.insert(ssid, item); } } QList sorted = strongest.values(); std::sort(sorted.begin(), sorted.end(), [](const QVariantMap& a, const QVariantMap& b) { return a.value("strength").toInt() > b.value("strength").toInt(); }); m_accessPoints.clear(); for (const auto& item : sorted) m_accessPoints << item; wifiLog(QString("Total networks found: %1").arg(m_accessPoints.size())); emit networksChanged(); } void NetworkManager::connectTo(const QString& ssid, const QString& password) { wifiLog(QString("Connecting to: %1").arg(ssid)); QDBusInterface nm( "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager", QDBusConnection::systemBus() ); if (!nm.isValid()) { wifiLog("NM interface not valid for connection"); return; } QDBusReply> devices = nm.call("GetDevices"); if (!devices.isValid()) { wifiLog(QString("GetDevices failed: %1").arg(devices.error().message())); return; } QDBusObjectPath wirelessDevice("/"); QDBusObjectPath accessPoint("/"); uint apFlags = 0; uint apWpaFlags = 0; uint apRsnFlags = 0; for (const auto& dev : devices.value()) { QDBusInterface devProps( "org.freedesktop.NetworkManager", dev.path(), "org.freedesktop.DBus.Properties", QDBusConnection::systemBus() ); QDBusReply typeReply = devProps.call( "Get", "org.freedesktop.NetworkManager.Device", "DeviceType" ); if (typeReply.isValid() && typeReply.value().toUInt() == 2) { wirelessDevice = dev; wifiLog(QString("Using wireless device: %1").arg(dev.path())); QDBusInterface wirelessIf( "org.freedesktop.NetworkManager", dev.path(), "org.freedesktop.NetworkManager.Device.Wireless", QDBusConnection::systemBus() ); QDBusReply> aps = wirelessIf.call("GetAccessPoints"); if (aps.isValid()) { for (const auto& ap : aps.value()) { QDBusInterface apIf( "org.freedesktop.NetworkManager", ap.path(), "org.freedesktop.NetworkManager.AccessPoint", QDBusConnection::systemBus() ); QByteArray apSsid = apIf.property("Ssid").toByteArray(); if (QString::fromUtf8(apSsid) == ssid) { accessPoint = ap; apFlags = apIf.property("Flags").toUInt(); apWpaFlags = apIf.property("WpaFlags").toUInt(); apRsnFlags = apIf.property("RsnFlags").toUInt(); wifiLog(QString("Found access point: %1").arg(ap.path())); wifiLog(QString("AP Flags: 0x%1, WpaFlags: 0x%2, RsnFlags: 0x%3") .arg(apFlags, 0, 16).arg(apWpaFlags, 0, 16).arg(apRsnFlags, 0, 16)); break; } } } break; } } if (wirelessDevice.path() == "/" || wirelessDevice.path().isEmpty()) { wifiLog("No wireless device found!"); return; } bool apSupportsPsk = ((apWpaFlags & NmKeyMgmtPsk) != 0) || ((apRsnFlags & NmKeyMgmtPsk) != 0); bool apSupportsSae = (apRsnFlags & NmKeyMgmtSae) != 0; bool apSupportsEnterprise = ((apWpaFlags & NmKeyMgmt8021x) != 0) || ((apRsnFlags & NmKeyMgmt8021x) != 0); bool apIsOpen = (apFlags == 0) && (apWpaFlags == 0) && (apRsnFlags == 0); bool apHasPrivacy = (apFlags & NmApFlagPrivacy) != 0; wifiLog(QString("Security detection: PSK=%1, SAE=%2, Enterprise=%3, Open=%4, Privacy=%5") .arg(apSupportsPsk).arg(apSupportsSae).arg(apSupportsEnterprise).arg(apIsOpen).arg(apHasPrivacy)); QVariantMap connection; connection["type"] = QString("802-11-wireless"); connection["id"] = ssid; connection["autoconnect"] = true; QVariantMap wifi; wifi["ssid"] = ssid.toUtf8(); wifi["mode"] = QString("infrastructure"); bool isHiddenNetwork = (accessPoint.path() == "/" || accessPoint.path().isEmpty()); if (isHiddenNetwork) { wifi["hidden"] = true; wifiLog("Treating as hidden network"); } NMConnectionSettings settings; settings["connection"] = connection; QVariantMap ipv4; ipv4["method"] = QString("auto"); settings["ipv4"] = ipv4; QVariantMap ipv6; ipv6["method"] = QString("auto"); settings["ipv6"] = ipv6; if (!password.isEmpty() && (apSupportsPsk || apSupportsSae || isHiddenNetwork)) { QVariantMap wifiSecurity; if (apSupportsSae && !apSupportsPsk) { wifiLog("Using WPA3-SAE security"); wifiSecurity["key-mgmt"] = QString("sae"); wifiSecurity["psk"] = password; wifiSecurity["pmf"] = NmPmfRequired; } else if (apSupportsPsk) { wifiLog("Using WPA-PSK security"); wifiSecurity["key-mgmt"] = QString("wpa-psk"); wifiSecurity["psk"] = password; } else if (isHiddenNetwork) { wifiLog("Hidden network with password, assuming WPA-PSK"); wifiSecurity["key-mgmt"] = QString("wpa-psk"); wifiSecurity["psk"] = password; } wifi["security"] = QString("802-11-wireless-security"); settings["802-11-wireless"] = wifi; settings["802-11-wireless-security"] = wifiSecurity; } else if (apSupportsEnterprise) { wifiLog("WARNING: Enterprise (802.1X) networks are not supported by this simple connector"); return; } else if (apIsOpen || (!apHasPrivacy && !apSupportsPsk && !apSupportsSae)) { wifiLog("Connecting to open network (no security)"); settings["802-11-wireless"] = wifi; } else if (!password.isEmpty() && !apSupportsPsk && !apSupportsSae) { wifiLog("WARNING: Password provided but AP does not support PSK/SAE authentication!"); wifiLog("Attempting to connect as open network..."); settings["802-11-wireless"] = wifi; } else { wifiLog("No password provided, treating as open network"); settings["802-11-wireless"] = wifi; } wifiLog("Calling AddAndActivateConnection..."); QDBusMessage msg = QDBusMessage::createMethodCall( "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager", "AddAndActivateConnection" ); QDBusArgument settingsArg; settingsArg.beginMap(QMetaType::fromType(), QMetaType::fromType()); for (auto it = settings.constBegin(); it != settings.constEnd(); ++it) { settingsArg.beginMapEntry(); settingsArg << it.key() << it.value(); settingsArg.endMapEntry(); } settingsArg.endMap(); msg << QVariant::fromValue(settingsArg); msg << QVariant::fromValue(wirelessDevice); msg << QVariant::fromValue(accessPoint); QDBusMessage reply = QDBusConnection::systemBus().call(msg); if (reply.type() == QDBusMessage::ErrorMessage) { wifiLog(QString("Connection failed: %1 - %2").arg(reply.errorName(), reply.errorMessage())); emit connectionFailed(reply.errorMessage()); } else { wifiLog("Connection initiated successfully"); m_lastConnectedSsid = ssid; m_lastConnectedPassword = password; wifiLog(QString("Stored credentials - SSID: '%1', Password length: %2").arg(m_lastConnectedSsid).arg(m_lastConnectedPassword.length())); if (apSupportsSae && !apSupportsPsk) { m_lastSecurityType = "sae"; } else if (apSupportsPsk) { m_lastSecurityType = "wpa-psk"; } else { m_lastSecurityType = ""; } wifiLog(QString("Stored security type: '%1'").arg(m_lastSecurityType)); m_connectionCheckCount = 0; startConnectionCheck(); } } void NetworkManager::startConnectionCheck() { m_connectionCheckCount++; wifiLog(QString("Connection check attempt %1/10").arg(m_connectionCheckCount)); updateConnectionState(); if (m_connected) { wifiLog("Connection confirmed!"); return; } if (m_connectionCheckCount < 10) { QTimer::singleShot(1000, this, &NetworkManager::startConnectionCheck); } else { wifiLog("Connection check timeout - connection may have failed"); emit connectionFailed("Connection timeout"); } }