1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qcorewlanengine.h"
41#include "../qnetworksession_impl.h"
42
43#include <QtNetwork/private/qnetworkconfiguration_p.h>
44
45#include <QtCore/qthread.h>
46#include <QtCore/qmutex.h>
47#include <QtCore/qcoreapplication.h>
48#include <QtCore/qstringlist.h>
49
50#include <QtCore/qdebug.h>
51
52#include <QDir>
53#ifndef QT_NO_BEARERMANAGEMENT
54
55extern "C" { // Otherwise it won't find CWKeychain* symbols at link time
56#import <CoreWLAN/CoreWLAN.h>
57}
58
59#include "private/qcore_mac_p.h"
60
61#include <net/if.h>
62#include <ifaddrs.h>
63
64@interface QT_MANGLE_NAMESPACE(QNSListener) : NSObject <CWEventDelegate>
65@property (assign) QCoreWlanEngine* engine;
66@end
67QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSListener);
68
69@implementation QNSListener {
70    NSNotificationCenter *notificationCenter;
71    CWWiFiClient *client;
72    QCoreWlanEngine *engine;
73    NSLock *locker;
74}
75
76- (instancetype)init
77{
78    if ((self = [super init])) {
79        [locker lock];
80        QMacAutoReleasePool pool;
81        notificationCenter = [NSNotificationCenter defaultCenter];
82        client = [CWWiFiClient sharedWiFiClient];
83        client.delegate = self;
84        [client startMonitoringEventWithType:CWEventTypePowerDidChange error:nil];
85        [locker unlock];
86    }
87    return self;
88}
89
90static QNSListener *listener = 0;
91
92-(void)dealloc
93{
94    client.delegate = nil;
95    listener = nil;
96    [super dealloc];
97}
98
99-(void)setEngine:(QCoreWlanEngine *)coreEngine
100{
101    [locker lock];
102    if(!engine)
103        engine = coreEngine;
104    [locker unlock];
105}
106
107-(QCoreWlanEngine *)engine
108{
109    return engine;
110}
111
112-(void)remove
113{
114    [locker lock];
115    [client stopMonitoringAllEventsAndReturnError:nil];
116    [locker unlock];
117}
118
119- (void)powerStateDidChangeForWiFiInterfaceWithName:(NSString *)interfaceName
120{
121    Q_UNUSED(interfaceName);
122    engine->requestUpdate();
123}
124@end
125
126
127QT_BEGIN_NAMESPACE
128
129void networkChangeCallback(SCDynamicStoreRef/* store*/, CFArrayRef changedKeys, void *info)
130{
131    for ( long i = 0; i < CFArrayGetCount(changedKeys); i++) {
132
133        QString changed =  QString::fromCFString((CFStringRef)CFArrayGetValueAtIndex(changedKeys, i));
134        if( changed.contains("/Network/Global/IPv4")) {
135            QCoreWlanEngine* wlanEngine = static_cast<QCoreWlanEngine*>(info);
136            wlanEngine->requestUpdate();
137        }
138    }
139    return;
140}
141
142
143QScanThread::QScanThread(QObject *parent)
144    :QThread(parent)
145{
146}
147
148QScanThread::~QScanThread()
149{
150}
151
152void QScanThread::quit()
153{
154    wait();
155}
156
157void QScanThread::run()
158{
159    QMacAutoReleasePool pool;
160    QStringList found;
161    mutex.lock();
162    CWInterface *currentInterface = [[CWWiFiClient sharedWiFiClient]
163        interfaceWithName:interfaceName.toNSString()];
164    mutex.unlock();
165    const bool currentInterfaceServiceActive = currentInterface.serviceActive;
166
167    if (currentInterface.powerOn) {
168        NSError *err = nil;
169
170        NSSet* apSet = [currentInterface scanForNetworksWithName:nil error:&err];
171
172        if (!err) {
173            for (CWNetwork *apNetwork in apSet) {
174                const QString networkSsid = QString::fromNSString([apNetwork ssid]);
175                const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkSsid));
176                found.append(id);
177
178                QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined;
179                bool known = isKnownSsid(networkSsid);
180                if (currentInterfaceServiceActive) {
181                    if (networkSsid == QString::fromNSString([currentInterface ssid])) {
182                        state = QNetworkConfiguration::Active;
183                    }
184                }
185                if (state == QNetworkConfiguration::Undefined) {
186                    if(known) {
187                        state = QNetworkConfiguration::Discovered;
188                    } else {
189                        state = QNetworkConfiguration::Undefined;
190                    }
191                }
192                QNetworkConfiguration::Purpose purpose = QNetworkConfiguration::UnknownPurpose;
193                if ([apNetwork supportsSecurity:kCWSecurityNone]) {
194                    purpose = QNetworkConfiguration::PublicPurpose;
195                } else {
196                    purpose = QNetworkConfiguration::PrivatePurpose;
197                }
198
199                found.append(foundNetwork(id, networkSsid, state, interfaceName, purpose));
200
201            }
202        }
203    }
204    // add known configurations that are not around.
205    for (auto i = userProfiles.cbegin(), end = userProfiles.cend(); i != end; ++i) {
206
207        QString networkName = i.key();
208        const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkName));
209
210        if(!found.contains(id)) {
211            QString networkSsid = getSsidFromNetworkName(networkName);
212            const QString ssidId = QString::number(qHash(QLatin1String("corewlan:") + networkSsid));
213            QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined;
214            QString interfaceName;
215            if (!i.value().isEmpty())
216                interfaceName = i.value().last();
217
218            if (currentInterfaceServiceActive) {
219                if (networkSsid == QString::fromNSString([currentInterface ssid])) {
220                    state = QNetworkConfiguration::Active;
221                }
222            }
223            if(state == QNetworkConfiguration::Undefined) {
224                if( userProfiles.contains(networkName)
225                    && found.contains(ssidId)) {
226                    state = QNetworkConfiguration::Discovered;
227                }
228            }
229
230            if(state == QNetworkConfiguration::Undefined) {
231                state = QNetworkConfiguration::Defined;
232            }
233
234            found.append(foundNetwork(id, networkName, state, interfaceName, QNetworkConfiguration::UnknownPurpose));
235        }
236    }
237    emit networksChanged();
238}
239
240QStringList QScanThread::foundNetwork(const QString &id, const QString &name, const QNetworkConfiguration::StateFlags state, const QString &interfaceName, const QNetworkConfiguration::Purpose purpose)
241{
242    QStringList found;
243    QMutexLocker locker(&mutex);
244        QNetworkConfigurationPrivate *ptr = new QNetworkConfigurationPrivate;
245
246        ptr->name = name;
247        ptr->isValid = true;
248        ptr->id = id;
249        ptr->state = state;
250        ptr->type = QNetworkConfiguration::InternetAccessPoint;
251        ptr->bearerType = QNetworkConfiguration::BearerWLAN;
252        ptr->purpose = purpose;
253
254        fetchedConfigurations.append( ptr);
255        configurationInterface.insert(ptr->id, interfaceName);
256
257        locker.unlock();
258        locker.relock();
259       found.append(id);
260    return found;
261}
262
263QList<QNetworkConfigurationPrivate *> QScanThread::getConfigurations()
264{
265    QMutexLocker locker(&mutex);
266    return qExchange(fetchedConfigurations, {});
267}
268
269void QScanThread::getUserConfigurations()
270{
271    QMutexLocker locker(&mutex);
272
273    QMacAutoReleasePool pool;
274    userProfiles.clear();
275
276    NSArray<NSString *> *wifiInterfaces = [CWWiFiClient interfaceNames];
277    for (NSString *ifName in wifiInterfaces) {
278
279        CWInterface *wifiInterface = [[CWWiFiClient sharedWiFiClient] interfaceWithName:ifName];
280
281        NSString *nsInterfaceName = wifiInterface.ssid;
282// add user configured system networks
283        SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, (CFStringRef)@"Qt corewlan", nil, nil);
284        NSDictionary * airportPlist = (NSDictionary *)SCDynamicStoreCopyValue(dynRef, (CFStringRef)[NSString stringWithFormat:@"Setup:/Network/Interface/%@/AirPort", nsInterfaceName]);
285        CFRelease(dynRef);
286        if(airportPlist != nil) {
287            NSDictionary *prefNetDict = [airportPlist objectForKey:@"PreferredNetworks"];
288
289            NSArray<NSString *> *thisSsidarray = [prefNetDict valueForKey:@"SSID_STR"];
290            for (NSString *ssidkey in thisSsidarray) {
291                QString thisSsid = QString::fromNSString(ssidkey);
292                if(!userProfiles.contains(thisSsid)) {
293                    QMap <QString,QString> map;
294                    map.insert(thisSsid, QString::fromNSString(nsInterfaceName));
295                    userProfiles.insert(thisSsid, map);
296                }
297            }
298            CFRelease(airportPlist);
299        }
300
301        // remembered networks
302        CWConfiguration *userConfig = [wifiInterface configuration];
303        NSOrderedSet *networkProfiles = [userConfig networkProfiles];
304        NSEnumerator *enumerator = [networkProfiles objectEnumerator];
305        CWNetworkProfile *wProfile;
306        while ((wProfile = [enumerator nextObject])) {
307            QString networkName = QString::fromNSString([wProfile ssid]);
308
309            if (!userProfiles.contains(networkName)) {
310                QMap<QString,QString> map;
311                map.insert(networkName, QString::fromNSString(nsInterfaceName));
312                userProfiles.insert(networkName, map);
313            }
314        }
315
316        // 802.1X user profiles
317        QString userProfilePath = QDir::homePath() + "/Library/Preferences/com.apple.eap.profiles.plist";
318        NSDictionary* eapDict = [[[NSDictionary alloc] initWithContentsOfFile:userProfilePath.toNSString()] autorelease];
319        if(eapDict != nil) {
320            NSString *profileStr= @"Profiles";
321            NSString *nameStr = @"UserDefinedName";
322            NSString *networkSsidStr = @"Wireless Network";
323            for (id profileKey in eapDict) {
324                if ([profileStr isEqualToString:profileKey]) {
325                    NSDictionary *itemDict = [eapDict objectForKey:profileKey];
326                    for (id itemKey in itemDict) {
327
328                        NSInteger dictSize = [itemKey count];
329                        id objects[dictSize];
330                        id keys[dictSize];
331
332                        [itemKey getObjects:objects andKeys:keys];
333                        QString networkName;
334                        QString ssid;
335                        for (int i = 0; i < dictSize; i++) {
336                            if([nameStr isEqualToString:keys[i]]) {
337                                networkName = QString::fromNSString(objects[i]);
338                            }
339                            if ([networkSsidStr isEqualToString:keys[i]]) {
340                                ssid = QString::fromNSString(objects[i]);
341                            }
342                            if (!userProfiles.contains(networkName)
343                                && !ssid.isEmpty()) {
344                                QMap<QString,QString> map;
345                                map.insert(ssid, QString::fromNSString(nsInterfaceName));
346                                userProfiles.insert(networkName, map);
347                            }
348                        }
349                    }
350                }
351            }
352        }
353    }
354}
355
356QString QScanThread::getSsidFromNetworkName(const QString &name) const
357{
358    QMutexLocker locker(&mutex);
359
360    for (auto i = userProfiles.cbegin(), end = userProfiles.cend(); i != end; ++i) {
361        for (auto ij = i.value().cbegin(), end = i.value().cend(); ij != end; ++ij) {
362             const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") +i.key()));
363             if(name == i.key() || name == networkNameHash) {
364                 return ij.key();
365             }
366        }
367    }
368    return QString();
369}
370
371QString QScanThread::getNetworkNameFromSsid(const QString &ssid) const
372{
373    QMutexLocker locker(&mutex);
374
375    for (auto i = userProfiles.cbegin(), end = userProfiles.cend(); i != end; ++i) {
376        if (i.value().contains(ssid))
377            return i.key();
378    }
379    return QString();
380}
381
382bool QScanThread::isKnownSsid(const QString &ssid) const
383{
384    QMutexLocker locker(&mutex);
385
386    for (auto i = userProfiles.cbegin(), end = userProfiles.cend(); i != end; ++i) {
387        if (i.value().contains(ssid))
388            return true;
389    }
390    return false;
391}
392
393
394QCoreWlanEngine::QCoreWlanEngine(QObject *parent)
395:   QBearerEngineImpl(parent), scanThread(0)
396{
397    scanThread = new QScanThread(this);
398    connect(scanThread, SIGNAL(networksChanged()),
399            this, SLOT(networksChanged()));
400}
401
402QCoreWlanEngine::~QCoreWlanEngine()
403{
404    scanThread->wait();
405
406    qDeleteAll(qExchange(foundConfigurations, {}));
407    [listener remove];
408    [listener release];
409}
410
411void QCoreWlanEngine::initialize()
412{
413    QMutexLocker locker(&mutex);
414    QMacAutoReleasePool pool;
415
416    if ([[CWWiFiClient interfaceNames] count] > 0 && !listener) {
417        listener = [QNSListener alloc] init];
418        listener.engine = this;
419        hasWifi = true;
420    } else {
421        hasWifi = false;
422    }
423    storeSession = NULL;
424
425    startNetworkChangeLoop();
426}
427
428
429QString QCoreWlanEngine::getInterfaceFromId(const QString &id)
430{
431    QMutexLocker locker(&mutex);
432
433    return scanThread->configurationInterface.value(id);
434}
435
436bool QCoreWlanEngine::hasIdentifier(const QString &id)
437{
438    QMutexLocker locker(&mutex);
439
440    return scanThread->configurationInterface.contains(id);
441}
442
443void QCoreWlanEngine::connectToId(const QString &id)
444{
445    QMutexLocker locker(&mutex);
446    QMacAutoReleasePool pool;
447    QString interfaceString = getInterfaceFromId(id);
448
449    CWInterface *wifiInterface =
450        [[CWWiFiClient sharedWiFiClient] interfaceWithName:interfaceString.toNSString()];
451
452    if (wifiInterface.powerOn) {
453        NSError *err = nil;
454        QString wantedSsid;
455        QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id);
456
457        const QString idHash = QString::number(qHash(QLatin1String("corewlan:") + ptr->name));
458        const QString idHash2 = QString::number(qHash(QLatin1String("corewlan:") + scanThread->getNetworkNameFromSsid(ptr->name)));
459
460        QString wantedNetwork;
461        for (auto i = scanThread->userProfiles.cbegin(), end = scanThread->userProfiles.cend(); i != end; ++i) {
462            wantedNetwork = i.key();
463            const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork));
464            if (id == networkNameHash) {
465                wantedSsid = scanThread->getSsidFromNetworkName(wantedNetwork);
466                break;
467            }
468        }
469
470        NSSet *scanSet = [wifiInterface scanForNetworksWithName:wantedSsid.toNSString() error:&err];
471
472        if(!err) {
473            for (CWNetwork *apNetwork in scanSet) {
474                NSData *ssidData = [apNetwork ssidData];
475                bool result = false;
476
477                SecIdentityRef identity = 0;
478                // Check first whether we require IEEE 802.1X authentication for the wanted SSID
479                if (CWKeychainCopyWiFiEAPIdentity(kCWKeychainDomainSystem, ssidData, &identity) == errSecSuccess) {
480                    NSString *username = nil;
481                    NSString *password = nil;
482                    if (CWKeychainFindWiFiEAPUsernameAndPassword(kCWKeychainDomainSystem, ssidData, &username, &password) == errSecSuccess) {
483                        result = [wifiInterface associateToEnterpriseNetwork:apNetwork
484                                    identity:identity username:(NSString *)username password:(NSString *)password
485                                    error:&err];
486                        [username release];
487                        [password release];
488                    }
489                    CFRelease(identity);
490                } else {
491                    NSString *password = nil;
492                    if (CWKeychainFindWiFiPassword(kCWKeychainDomainSystem, ssidData, &password) == errSecSuccess) {
493                        result = [wifiInterface associateToNetwork:apNetwork password:(NSString *)password error:&err];
494                        [password release];
495                    }
496                }
497
498                if (!err) {
499                    if (!result) {
500                        emit connectionError(id, ConnectError);
501                    } else {
502                        return;
503                    }
504                } else {
505                    qDebug() <<"associate ERROR"<<  QString::fromNSString([err localizedDescription ]);
506                }
507            } //end scan network
508        } else {
509            qDebug() <<"scan ERROR"<<  QString::fromNSString([err localizedDescription ]);
510        }
511        emit connectionError(id, InterfaceLookupError);
512    }
513
514    locker.unlock();
515    emit connectionError(id, InterfaceLookupError);
516}
517
518void QCoreWlanEngine::disconnectFromId(const QString &id)
519{
520    QMutexLocker locker(&mutex);
521
522    QString interfaceString = getInterfaceFromId(id);
523    if (interfaceString.isEmpty()) {
524        locker.unlock();
525        emit connectionError(id, DisconnectionError);
526        return;
527    }
528    QMacAutoReleasePool pool;
529
530    CWInterface *wifiInterface =
531        [[CWWiFiClient sharedWiFiClient] interfaceWithName:interfaceString.toNSString()];
532    disconnectedInterfaceString = interfaceString;
533
534    [wifiInterface disassociate];
535
536    QTimer::singleShot(1000, this,SLOT(checkDisconnect()));
537}
538
539void QCoreWlanEngine::checkDisconnect()
540{
541    QMutexLocker locker(&mutex);
542    if (!disconnectedInterfaceString.isEmpty()) {
543        QMacAutoReleasePool pool;
544
545        CWInterface *wifiInterface = [[CWWiFiClient sharedWiFiClient]
546            interfaceWithName:disconnectedInterfaceString.toNSString()];
547
548        const QString networkSsid = QString::fromNSString([wifiInterface ssid]);
549        if (!networkSsid.isEmpty()) {
550            const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkSsid));
551            locker.unlock();
552            emit connectionError(id, DisconnectionError);
553            locker.relock();
554        }
555        disconnectedInterfaceString.clear();
556    }
557}
558
559void QCoreWlanEngine::requestUpdate()
560{
561    scanThread->getUserConfigurations();
562    doRequestUpdate();
563}
564
565void QCoreWlanEngine::doRequestUpdate()
566{
567    QMutexLocker locker(&mutex);
568
569    QMacAutoReleasePool pool;
570
571    NSArray<NSString *> *wifiInterfaces = [CWWiFiClient interfaceNames];
572    for (NSString *ifName in wifiInterfaces) {
573            scanThread->interfaceName = QString::fromNSString(ifName);
574            scanThread->start();
575    }
576    locker.unlock();
577    if ([wifiInterfaces count] == 0)
578        networksChanged();
579}
580
581bool QCoreWlanEngine::isWifiReady(const QString &wifiDeviceName)
582{
583    QMutexLocker locker(&mutex);
584    bool haswifi = false;
585    if(hasWifi) {
586        QMacAutoReleasePool pool;
587        CWInterface *defaultInterface = [[CWWiFiClient sharedWiFiClient]
588            interfaceWithName:wifiDeviceName.toNSString()];
589        if (defaultInterface.powerOn) {
590            haswifi = true;
591        }
592    }
593    return haswifi;
594}
595
596
597QNetworkSession::State QCoreWlanEngine::sessionStateForId(const QString &id)
598{
599    QMutexLocker locker(&mutex);
600    QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id);
601
602    if (!ptr)
603        return QNetworkSession::Invalid;
604
605    if (!ptr->isValid) {
606        return QNetworkSession::Invalid;
607    } else if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) {
608        return QNetworkSession::Connected;
609    } else if ((ptr->state & QNetworkConfiguration::Discovered) ==
610                QNetworkConfiguration::Discovered) {
611        return QNetworkSession::Disconnected;
612    } else if ((ptr->state & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined) {
613        return QNetworkSession::NotAvailable;
614    } else if ((ptr->state & QNetworkConfiguration::Undefined) ==
615                QNetworkConfiguration::Undefined) {
616        return QNetworkSession::NotAvailable;
617    }
618
619    return QNetworkSession::Invalid;
620}
621
622QNetworkConfigurationManager::Capabilities QCoreWlanEngine::capabilities() const
623{
624    return QNetworkConfigurationManager::ForcedRoaming;
625}
626
627void QCoreWlanEngine::startNetworkChangeLoop()
628{
629
630    SCDynamicStoreContext dynStoreContext = { 0, this/*(void *)storeSession*/, NULL, NULL, NULL };
631    storeSession = SCDynamicStoreCreate(NULL,
632                                 CFSTR("networkChangeCallback"),
633                                 networkChangeCallback,
634                                 &dynStoreContext);
635    if (!storeSession ) {
636        qWarning() << "could not open dynamic store: error:" << SCErrorString(SCError());
637        return;
638    }
639
640    CFMutableArrayRef notificationKeys;
641    notificationKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
642    CFMutableArrayRef patternsArray;
643    patternsArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
644
645    CFStringRef storeKey;
646    storeKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
647                                                     kSCDynamicStoreDomainState,
648                                                     kSCEntNetIPv4);
649    CFArrayAppendValue(notificationKeys, storeKey);
650    CFRelease(storeKey);
651
652    storeKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
653                                                      kSCDynamicStoreDomainState,
654                                                      kSCCompAnyRegex,
655                                                      kSCEntNetIPv4);
656    CFArrayAppendValue(patternsArray, storeKey);
657    CFRelease(storeKey);
658
659    if (!SCDynamicStoreSetNotificationKeys(storeSession , notificationKeys, patternsArray)) {
660        qWarning() << "register notification error:"<< SCErrorString(SCError());
661        CFRelease(storeSession );
662        CFRelease(notificationKeys);
663        CFRelease(patternsArray);
664        return;
665    }
666    CFRelease(notificationKeys);
667    CFRelease(patternsArray);
668
669    runloopSource = SCDynamicStoreCreateRunLoopSource(NULL, storeSession , 0);
670    if (!runloopSource) {
671        qWarning() << "runloop source error:"<< SCErrorString(SCError());
672        CFRelease(storeSession );
673        return;
674    }
675
676    CFRunLoopAddSource(CFRunLoopGetCurrent(), runloopSource, kCFRunLoopDefaultMode);
677    return;
678}
679
680QNetworkSessionPrivate *QCoreWlanEngine::createSessionBackend()
681{
682    return new QNetworkSessionPrivateImpl;
683}
684
685QNetworkConfigurationPrivatePointer QCoreWlanEngine::defaultConfiguration()
686{
687    return QNetworkConfigurationPrivatePointer();
688}
689
690bool QCoreWlanEngine::requiresPolling() const
691{
692    return true;
693}
694
695void QCoreWlanEngine::networksChanged()
696{
697    QMutexLocker locker(&mutex);
698
699    QStringList previous = accessPointConfigurations.keys();
700
701    QList<QNetworkConfigurationPrivate *> foundConfigurations = scanThread->getConfigurations();
702    while (!foundConfigurations.isEmpty()) {
703        QNetworkConfigurationPrivate *cpPriv = foundConfigurations.takeFirst();
704
705        previous.removeAll(cpPriv->id);
706
707        if (accessPointConfigurations.contains(cpPriv->id)) {
708            QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(cpPriv->id);
709
710            bool changed = false;
711
712            ptr->mutex.lock();
713
714            if (ptr->isValid != cpPriv->isValid) {
715                ptr->isValid = cpPriv->isValid;
716                changed = true;
717            }
718
719            if (ptr->name != cpPriv->name) {
720                ptr->name = cpPriv->name;
721                changed = true;
722            }
723
724            if (ptr->bearerType != cpPriv->bearerType) {
725                ptr->bearerType = cpPriv->bearerType;
726                changed = true;
727            }
728
729            if (ptr->state != cpPriv->state) {
730                ptr->state = cpPriv->state;
731                changed = true;
732            }
733
734            ptr->mutex.unlock();
735
736            if (changed) {
737                locker.unlock();
738                emit configurationChanged(ptr);
739                locker.relock();
740            }
741
742            delete cpPriv;
743        } else {
744            QNetworkConfigurationPrivatePointer ptr(cpPriv);
745
746            accessPointConfigurations.insert(ptr->id, ptr);
747
748            locker.unlock();
749            emit configurationAdded(ptr);
750            locker.relock();
751        }
752    }
753
754    while (!previous.isEmpty()) {
755        QNetworkConfigurationPrivatePointer ptr =
756            accessPointConfigurations.take(previous.takeFirst());
757
758        locker.unlock();
759        emit configurationRemoved(ptr);
760        locker.relock();
761    }
762
763    locker.unlock();
764    emit updateCompleted();
765
766}
767
768quint64 QCoreWlanEngine::bytesWritten(const QString &id)
769{
770    QMutexLocker locker(&mutex);
771    const QString interfaceStr = getInterfaceFromId(id);
772    return getBytes(interfaceStr,false);
773}
774
775quint64 QCoreWlanEngine::bytesReceived(const QString &id)
776{
777    QMutexLocker locker(&mutex);
778    const QString interfaceStr = getInterfaceFromId(id);
779    return getBytes(interfaceStr,true);
780}
781
782quint64 QCoreWlanEngine::startTime(const QString &identifier)
783{
784    QMutexLocker locker(&mutex);
785    QMacAutoReleasePool pool;
786    quint64 timestamp = 0;
787
788    NSString *filePath = @"/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist";
789    NSDictionary* plistDict = [[[NSDictionary alloc] initWithContentsOfFile:filePath] autorelease];
790    if(plistDict == nil)
791        return timestamp;
792    NSString *input = @"KnownNetworks";
793    NSString *timeStampStr = @"_timeStamp";
794
795    NSString *ssidStr = @"SSID_STR";
796
797    for (id key in plistDict) {
798        if ([input isEqualToString:key]) {
799
800            NSDictionary *knownNetworksDict = [plistDict objectForKey:key];
801            if(knownNetworksDict == nil)
802                return timestamp;
803            for (id networkKey in knownNetworksDict) {
804                bool isFound = false;
805                NSDictionary *itemDict = [knownNetworksDict objectForKey:networkKey];
806                if(itemDict == nil)
807                    return timestamp;
808                NSInteger dictSize = [itemDict count];
809                id objects[dictSize];
810                id keys[dictSize];
811
812                [itemDict getObjects:objects andKeys:keys];
813                bool ok = false;
814                for(int i = 0; i < dictSize; i++) {
815                    if([ssidStr isEqualToString:keys[i]]) {
816                        const QString ident = QString::number(qHash(QLatin1String("corewlan:") + QString::fromNSString(objects[i])));
817                        if(ident == identifier) {
818                            ok = true;
819                        }
820                    }
821                    if(ok && [timeStampStr isEqualToString:keys[i]]) {
822                        timestamp = (quint64)[objects[i] timeIntervalSince1970];
823                        isFound = true;
824                        break;
825                    }
826                }
827                if(isFound)
828                    break;
829            }
830        }
831    }
832    return timestamp;
833}
834
835quint64 QCoreWlanEngine::getBytes(const QString &interfaceName, bool b)
836{
837    struct ifaddrs *ifAddressList, *ifAddress;
838    struct if_data *if_data;
839
840    quint64 bytes = 0;
841    ifAddressList = nil;
842    if(getifaddrs(&ifAddressList) == 0) {
843        for(ifAddress = ifAddressList; ifAddress; ifAddress = ifAddress->ifa_next) {
844            if(interfaceName == ifAddress->ifa_name) {
845                if_data = (struct if_data*)ifAddress->ifa_data;
846                if(b) {
847                    bytes = if_data->ifi_ibytes;
848                    break;
849                } else {
850                    bytes = if_data->ifi_obytes;
851                    break;
852                }
853            }
854        }
855        freeifaddrs(ifAddressList);
856    }
857    return bytes;
858}
859
860QT_END_NAMESPACE
861
862#endif
863