1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qnativewifiengine.h"
43 #include "platformdefs.h"
44 #include "../qnetworksession_impl.h"
45
46 #include <QtNetwork/private/qnetworkconfiguration_p.h>
47
48 #include <QtCore/qstringlist.h>
49 #include <QtCore/qcoreapplication.h>
50
51 #include <QtCore/qdebug.h>
52
53 #ifndef QT_NO_BEARERMANAGEMENT
54
55 QT_BEGIN_NAMESPACE
56
57 WlanOpenHandleProto local_WlanOpenHandle = 0;
58 WlanRegisterNotificationProto local_WlanRegisterNotification = 0;
59 WlanEnumInterfacesProto local_WlanEnumInterfaces = 0;
60 WlanGetAvailableNetworkListProto local_WlanGetAvailableNetworkList = 0;
61 WlanQueryInterfaceProto local_WlanQueryInterface = 0;
62 WlanConnectProto local_WlanConnect = 0;
63 WlanDisconnectProto local_WlanDisconnect = 0;
64 WlanScanProto local_WlanScan = 0;
65 WlanFreeMemoryProto local_WlanFreeMemory = 0;
66 WlanCloseHandleProto local_WlanCloseHandle = 0;
67
qNotificationCallback(WLAN_NOTIFICATION_DATA * data,QNativeWifiEngine * d)68 void qNotificationCallback(WLAN_NOTIFICATION_DATA *data, QNativeWifiEngine *d)
69 {
70 Q_UNUSED(d);
71
72 if (data->NotificationSource == WLAN_NOTIFICATION_SOURCE_ACM) {
73 switch (data->NotificationCode) {
74 case wlan_notification_acm_connection_complete:
75 case wlan_notification_acm_disconnected:
76 case wlan_notification_acm_scan_complete:
77 case wlan_notification_acm_scan_fail:
78 QMetaObject::invokeMethod(d, "scanComplete", Qt::QueuedConnection);
79 break;
80 default:
81 #ifdef BEARER_MANAGEMENT_DEBUG
82 qDebug() << "wlan acm notification" << (int)data->NotificationCode;
83 #endif
84 break;
85 }
86 } else {
87 #ifdef BEARER_MANAGEMENT_DEBUG
88 qDebug() << "wlan notification source" << (int)data->NotificationSource << "code" << (int)data->NotificationCode;
89 #endif
90 }
91 }
92
QNativeWifiEngine(QObject * parent)93 QNativeWifiEngine::QNativeWifiEngine(QObject *parent)
94 : QBearerEngineImpl(parent), handle(INVALID_HANDLE_VALUE)
95 {
96 connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(closeHandle()));
97 }
98
~QNativeWifiEngine()99 QNativeWifiEngine::~QNativeWifiEngine()
100 {
101 closeHandle();
102 }
103
scanComplete()104 void QNativeWifiEngine::scanComplete()
105 {
106 QMutexLocker locker(&mutex);
107
108 if (!available()) {
109 locker.unlock();
110 emit updateCompleted();
111 return;
112 }
113
114 // enumerate interfaces
115 WLAN_INTERFACE_INFO_LIST *interfaceList;
116 DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList);
117 if (result != ERROR_SUCCESS) {
118 #ifdef BEARER_MANAGEMENT_DEBUG
119 qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result);
120 #endif
121
122 locker.unlock();
123 emit updateCompleted();
124
125 return;
126 }
127
128 QStringList previous = accessPointConfigurations.keys();
129
130 for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) {
131 const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i];
132
133 WLAN_AVAILABLE_NETWORK_LIST *networkList;
134 result = local_WlanGetAvailableNetworkList(handle, &interface.InterfaceGuid,
135 3, 0, &networkList);
136 if (result != ERROR_SUCCESS) {
137 #ifdef BEARER_MANAGEMENT_DEBUG
138 qDebug("%s: WlanGetAvailableNetworkList failed with error %ld\n",
139 __FUNCTION__, result);
140 #endif
141 continue;
142 }
143
144 QStringList seenNetworks;
145
146 for (unsigned int j = 0; j < networkList->dwNumberOfItems; ++j) {
147 WLAN_AVAILABLE_NETWORK &network = networkList->Network[j];
148
149 QString networkName;
150
151 if (network.strProfileName[0] != 0) {
152 networkName = QString::fromWCharArray(network.strProfileName);
153 } else {
154 networkName = QByteArray(reinterpret_cast<char *>(network.dot11Ssid.ucSSID),
155 network.dot11Ssid.uSSIDLength);
156 }
157
158 const QString id = QString::number(qHash(QLatin1String("WLAN:") + networkName));
159
160 previous.removeAll(id);
161
162 QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined;
163
164 if (!(network.dwFlags & WLAN_AVAILABLE_NETWORK_HAS_PROFILE))
165 state = QNetworkConfiguration::Undefined;
166
167 if (network.strProfileName[0] != 0) {
168 if (network.bNetworkConnectable) {
169 if (network.dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED)
170 state = QNetworkConfiguration::Active;
171 else
172 state = QNetworkConfiguration::Discovered;
173 } else {
174 state = QNetworkConfiguration::Defined;
175 }
176 }
177
178 if (seenNetworks.contains(networkName))
179 continue;
180 else
181 seenNetworks.append(networkName);
182
183 if (accessPointConfigurations.contains(id)) {
184 QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id);
185
186 bool changed = false;
187
188 ptr->mutex.lock();
189
190 if (!ptr->isValid) {
191 ptr->isValid = true;
192 changed = true;
193 }
194
195 if (ptr->name != networkName) {
196 ptr->name = networkName;
197 changed = true;
198 }
199
200 if (ptr->state != state) {
201 ptr->state = state;
202 changed = true;
203 }
204
205 ptr->mutex.unlock();
206
207 if (changed) {
208 locker.unlock();
209 emit configurationChanged(ptr);
210 locker.relock();
211 }
212 } else {
213 QNetworkConfigurationPrivatePointer ptr(new QNetworkConfigurationPrivate);
214
215 ptr->name = networkName;
216 ptr->isValid = true;
217 ptr->id = id;
218 ptr->state = state;
219 ptr->type = QNetworkConfiguration::InternetAccessPoint;
220 ptr->bearerType = QNetworkConfiguration::BearerWLAN;
221
222 accessPointConfigurations.insert(id, ptr);
223
224 locker.unlock();
225 emit configurationAdded(ptr);
226 locker.relock();
227 }
228 }
229
230 local_WlanFreeMemory(networkList);
231 }
232
233 local_WlanFreeMemory(interfaceList);
234
235 while (!previous.isEmpty()) {
236 QNetworkConfigurationPrivatePointer ptr =
237 accessPointConfigurations.take(previous.takeFirst());
238
239 locker.unlock();
240 emit configurationRemoved(ptr);
241 locker.relock();
242 }
243
244 locker.unlock();
245 emit updateCompleted();
246 }
247
getInterfaceFromId(const QString & id)248 QString QNativeWifiEngine::getInterfaceFromId(const QString &id)
249 {
250 QMutexLocker locker(&mutex);
251
252 if (!available())
253 return QString();
254
255 // enumerate interfaces
256 WLAN_INTERFACE_INFO_LIST *interfaceList;
257 DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList);
258 if (result != ERROR_SUCCESS) {
259 #ifdef BEARER_MANAGEMENT_DEBUG
260 qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result);
261 #endif
262 return QString();
263 }
264
265 for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) {
266 const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i];
267
268 DWORD dataSize;
269 WLAN_CONNECTION_ATTRIBUTES *connectionAttributes;
270 result = local_WlanQueryInterface(handle, &interface.InterfaceGuid,
271 wlan_intf_opcode_current_connection, 0, &dataSize,
272 reinterpret_cast<PVOID *>(&connectionAttributes), 0);
273 if (result != ERROR_SUCCESS) {
274 #ifdef BEARER_MANAGEMENT_DEBUG
275 if (result != ERROR_INVALID_STATE)
276 qDebug("%s: WlanQueryInterface failed with error %ld\n", __FUNCTION__, result);
277 #endif
278
279 continue;
280 }
281
282 if (qHash(QLatin1String("WLAN:") +
283 QString::fromWCharArray(connectionAttributes->strProfileName)) == id.toUInt()) {
284 QString guid("{%1-%2-%3-%4%5-%6%7%8%9%10%11}");
285
286 guid = guid.arg(interface.InterfaceGuid.Data1, 8, 16, QChar('0'));
287 guid = guid.arg(interface.InterfaceGuid.Data2, 4, 16, QChar('0'));
288 guid = guid.arg(interface.InterfaceGuid.Data3, 4, 16, QChar('0'));
289 for (int i = 0; i < 8; ++i)
290 guid = guid.arg(interface.InterfaceGuid.Data4[i], 2, 16, QChar('0'));
291
292 local_WlanFreeMemory(connectionAttributes);
293 local_WlanFreeMemory(interfaceList);
294
295 return guid.toUpper();
296 }
297
298 local_WlanFreeMemory(connectionAttributes);
299 }
300
301 local_WlanFreeMemory(interfaceList);
302
303 return QString();
304 }
305
hasIdentifier(const QString & id)306 bool QNativeWifiEngine::hasIdentifier(const QString &id)
307 {
308 QMutexLocker locker(&mutex);
309
310 if (!available())
311 return false;
312
313 // enumerate interfaces
314 WLAN_INTERFACE_INFO_LIST *interfaceList;
315 DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList);
316 if (result != ERROR_SUCCESS) {
317 #ifdef BEARER_MANAGEMENT_DEBUG
318 qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result);
319 #endif
320 return false;
321 }
322
323 for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) {
324 const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i];
325
326 WLAN_AVAILABLE_NETWORK_LIST *networkList;
327 result = local_WlanGetAvailableNetworkList(handle, &interface.InterfaceGuid,
328 3, 0, &networkList);
329 if (result != ERROR_SUCCESS) {
330 #ifdef BEARER_MANAGEMENT_DEBUG
331 qDebug("%s: WlanGetAvailableNetworkList failed with error %ld\n",
332 __FUNCTION__, result);
333 #endif
334 continue;
335 }
336
337 for (unsigned int j = 0; j < networkList->dwNumberOfItems; ++j) {
338 WLAN_AVAILABLE_NETWORK &network = networkList->Network[j];
339
340 QString networkName;
341
342 if (network.strProfileName[0] != 0) {
343 networkName = QString::fromWCharArray(network.strProfileName);
344 } else {
345 networkName = QByteArray(reinterpret_cast<char *>(network.dot11Ssid.ucSSID),
346 network.dot11Ssid.uSSIDLength);
347 }
348
349 if (qHash(QLatin1String("WLAN:") + networkName) == id.toUInt()) {
350 local_WlanFreeMemory(networkList);
351 local_WlanFreeMemory(interfaceList);
352 return true;
353 }
354 }
355
356 local_WlanFreeMemory(networkList);
357 }
358
359 local_WlanFreeMemory(interfaceList);
360
361 return false;
362 }
363
connectToId(const QString & id)364 void QNativeWifiEngine::connectToId(const QString &id)
365 {
366 QMutexLocker locker(&mutex);
367
368 if (!available()) {
369 locker.unlock();
370 emit connectionError(id, InterfaceLookupError);
371 return;
372 }
373
374 WLAN_INTERFACE_INFO_LIST *interfaceList;
375 DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList);
376 if (result != ERROR_SUCCESS) {
377 #ifdef BEARER_MANAGEMENT_DEBUG
378 qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result);
379 #endif
380 locker.unlock();
381 emit connectionError(id, InterfaceLookupError);
382 return;
383 }
384
385 QString profile;
386
387 for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) {
388 const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i];
389
390 WLAN_AVAILABLE_NETWORK_LIST *networkList;
391 result = local_WlanGetAvailableNetworkList(handle, &interface.InterfaceGuid,
392 3, 0, &networkList);
393 if (result != ERROR_SUCCESS) {
394 #ifdef BEARER_MANAGEMENT_DEBUG
395 qDebug("%s: WlanGetAvailableNetworkList failed with error %ld\n",
396 __FUNCTION__, result);
397 #endif
398 continue;
399 }
400
401 for (unsigned int j = 0; j < networkList->dwNumberOfItems; ++j) {
402 WLAN_AVAILABLE_NETWORK &network = networkList->Network[j];
403
404 profile = QString::fromWCharArray(network.strProfileName);
405
406 if (qHash(QLatin1String("WLAN:") + profile) == id.toUInt())
407 break;
408 else
409 profile.clear();
410 }
411
412 local_WlanFreeMemory(networkList);
413
414 if (!profile.isEmpty()) {
415 WLAN_CONNECTION_PARAMETERS parameters;
416 parameters.wlanConnectionMode = wlan_connection_mode_profile;
417 parameters.strProfile = reinterpret_cast<LPCWSTR>(profile.utf16());
418 parameters.pDot11Ssid = 0;
419 parameters.pDesiredBssidList = 0;
420 parameters.dot11BssType = dot11_BSS_type_any;
421 parameters.dwFlags = 0;
422
423 DWORD result = local_WlanConnect(handle, &interface.InterfaceGuid, ¶meters, 0);
424 if (result != ERROR_SUCCESS) {
425 #ifdef BEARER_MANAGEMENT_DEBUG
426 qDebug("%s: WlanConnect failed with error %ld\n", __FUNCTION__, result);
427 #endif
428 locker.unlock();
429 emit connectionError(id, ConnectError);
430 locker.relock();
431 break;
432 }
433
434 break;
435 }
436 }
437
438 local_WlanFreeMemory(interfaceList);
439
440 if (profile.isEmpty()) {
441 locker.unlock();
442 emit connectionError(id, InterfaceLookupError);
443 }
444 }
445
disconnectFromId(const QString & id)446 void QNativeWifiEngine::disconnectFromId(const QString &id)
447 {
448 QMutexLocker locker(&mutex);
449
450 if (!available()) {
451 locker.unlock();
452 emit connectionError(id, InterfaceLookupError);
453 return;
454 }
455
456 QString interface = getInterfaceFromId(id);
457
458 if (interface.isEmpty()) {
459 locker.unlock();
460 emit connectionError(id, InterfaceLookupError);
461 return;
462 }
463
464 QStringList split = interface.mid(1, interface.length() - 2).split('-');
465
466 GUID guid;
467 guid.Data1 = split.at(0).toUInt(0, 16);
468 guid.Data2 = split.at(1).toUShort(0, 16);
469 guid.Data3 = split.at(2).toUShort(0, 16);
470 guid.Data4[0] = split.at(3).left(2).toUShort(0, 16);
471 guid.Data4[1] = split.at(3).right(2).toUShort(0, 16);
472 for (int i = 0; i < 6; ++i)
473 guid.Data4[i + 2] = split.at(4).mid(i*2, 2).toUShort(0, 16);
474
475 DWORD result = local_WlanDisconnect(handle, &guid, 0);
476 if (result != ERROR_SUCCESS) {
477 #ifdef BEARER_MANAGEMENT_DEBUG
478 qDebug("%s: WlanDisconnect failed with error %ld\n", __FUNCTION__, result);
479 #endif
480 locker.unlock();
481 emit connectionError(id, DisconnectionError);
482 return;
483 }
484 }
485
initialize()486 void QNativeWifiEngine::initialize()
487 {
488 scanComplete();
489 }
490
requestUpdate()491 void QNativeWifiEngine::requestUpdate()
492 {
493 QMutexLocker locker(&mutex);
494
495 if (!available()) {
496 locker.unlock();
497 emit updateCompleted();
498 return;
499 }
500
501 // enumerate interfaces
502 WLAN_INTERFACE_INFO_LIST *interfaceList;
503 DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList);
504 if (result != ERROR_SUCCESS) {
505 #ifdef BEARER_MANAGEMENT_DEBUG
506 qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result);
507 #endif
508
509 locker.unlock();
510 emit updateCompleted();
511
512 return;
513 }
514
515 bool requested = false;
516 for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) {
517 result = local_WlanScan(handle, &interfaceList->InterfaceInfo[i].InterfaceGuid, 0, 0, 0);
518 if (result != ERROR_SUCCESS) {
519 #ifdef BEARER_MANAGEMENT_DEBUG
520 qDebug("%s: WlanScan failed with error %ld\n", __FUNCTION__, result);
521 #endif
522 } else {
523 requested = true;
524 }
525 }
526
527 local_WlanFreeMemory(interfaceList);
528
529 if (!requested) {
530 locker.unlock();
531 emit updateCompleted();
532 }
533 }
534
sessionStateForId(const QString & id)535 QNetworkSession::State QNativeWifiEngine::sessionStateForId(const QString &id)
536 {
537 QMutexLocker locker(&mutex);
538
539 QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id);
540
541 if (!ptr)
542 return QNetworkSession::Invalid;
543
544 if (!ptr->isValid) {
545 return QNetworkSession::Invalid;
546 } else if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) {
547 return QNetworkSession::Connected;
548 } else if ((ptr->state & QNetworkConfiguration::Discovered) ==
549 QNetworkConfiguration::Discovered) {
550 return QNetworkSession::Disconnected;
551 } else if ((ptr->state & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined) {
552 return QNetworkSession::NotAvailable;
553 } else if ((ptr->state & QNetworkConfiguration::Undefined) ==
554 QNetworkConfiguration::Undefined) {
555 return QNetworkSession::NotAvailable;
556 }
557
558 return QNetworkSession::Invalid;
559 }
560
capabilities() const561 QNetworkConfigurationManager::Capabilities QNativeWifiEngine::capabilities() const
562 {
563 return QNetworkConfigurationManager::ForcedRoaming |
564 QNetworkConfigurationManager::CanStartAndStopInterfaces;
565 }
566
createSessionBackend()567 QNetworkSessionPrivate *QNativeWifiEngine::createSessionBackend()
568 {
569 return new QNetworkSessionPrivateImpl;
570 }
571
defaultConfiguration()572 QNetworkConfigurationPrivatePointer QNativeWifiEngine::defaultConfiguration()
573 {
574 return QNetworkConfigurationPrivatePointer();
575 }
576
available()577 bool QNativeWifiEngine::available()
578 {
579 if (handle != INVALID_HANDLE_VALUE)
580 return true;
581
582 DWORD clientVersion;
583
584 DWORD result = local_WlanOpenHandle(1, 0, &clientVersion, &handle);
585 if (result != ERROR_SUCCESS) {
586 #ifdef BEARER_MANAGEMENT_DEBUG
587 if (result != ERROR_SERVICE_NOT_ACTIVE)
588 qDebug("%s: WlanOpenHandle failed with error %ld\n", __FUNCTION__, result);
589 #endif
590
591 return false;
592 }
593
594 result = local_WlanRegisterNotification(handle, WLAN_NOTIFICATION_SOURCE_ALL, true,
595 WLAN_NOTIFICATION_CALLBACK(qNotificationCallback),
596 this, 0, 0);
597 #ifdef BEARER_MANAGEMENT_DEBUG
598 if (result != ERROR_SUCCESS)
599 qDebug("%s: WlanRegisterNotification failed with error %ld\n", __FUNCTION__, result);
600 #endif
601
602 return handle != INVALID_HANDLE_VALUE;
603 }
604
closeHandle()605 void QNativeWifiEngine::closeHandle()
606 {
607 if (handle != INVALID_HANDLE_VALUE) {
608 local_WlanCloseHandle(handle, 0);
609 handle = INVALID_HANDLE_VALUE;
610 }
611 }
612
requiresPolling() const613 bool QNativeWifiEngine::requiresPolling() const
614 {
615 // On Windows XP SP2 and SP3 only connection and disconnection notifications are available.
616 // We need to poll for changes in available wireless networks.
617 return true;
618 }
619
620 QT_END_NAMESPACE
621
622 #endif // QT_NO_BEARERMANAGEMENT
623