From f1d884b6dad5a93d7a3077b6b05d3ec7fcd9a6ea Mon Sep 17 00:00:00 2001 From: Teemu Holappa Date: Thu, 11 Feb 2016 11:50:55 +0200 Subject: Refactored Qml plugins into modules. Separated C++ and Qml interfaces. All the UI's from plugins are moved to the settingsui folder. Change-Id: Id6a6623346b18321357bc42d24121c4d9cdfd098 Reviewed-by: Kimmo Ollila --- .../wpasupplicant/qnetworksettingsmanager_p.cpp | 433 +++++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 src/networksettings/wpasupplicant/qnetworksettingsmanager_p.cpp (limited to 'src/networksettings/wpasupplicant/qnetworksettingsmanager_p.cpp') diff --git a/src/networksettings/wpasupplicant/qnetworksettingsmanager_p.cpp b/src/networksettings/wpasupplicant/qnetworksettingsmanager_p.cpp new file mode 100644 index 0000000..17a1f2f --- /dev/null +++ b/src/networksettings/wpasupplicant/qnetworksettingsmanager_p.cpp @@ -0,0 +1,433 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Device Utilities module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include "qnetworksettingsmanager_p.h" +#include "qwificontroller_p.h" +#include "qnetworksettingsinterface_p.h" +#include "qnetworksettingsservice_p.h" +#include "qnetworksettingsuseragent.h" +#include "qwifisupplicant_p.h" + +QNetworkSettingsManagerPrivate::QNetworkSettingsManagerPrivate(QNetworkSettingsManager *parent) + :QObject(parent) + ,q_ptr(parent) +{ + m_serviceFilter.setSourceModel(&m_serviceModel); + m_wifiController = new QWifiController(this); + m_wifiController->asyncCall(QWifiController::InitializeBackend); + + QObject::connect(m_wifiController, &QWifiController::backendStateChanged, + this, &QNetworkSettingsManagerPrivate::handleBackendStateChanged); + QObject::connect(m_wifiController, &QWifiController::dhcpRequestFinished, + this, &QNetworkSettingsManagerPrivate::handleDhcpRequestFinished); + + QObject::connect(m_wifiController, &QWifiController::raiseError, this, &QNetworkSettingsManagerPrivate::updateLastError); + m_wifiController->start(); + + updateWifiState(); +} + +QNetworkSettingsManagerPrivate::~QNetworkSettingsManagerPrivate() +{ + m_wifiController->asyncCall(QWifiController::ExitEventLoop); + m_wifiController->wait(); + delete m_wifiController; +} + +bool QNetworkSettingsManagerPrivate::event(QEvent *event) +{ + switch ((int) event->type()) { + case WIFI_SCAN_RESULTS: + parseScanResults(call(QStringLiteral("SCAN_RESULTS"))); + return true; + case WIFI_CONNECTED: + handleConnected(); + return true; + case WIFI_DISCONNECTED: + handleDisconneced(); + return true; + case WIFI_AUTHENTICATING: + handleAuthenticating(static_cast(event)); + return true; + case WIFI_HANDSHAKE_FAILED: + updateNetworkState(QNetworkSettingsState::Failure); + return true; + } + return QObject::event(event); +} + +void QNetworkSettingsManagerPrivate::connectNetwork(const QString& ssid) +{ + if (m_backendState != QWifiController::Running) { + qCWarning(B2QT_WIFI) << "start wifi backend before calling connect()"; + return; + } + + call(QStringLiteral("DISABLE_NETWORK all")); + m_currentSSID = ssid; + emit m_agent->showUserCredentialsInput(); +} + +void QNetworkSettingsManagerPrivate::userInteractionReady(bool cancel) +{ + if (cancel) { + m_currentSSID = ""; + return; + } + bool networkKnown = false; + QString id; + const QStringList configuredNetworks = call(QStringLiteral("LIST_NETWORKS")).split('\n'); + for (int i = 1; i < configuredNetworks.length(); ++i) { + const QStringList networkFields = configuredNetworks.at(i).split('\t'); + const QString ssid = QWifiSupplicant::decodeSsid(networkFields.at(1)); + if (ssid == m_currentSSID) { + id = networkFields.at(0); + networkKnown = true; + break; + } + } + + if (!networkKnown) { + bool ok; + id = call(QStringLiteral("ADD_NETWORK")); + id.toInt(&ok); + if (!ok) { + updateLastError(QStringLiteral("failed to add network")); + return; + } + } + + bool ok = true; + QChar q = QLatin1Char('"'); + QString setNetworkCommand = QLatin1String("SET_NETWORK ") + id; + if (!networkKnown) { + ok = ok && checkedCall(setNetworkCommand + QLatin1String(" ssid ") + q + m_currentSSID + q); + } + + QString key_mgmt; + WpaSupplicantService *service = networkForSSID(m_currentSSID); + if (!service) { + return; + } + QString psk = m_agent->passPhrase(); + + // --------------------- configure network ------------------------------ + // ref: http://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf + // ref: https://www.freebsd.org/cgi/man.cgi?wpa_supplicant.conf + // ---------------------------------------------------------------------- + if (service->wirelessConfig()->supportsSecurity(QNetworkSettingsWireless::WPA) || + service->wirelessConfig()->supportsSecurity(QNetworkSettingsWireless::WPA2)) { + // ### TODO - password length has limits (see IEEE 802.11), we need to check + // for those limits here. Supplicant gives only a meaningless "fail" message. + ok = ok && checkedCall(setNetworkCommand + QLatin1String(" psk ") + q + psk + q); + key_mgmt = QLatin1String("WPA-PSK"); + } else if (service->wirelessConfig()->supportsSecurity(QNetworkSettingsWireless::WEP)) { + ok = ok && checkedCall(setNetworkCommand + QLatin1String(" wep_key0 ") + q + psk + q); + ok = ok && checkedCall(setNetworkCommand + QLatin1String(" auth_alg OPEN SHARED")); + key_mgmt = QLatin1String("NONE"); + } else if (service->wirelessConfig()->supportsSecurity(QNetworkSettingsWireless::None)) { + // open network + key_mgmt = QLatin1String("NONE"); + } + + if (service->wirelessConfig()->hidden()) + ok = ok && checkedCall(setNetworkCommand + QLatin1String(" scan_ssid 1")); + + ok = ok && checkedCall(setNetworkCommand + QLatin1String(" key_mgmt ") + key_mgmt); + if (!ok) { + if (!networkKnown) + call(QLatin1String("REMOVE_NETWORK ") + id); + updateLastError(QLatin1String("failed to set properties on network: ") + id); + return; + } + + call(QLatin1String("SELECT_NETWORK ") + id); + call(QStringLiteral("RECONNECT")); +} + +void QNetworkSettingsManagerPrivate::disconnectNetwork() +{ + call(QStringLiteral("DISCONNECT")); + m_wifiController->asyncCall(QWifiController::StopDhcp); +} + +void QNetworkSettingsManagerPrivate::handleBackendStateChanged(QWifiController::BackendState backendState) +{ + switch (backendState) { + case QWifiController::NotRunning: + updateNetworkState(QNetworkSettingsState::Disconnect); + break; + default: + break; + } + updateBackendState(backendState); +} + +void QNetworkSettingsManagerPrivate::handleDhcpRequestFinished(const QString &status) +{ + qCDebug(B2QT_WIFI) << "handleDhcpRequestFinished: " << status << " for " << m_currentSSID; + if (status == QLatin1String("success")) { + updateNetworkState(QNetworkSettingsState::Online); + call(QStringLiteral("SAVE_CONFIG")); + } else { + updateNetworkState(QNetworkSettingsState::Failure); + } +} + +void QNetworkSettingsManagerPrivate::setUserAgent(QNetworkSettingsUserAgent *agent) +{ + m_agent = agent; + connect(m_agent, &QNetworkSettingsUserAgent::ready, this, &QNetworkSettingsManagerPrivate::userInteractionReady); +} + +void QNetworkSettingsManagerPrivate::setCurrentSSID(const QString &ssid) +{ + qCDebug(B2QT_WIFI) << "current SSID: " << m_currentSSID << " -> " << ssid; + if (m_currentSSID == ssid) + return; + + m_currentSSID = ssid; +} + +void QNetworkSettingsManagerPrivate::handleAuthenticating(QWifiEvent *event) +{ + QString data = event->data().trimmed(); + QString ssid = data.mid(data.indexOf(QLatin1String("SSID")) + 6); + ssid = ssid.left(ssid.lastIndexOf(QLatin1Char('\''))); + + setCurrentSSID(QWifiSupplicant::decodeSsid(ssid)); + updateNetworkState(QNetworkSettingsState::Association); +} + +void QNetworkSettingsManagerPrivate::handleConnected() +{ + qCDebug(B2QT_WIFI) << "connected network: " << m_currentSSID; + updateNetworkState(QNetworkSettingsState::Ready); + m_wifiController->asyncCall(QWifiController::AcquireIPAddress); +} + +void QNetworkSettingsManagerPrivate::handleDisconneced() +{ + updateNetworkState(QNetworkSettingsState::Disconnect); +} + +void QNetworkSettingsManagerPrivate::updateNetworkState(QNetworkSettingsState::States networkState) +{ + //Update interface + if (!m_interfaceModel.getModel().isEmpty()) { + WpaSupplicantSettingsInterface* interface = qobject_cast(m_interfaceModel.getModel().first()); + if (interface && interface->state() != networkState) { + interface->setState(networkState); + } + } + + //Update service state + WpaSupplicantService *service = networkForSSID(m_currentSSID); + if (service) { + service->setState(networkState); + } +} + +void QNetworkSettingsManagerPrivate::updateBackendState(QWifiController::BackendState backendState) +{ + if (m_backendState == backendState) + return; + + m_backendState = backendState; + + if (m_backendState == QWifiController::Running && m_interfaceModel.getModel().isEmpty()) { + WpaSupplicantSettingsInterface *interface = new WpaSupplicantSettingsInterface(this); + m_interfaceModel.append(interface); + } else if (m_backendState == QWifiController::NotRunning && m_interfaceModel.getModel().size() > 0){ + m_interfaceModel.remove(0); + } +} + +void QNetworkSettingsManagerPrivate::updateWifiState() +{ + QProcess ps; + ps.start(QStringLiteral("ps")); + if (!ps.waitForStarted()) { + updateLastError(ps.program() + QLatin1String(": ") + ps.errorString()); + return; + } + + ps.waitForFinished(); + bool supplicantRunning = ps.readAll().contains("wpa_supplicant"); + if (supplicantRunning && m_wifiController->resetSupplicantSocket()) + m_backendState = QWifiController::Running; +} + +QString QNetworkSettingsManagerPrivate::call(const QString &command) +{ + if (m_backendState != QWifiController::Running) + return QString(); + + QByteArray reply; + bool success = m_wifiController->supplicant()->sendCommand(command, &reply); + if (!success) { + qCDebug(B2QT_WIFI) << "call to supplicant failed"; + return QString(); + } + + return QLatin1String(reply.trimmed()); +} + +bool QNetworkSettingsManagerPrivate::checkedCall(const QString &command) +{ + return call(command).toUpper() == QLatin1String("OK"); +} + +void QNetworkSettingsManagerPrivate::updateLastError(const QString &error) +{ + qCWarning(B2QT_WIFI) << error; + if (!m_interfaceModel.getModel().isEmpty()) { + WpaSupplicantSettingsInterface* interface = qobject_cast(m_interfaceModel.getModel().first()); + if (interface) { + interface->setState(QNetworkSettingsState::Failure); + } + } +} + +WpaSupplicantService* QNetworkSettingsManagerPrivate::networkForSSID(const QString& ssid) +{ + int pos = 0; + return networkForSSID(ssid, pos); +} + +WpaSupplicantService* QNetworkSettingsManagerPrivate::networkForSSID(const QString& ssid, int& pos) +{ + QList services = m_serviceModel.getModel(); + pos = 0; + foreach (QNetworkSettingsService *service, services) { + if (service->name() == ssid) { + return qobject_cast(service); + } + pos++; + } + pos = -1; + return NULL; +} + +WpaSupplicantService* QNetworkSettingsManagerPrivate::outOfRangeListContains(const QString& ssid) +{ + QList services = m_outOfRangeServiceModel.getModel(); + foreach (QNetworkSettingsService *service, services) { + if (service->name() == ssid) { + return qobject_cast(service); + } + } + return NULL; +} + +void QNetworkSettingsManagerPrivate::parseScanResults(const QString &results) +{ + QStringList lines = results.split('\n'); + QSet sensibleNetworks; + + for (int i = 1; i < lines.size(); ++i) { + QStringList info = lines.at(i).split('\t'); + if (info.size() < 5 || info.at(4).isEmpty() || info.at(0).isEmpty()) + continue; + int pos = 0; + + QString ssid = QWifiSupplicant::decodeSsid(info.at(4)); + if (ssid.isEmpty()) + continue; + + sensibleNetworks.insert(ssid); + WpaSupplicantService *knownNetwork = networkForSSID(ssid, pos); + + if (!knownNetwork) { + knownNetwork = outOfRangeListContains(ssid); + m_outOfRangeServiceModel.getModel().removeOne(knownNetwork); + } + + int signalStrength = info.at(2).trimmed().toInt(); + if (signalStrength < 0) { + // signal is reported in dBm, rough conversion: best = -40, worst = -100 + int val = qAbs(qMax(-100, qMin(signalStrength, -40)) + 40); // clamp and normalize to 0 + signalStrength = 100 - (int) ((100.0 * (double) val) / 60.0); + } else if (signalStrength > 100) { + qCWarning(B2QT_WIFI) << "unexpected value for a signal level: " << signalStrength; + } + + if (!knownNetwork) { + WpaSupplicantService *network = new WpaSupplicantService(this); + network->setId(info.at(0)); + network->setFlags(info.at(3)); + network->wirelessConfig()->setSignalStrength(signalStrength); + network->setName(ssid); + m_serviceModel.append(network); + } else { + if (knownNetwork->wirelessConfig()->outOfRange()) { + // known network has come back into a range + knownNetwork->wirelessConfig()->setOutOfRange(false); + m_serviceModel.append(knownNetwork); + pos = m_serviceModel.getModel().size() - 1; + } + // ssids are the same, compare bssids.. + if (knownNetwork->id() == info.at(0)) { + // same access point, simply update the signal strength + knownNetwork->wirelessConfig()->setSignalStrength(signalStrength); + knownNetwork->wirelessConfig()->setOutOfRange(false); + m_serviceModel.updated(pos); + } else if (knownNetwork->wirelessConfig()->signalStrength() < signalStrength) { + // replace with a stronger access point within the same network + knownNetwork->wirelessConfig()->setOutOfRange(false); + knownNetwork->setId(info.at(0)); + knownNetwork->setFlags(info.at(3)); + knownNetwork->wirelessConfig()->setSignalStrength(signalStrength); + knownNetwork->setName(ssid); + m_serviceModel.updated(pos); + } + } + } + // remove out-of-range networks from the data model + QList networks; + for (int i = 0; i < networks.size();) { + if (!sensibleNetworks.contains(networks.at(i)->name())) { + WpaSupplicantService *n = qobject_cast(networks.at(i)); + m_serviceModel.remove(i); + if (n) { + n->wirelessConfig()->setOutOfRange(true); + m_outOfRangeServiceModel.append(n); + } + } else { + ++i; + } + } +} -- cgit v1.2.3