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, &parameters, 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