blob: 777b4eea59688da28df89004f2a90e5ad89ab96c [file] [log] [blame]
/****************************************************************************
**
** 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, &parameters, 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