| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the plugins of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** 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 https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qnativewifiengine.h" |
| #include "platformdefs.h" |
| #include "../qnetworksession_impl.h" |
| |
| #include <QtNetwork/private/qnetworkconfiguration_p.h> |
| |
| #include <QtCore/qstringlist.h> |
| #include <QtCore/qcoreapplication.h> |
| #include <QtCore/qoperatingsystemversion.h> |
| |
| #include <QtCore/qdebug.h> |
| |
| #ifndef QT_NO_BEARERMANAGEMENT |
| |
| QT_BEGIN_NAMESPACE |
| |
| WlanOpenHandleProto local_WlanOpenHandle = 0; |
| WlanRegisterNotificationProto local_WlanRegisterNotification = 0; |
| WlanEnumInterfacesProto local_WlanEnumInterfaces = 0; |
| WlanGetAvailableNetworkListProto local_WlanGetAvailableNetworkList = 0; |
| WlanQueryInterfaceProto local_WlanQueryInterface = 0; |
| WlanConnectProto local_WlanConnect = 0; |
| WlanDisconnectProto local_WlanDisconnect = 0; |
| WlanScanProto local_WlanScan = 0; |
| WlanFreeMemoryProto local_WlanFreeMemory = 0; |
| WlanCloseHandleProto local_WlanCloseHandle = 0; |
| |
| void qNotificationCallback(WLAN_NOTIFICATION_DATA *data, QNativeWifiEngine *d) |
| { |
| Q_UNUSED(d); |
| |
| if (data->NotificationSource == WLAN_NOTIFICATION_SOURCE_ACM) { |
| switch (data->NotificationCode) { |
| case wlan_notification_acm_connection_complete: |
| case wlan_notification_acm_disconnected: |
| case wlan_notification_acm_scan_complete: |
| case wlan_notification_acm_scan_fail: |
| QMetaObject::invokeMethod(d, "scanComplete", Qt::QueuedConnection); |
| break; |
| default: |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug() << "wlan acm notification" << (int)data->NotificationCode; |
| #endif |
| break; |
| } |
| } else { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug() << "wlan notification source" << (int)data->NotificationSource << "code" << (int)data->NotificationCode; |
| #endif |
| } |
| } |
| |
| QNativeWifiEngine::QNativeWifiEngine(QObject *parent) |
| : QBearerEngineImpl(parent), handle(INVALID_HANDLE_VALUE) |
| { |
| connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(closeHandle())); |
| } |
| |
| QNativeWifiEngine::~QNativeWifiEngine() |
| { |
| closeHandle(); |
| } |
| |
| void QNativeWifiEngine::scanComplete() |
| { |
| QMutexLocker locker(&mutex); |
| |
| if (!available()) { |
| locker.unlock(); |
| emit updateCompleted(); |
| return; |
| } |
| |
| // enumerate interfaces |
| WLAN_INTERFACE_INFO_LIST *interfaceList; |
| DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| |
| locker.unlock(); |
| emit updateCompleted(); |
| |
| return; |
| } |
| |
| QStringList previous = accessPointConfigurations.keys(); |
| |
| for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) { |
| const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i]; |
| |
| WLAN_AVAILABLE_NETWORK_LIST *networkList; |
| result = local_WlanGetAvailableNetworkList(handle, &interface.InterfaceGuid, |
| 3, 0, &networkList); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanGetAvailableNetworkList failed with error %ld\n", |
| __FUNCTION__, result); |
| #endif |
| continue; |
| } |
| |
| QStringList seenNetworks; |
| |
| for (unsigned int j = 0; j < networkList->dwNumberOfItems; ++j) { |
| WLAN_AVAILABLE_NETWORK &network = networkList->Network[j]; |
| |
| QString networkName; |
| |
| if (network.strProfileName[0] != 0) { |
| networkName = QString::fromWCharArray(network.strProfileName); |
| } else { |
| networkName = QByteArray(reinterpret_cast<char *>(network.dot11Ssid.ucSSID), |
| network.dot11Ssid.uSSIDLength); |
| } |
| |
| const QString id = QString::number(qHash(QLatin1String("WLAN:") + networkName)); |
| |
| previous.removeAll(id); |
| |
| QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined; |
| |
| if (!(network.dwFlags & WLAN_AVAILABLE_NETWORK_HAS_PROFILE)) |
| state = QNetworkConfiguration::Undefined; |
| |
| if (network.strProfileName[0] != 0) { |
| if (network.bNetworkConnectable) { |
| if (network.dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED) |
| state = QNetworkConfiguration::Active; |
| else |
| state = QNetworkConfiguration::Discovered; |
| } else { |
| state = QNetworkConfiguration::Defined; |
| } |
| } |
| |
| if (seenNetworks.contains(networkName)) |
| continue; |
| else |
| seenNetworks.append(networkName); |
| |
| if (accessPointConfigurations.contains(id)) { |
| QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); |
| |
| bool changed = false; |
| |
| ptr->mutex.lock(); |
| |
| if (!ptr->isValid) { |
| ptr->isValid = true; |
| changed = true; |
| } |
| |
| if (ptr->name != networkName) { |
| ptr->name = networkName; |
| changed = true; |
| } |
| |
| if (ptr->state != state) { |
| ptr->state = state; |
| changed = true; |
| } |
| |
| ptr->mutex.unlock(); |
| |
| if (changed) { |
| locker.unlock(); |
| emit configurationChanged(ptr); |
| locker.relock(); |
| } |
| } else { |
| QNetworkConfigurationPrivatePointer ptr(new QNetworkConfigurationPrivate); |
| |
| ptr->name = networkName; |
| ptr->isValid = true; |
| ptr->id = id; |
| ptr->state = state; |
| ptr->type = QNetworkConfiguration::InternetAccessPoint; |
| ptr->bearerType = QNetworkConfiguration::BearerWLAN; |
| |
| accessPointConfigurations.insert(id, ptr); |
| |
| locker.unlock(); |
| emit configurationAdded(ptr); |
| locker.relock(); |
| } |
| } |
| |
| local_WlanFreeMemory(networkList); |
| } |
| |
| local_WlanFreeMemory(interfaceList); |
| |
| while (!previous.isEmpty()) { |
| QNetworkConfigurationPrivatePointer ptr = |
| accessPointConfigurations.take(previous.takeFirst()); |
| |
| locker.unlock(); |
| emit configurationRemoved(ptr); |
| locker.relock(); |
| } |
| |
| locker.unlock(); |
| emit updateCompleted(); |
| } |
| |
| QString QNativeWifiEngine::getInterfaceFromId(const QString &id) |
| { |
| QMutexLocker locker(&mutex); |
| |
| if (!available()) |
| return QString(); |
| |
| // enumerate interfaces |
| WLAN_INTERFACE_INFO_LIST *interfaceList; |
| DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| return QString(); |
| } |
| |
| for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) { |
| const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i]; |
| |
| DWORD dataSize; |
| WLAN_CONNECTION_ATTRIBUTES *connectionAttributes; |
| result = local_WlanQueryInterface(handle, &interface.InterfaceGuid, |
| wlan_intf_opcode_current_connection, 0, &dataSize, |
| reinterpret_cast<PVOID *>(&connectionAttributes), 0); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| if (result != ERROR_INVALID_STATE) |
| qDebug("%s: WlanQueryInterface failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| |
| continue; |
| } |
| |
| if (qHash(QLatin1String("WLAN:") + |
| QString::fromWCharArray(connectionAttributes->strProfileName)) == id.toUInt()) { |
| QString guid("{%1-%2-%3-%4%5-%6%7%8%9%10%11}"); |
| |
| guid = guid.arg(interface.InterfaceGuid.Data1, 8, 16, QChar('0')); |
| guid = guid.arg(interface.InterfaceGuid.Data2, 4, 16, QChar('0')); |
| guid = guid.arg(interface.InterfaceGuid.Data3, 4, 16, QChar('0')); |
| for (int i = 0; i < 8; ++i) |
| guid = guid.arg(interface.InterfaceGuid.Data4[i], 2, 16, QChar('0')); |
| |
| local_WlanFreeMemory(connectionAttributes); |
| local_WlanFreeMemory(interfaceList); |
| |
| return guid.toUpper(); |
| } |
| |
| local_WlanFreeMemory(connectionAttributes); |
| } |
| |
| local_WlanFreeMemory(interfaceList); |
| |
| return QString(); |
| } |
| |
| bool QNativeWifiEngine::hasIdentifier(const QString &id) |
| { |
| QMutexLocker locker(&mutex); |
| |
| if (!available()) |
| return false; |
| |
| // enumerate interfaces |
| WLAN_INTERFACE_INFO_LIST *interfaceList; |
| DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| return false; |
| } |
| |
| for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) { |
| const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i]; |
| |
| WLAN_AVAILABLE_NETWORK_LIST *networkList; |
| result = local_WlanGetAvailableNetworkList(handle, &interface.InterfaceGuid, |
| 3, 0, &networkList); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanGetAvailableNetworkList failed with error %ld\n", |
| __FUNCTION__, result); |
| #endif |
| continue; |
| } |
| |
| for (unsigned int j = 0; j < networkList->dwNumberOfItems; ++j) { |
| WLAN_AVAILABLE_NETWORK &network = networkList->Network[j]; |
| |
| QString networkName; |
| |
| if (network.strProfileName[0] != 0) { |
| networkName = QString::fromWCharArray(network.strProfileName); |
| } else { |
| networkName = QByteArray(reinterpret_cast<char *>(network.dot11Ssid.ucSSID), |
| network.dot11Ssid.uSSIDLength); |
| } |
| |
| if (qHash(QLatin1String("WLAN:") + networkName) == id.toUInt()) { |
| local_WlanFreeMemory(networkList); |
| local_WlanFreeMemory(interfaceList); |
| return true; |
| } |
| } |
| |
| local_WlanFreeMemory(networkList); |
| } |
| |
| local_WlanFreeMemory(interfaceList); |
| |
| return false; |
| } |
| |
| void QNativeWifiEngine::connectToId(const QString &id) |
| { |
| QMutexLocker locker(&mutex); |
| |
| if (!available()) { |
| locker.unlock(); |
| emit connectionError(id, InterfaceLookupError); |
| return; |
| } |
| |
| WLAN_INTERFACE_INFO_LIST *interfaceList; |
| DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| locker.unlock(); |
| emit connectionError(id, InterfaceLookupError); |
| return; |
| } |
| |
| QString profile; |
| |
| for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) { |
| const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i]; |
| |
| WLAN_AVAILABLE_NETWORK_LIST *networkList; |
| result = local_WlanGetAvailableNetworkList(handle, &interface.InterfaceGuid, |
| 3, 0, &networkList); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanGetAvailableNetworkList failed with error %ld\n", |
| __FUNCTION__, result); |
| #endif |
| continue; |
| } |
| |
| for (unsigned int j = 0; j < networkList->dwNumberOfItems; ++j) { |
| WLAN_AVAILABLE_NETWORK &network = networkList->Network[j]; |
| |
| profile = QString::fromWCharArray(network.strProfileName); |
| |
| if (qHash(QLatin1String("WLAN:") + profile) == id.toUInt()) |
| break; |
| else |
| profile.clear(); |
| } |
| |
| local_WlanFreeMemory(networkList); |
| |
| if (!profile.isEmpty()) { |
| WLAN_CONNECTION_PARAMETERS parameters; |
| parameters.wlanConnectionMode = wlan_connection_mode_profile; |
| parameters.strProfile = reinterpret_cast<LPCWSTR>(profile.utf16()); |
| parameters.pDot11Ssid = 0; |
| parameters.pDesiredBssidList = 0; |
| parameters.dot11BssType = dot11_BSS_type_any; |
| parameters.dwFlags = 0; |
| |
| DWORD result = local_WlanConnect(handle, &interface.InterfaceGuid, ¶meters, 0); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanConnect failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| locker.unlock(); |
| emit connectionError(id, ConnectError); |
| locker.relock(); |
| break; |
| } |
| |
| break; |
| } |
| } |
| |
| local_WlanFreeMemory(interfaceList); |
| |
| if (profile.isEmpty()) { |
| locker.unlock(); |
| emit connectionError(id, InterfaceLookupError); |
| } |
| } |
| |
| void QNativeWifiEngine::disconnectFromId(const QString &id) |
| { |
| QMutexLocker locker(&mutex); |
| |
| if (!available()) { |
| locker.unlock(); |
| emit connectionError(id, InterfaceLookupError); |
| return; |
| } |
| |
| QString interface = getInterfaceFromId(id); |
| |
| if (interface.isEmpty()) { |
| locker.unlock(); |
| emit connectionError(id, InterfaceLookupError); |
| return; |
| } |
| |
| const QVector<QStringRef> split = interface.midRef(1, interface.length() - 2).split(QLatin1Char('-')); |
| |
| GUID guid; |
| guid.Data1 = split.at(0).toUInt(0, 16); |
| guid.Data2 = split.at(1).toUShort(0, 16); |
| guid.Data3 = split.at(2).toUShort(0, 16); |
| guid.Data4[0] = split.at(3).left(2).toUShort(0, 16); |
| guid.Data4[1] = split.at(3).right(2).toUShort(0, 16); |
| for (int i = 0; i < 6; ++i) |
| guid.Data4[i + 2] = split.at(4).mid(i*2, 2).toUShort(0, 16); |
| |
| DWORD result = local_WlanDisconnect(handle, &guid, 0); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanDisconnect failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| locker.unlock(); |
| emit connectionError(id, DisconnectionError); |
| return; |
| } |
| } |
| |
| void QNativeWifiEngine::initialize() |
| { |
| scanComplete(); |
| } |
| |
| void QNativeWifiEngine::requestUpdate() |
| { |
| QMutexLocker locker(&mutex); |
| |
| if (!available()) { |
| locker.unlock(); |
| emit updateCompleted(); |
| return; |
| } |
| |
| // enumerate interfaces |
| WLAN_INTERFACE_INFO_LIST *interfaceList; |
| DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| |
| locker.unlock(); |
| emit updateCompleted(); |
| |
| return; |
| } |
| |
| bool requested = false; |
| for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) { |
| result = local_WlanScan(handle, &interfaceList->InterfaceInfo[i].InterfaceGuid, 0, 0, 0); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| qDebug("%s: WlanScan failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| } else { |
| requested = true; |
| } |
| } |
| |
| local_WlanFreeMemory(interfaceList); |
| |
| if (!requested) { |
| locker.unlock(); |
| emit updateCompleted(); |
| } |
| } |
| |
| QNetworkSession::State QNativeWifiEngine::sessionStateForId(const QString &id) |
| { |
| QMutexLocker locker(&mutex); |
| |
| QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); |
| |
| if (!ptr) |
| return QNetworkSession::Invalid; |
| |
| if (!ptr->isValid) { |
| return QNetworkSession::Invalid; |
| } else if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) { |
| return QNetworkSession::Connected; |
| } else if ((ptr->state & QNetworkConfiguration::Discovered) == |
| QNetworkConfiguration::Discovered) { |
| return QNetworkSession::Disconnected; |
| } else if ((ptr->state & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined) { |
| return QNetworkSession::NotAvailable; |
| } else if ((ptr->state & QNetworkConfiguration::Undefined) == |
| QNetworkConfiguration::Undefined) { |
| return QNetworkSession::NotAvailable; |
| } |
| |
| return QNetworkSession::Invalid; |
| } |
| |
| QNetworkConfigurationManager::Capabilities QNativeWifiEngine::capabilities() const |
| { |
| return QNetworkConfigurationManager::ForcedRoaming | |
| QNetworkConfigurationManager::CanStartAndStopInterfaces; |
| } |
| |
| QNetworkSessionPrivate *QNativeWifiEngine::createSessionBackend() |
| { |
| return new QNetworkSessionPrivateImpl; |
| } |
| |
| QNetworkConfigurationPrivatePointer QNativeWifiEngine::defaultConfiguration() |
| { |
| return QNetworkConfigurationPrivatePointer(); |
| } |
| |
| bool QNativeWifiEngine::available() |
| { |
| if (handle != INVALID_HANDLE_VALUE) |
| return true; |
| |
| DWORD clientVersion; |
| |
| DWORD result = local_WlanOpenHandle(1, 0, &clientVersion, &handle); |
| if (result != ERROR_SUCCESS) { |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| if (result != ERROR_SERVICE_NOT_ACTIVE) |
| qDebug("%s: WlanOpenHandle failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| |
| return false; |
| } |
| |
| result = local_WlanRegisterNotification(handle, WLAN_NOTIFICATION_SOURCE_ALL, true, |
| WLAN_NOTIFICATION_CALLBACK(qNotificationCallback), |
| this, 0, 0); |
| #ifdef BEARER_MANAGEMENT_DEBUG |
| if (result != ERROR_SUCCESS) |
| qDebug("%s: WlanRegisterNotification failed with error %ld\n", __FUNCTION__, result); |
| #endif |
| |
| return handle != INVALID_HANDLE_VALUE; |
| } |
| |
| void QNativeWifiEngine::closeHandle() |
| { |
| if (handle != INVALID_HANDLE_VALUE) { |
| local_WlanCloseHandle(handle, 0); |
| handle = INVALID_HANDLE_VALUE; |
| } |
| } |
| |
| bool QNativeWifiEngine::requiresPolling() const |
| { |
| // On Windows XP SP2 and SP3 only connection and disconnection notifications are available. |
| // We need to poll for changes in available wireless networks. |
| return QOperatingSystemVersion::current() |
| <= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 5, 2); |
| } |
| |
| QT_END_NAMESPACE |
| |
| #endif // QT_NO_BEARERMANAGEMENT |