--- src/plugins/bearer/corewlan/qcorewlanengine.mm +++ src/plugins/bearer/corewlan/qcorewlanengine.mm @@ -52,29 +52,17 @@ #include #include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include + +extern "C" { // Otherwise it won't find CWKeychain* symbols at link time +#import +} + #include "private/qcore_mac_p.h" #include #include -inline QString qt_NSStringToQString(const NSString *nsstr) -{ return QCFString::toQString(reinterpret_cast(nsstr)); } - -inline NSString *qt_QStringToNSString(const QString &qstr) -{ return [const_cast(reinterpret_cast(QCFString::toCFStringRef(qstr))) autorelease]; } - +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 @interface QT_MANGLE_NAMESPACE(QNSListener) : NSObject { @@ -86,6 +74,7 @@ inline NSString *qt_QStringToNSString(const QString &qstr) - (void)notificationHandler;//:(NSNotification *)notification; - (void)remove; - (void)setEngine:(QCoreWlanEngine *)coreEngine; +- (QCoreWlanEngine *)engine; - (void)dealloc; @property (assign) QCoreWlanEngine* engine; @@ -93,7 +82,6 @@ inline NSString *qt_QStringToNSString(const QString &qstr) @end @implementation QT_MANGLE_NAMESPACE(QNSListener) -@synthesize engine; - (id) init { @@ -101,7 +89,7 @@ inline NSString *qt_QStringToNSString(const QString &qstr) NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; notificationCenter = [NSNotificationCenter defaultCenter]; currentInterface = [CWInterface interfaceWithName:nil]; - [notificationCenter addObserver:self selector:@selector(notificationHandler:) name:kCWPowerDidChangeNotification object:nil]; + [notificationCenter addObserver:self selector:@selector(notificationHandler:) name:CWPowerDidChangeNotification object:nil]; [locker unlock]; [autoreleasepool release]; return self; @@ -120,6 +108,11 @@ inline NSString *qt_QStringToNSString(const QString &qstr) [locker unlock]; } +-(QCoreWlanEngine *)engine +{ + return engine; +} + -(void)remove { [locker lock]; @@ -133,7 +126,7 @@ inline NSString *qt_QStringToNSString(const QString &qstr) } @end -QT_MANGLE_NAMESPACE(QNSListener) *listener = 0; +static QT_MANGLE_NAMESPACE(QNSListener) *listener = 0; QT_BEGIN_NAMESPACE @@ -170,36 +163,28 @@ void QScanThread::run() NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; QStringList found; mutex.lock(); - CWInterface *currentInterface = [CWInterface interfaceWithName:qt_QStringToNSString(interfaceName)]; + CWInterface *currentInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceName)]; mutex.unlock(); - if([currentInterface power]) { + if (currentInterface.powerOn) { NSError *err = nil; - NSDictionary *parametersDict = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:YES], kCWScanKeyMerge, - [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType, - [NSNumber numberWithInteger:100], kCWScanKeyRestTime, nil]; - NSArray* apArray = [currentInterface scanForNetworksWithParameters:parametersDict error:&err]; - CWNetwork *apNetwork; + NSSet* apSet = [currentInterface scanForNetworksWithName:nil error:&err]; if (!err) { - - for(uint row=0; row < [apArray count]; row++ ) { - apNetwork = [apArray objectAtIndex:row]; - - const QString networkSsid = qt_NSStringToQString([apNetwork ssid]); + for (CWNetwork *apNetwork in apSet) { + const QString networkSsid = QCFString::toQString(CFStringRef([apNetwork ssid])); const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkSsid)); found.append(id); QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined; bool known = isKnownSsid(networkSsid); - if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) { - if( networkSsid == qt_NSStringToQString( [currentInterface ssid])) { + if (currentInterface.serviceActive) { + if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) { state = QNetworkConfiguration::Active; } } - if(state == QNetworkConfiguration::Undefined) { + if (state == QNetworkConfiguration::Undefined) { if(known) { state = QNetworkConfiguration::Discovered; } else { @@ -207,7 +192,7 @@ void QScanThread::run() } } QNetworkConfiguration::Purpose purpose = QNetworkConfiguration::UnknownPurpose; - if([[apNetwork securityMode] intValue] == kCWSecurityModeOpen) { + if ([apNetwork supportsSecurity:kCWSecurityNone]) { purpose = QNetworkConfiguration::PublicPurpose; } else { purpose = QNetworkConfiguration::PrivatePurpose; @@ -237,8 +222,8 @@ void QScanThread::run() interfaceName = ij.value(); } - if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) { - if( networkSsid == qt_NSStringToQString([currentInterface ssid])) { + if (currentInterface.serviceActive) { + if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) { state = QNetworkConfiguration::Active; } } @@ -300,14 +285,14 @@ void QScanThread::getUserConfigurations() NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; userProfiles.clear(); - NSArray *wifiInterfaces = [CWInterface supportedInterfaces]; - for(uint row=0; row < [wifiInterfaces count]; row++ ) { + NSSet *wifiInterfaces = [CWInterface interfaceNames]; + for (NSString *ifName in wifiInterfaces) { - CWInterface *wifiInterface = [CWInterface interfaceWithName: [wifiInterfaces objectAtIndex:row]]; - if ( ![wifiInterface power] ) + CWInterface *wifiInterface = [CWInterface interfaceWithName: ifName]; + if (!wifiInterface.powerOn) continue; - NSString *nsInterfaceName = [wifiInterface name]; + NSString *nsInterfaceName = wifiInterface.ssid; // add user configured system networks SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, (CFStringRef)@"Qt corewlan", nil, nil); NSDictionary * airportPlist = (NSDictionary *)SCDynamicStoreCopyValue(dynRef, (CFStringRef)[NSString stringWithFormat:@"Setup:/Network/Interface/%@/AirPort", nsInterfaceName]); @@ -316,11 +301,11 @@ void QScanThread::getUserConfigurations() NSDictionary *prefNetDict = [airportPlist objectForKey:@"PreferredNetworks"]; NSArray *thisSsidarray = [prefNetDict valueForKey:@"SSID_STR"]; - for(NSString *ssidkey in thisSsidarray) { - QString thisSsid = qt_NSStringToQString(ssidkey); + for (NSString *ssidkey in thisSsidarray) { + QString thisSsid = QCFString::toQString(CFStringRef(ssidkey)); if(!userProfiles.contains(thisSsid)) { QMap map; - map.insert(thisSsid, qt_NSStringToQString(nsInterfaceName)); + map.insert(thisSsid, QCFString::toQString(CFStringRef(nsInterfaceName))); userProfiles.insert(thisSsid, map); } } @@ -329,7 +314,7 @@ void QScanThread::getUserConfigurations() // 802.1X user profiles QString userProfilePath = QDir::homePath() + "/Library/Preferences/com.apple.eap.profiles.plist"; - NSDictionary* eapDict = [[[NSDictionary alloc] initWithContentsOfFile:qt_QStringToNSString(userProfilePath)] autorelease]; + NSDictionary* eapDict = [[[NSDictionary alloc] initWithContentsOfFile: (NSString *)QCFString::toCFStringRef(userProfilePath)] autorelease]; if(eapDict != nil) { NSString *profileStr= @"Profiles"; NSString *nameStr = @"UserDefinedName"; @@ -348,15 +333,15 @@ void QScanThread::getUserConfigurations() QString ssid; for(int i = 0; i < dictSize; i++) { if([nameStr isEqualToString:keys[i]]) { - networkName = qt_NSStringToQString(objects[i]); + networkName = QCFString::toQString(CFStringRef(objects[i])); } if([networkSsidStr isEqualToString:keys[i]]) { - ssid = qt_NSStringToQString(objects[i]); + ssid = QCFString::toQString(CFStringRef(objects[i])); } if(!userProfiles.contains(networkName) && !ssid.isEmpty()) { QMap map; - map.insert(ssid, qt_NSStringToQString(nsInterfaceName)); + map.insert(ssid, QCFString::toQString(CFStringRef(nsInterfaceName))); userProfiles.insert(networkName, map); } } @@ -444,7 +429,7 @@ void QCoreWlanEngine::initialize() QMutexLocker locker(&mutex); NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; - if([[CWInterface supportedInterfaces] count] > 0 && !listener) { + if ([[CWInterface interfaceNames] count] > 0 && !listener) { listener = [[QT_MANGLE_NAMESPACE(QNSListener) alloc] init]; listener.engine = this; hasWifi = true; @@ -479,141 +464,68 @@ void QCoreWlanEngine::connectToId(const QString &id) QString interfaceString = getInterfaceFromId(id); CWInterface *wifiInterface = - [CWInterface interfaceWithName: qt_QStringToNSString(interfaceString)]; + [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)]; - if ([wifiInterface power]) { + if (wifiInterface.powerOn) { NSError *err = nil; - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0]; - QString wantedSsid; - QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); const QString idHash = QString::number(qHash(QLatin1String("corewlan:") + ptr->name)); const QString idHash2 = QString::number(qHash(QLatin1String("corewlan:") + scanThread->getNetworkNameFromSsid(ptr->name))); - bool using8021X = false; - if (idHash2 != id) { - NSArray *array = [CW8021XProfile allUser8021XProfiles]; - - for (NSUInteger i = 0; i < [array count]; ++i) { - const QString networkNameHashCheck = QString::number(qHash(QLatin1String("corewlan:") + qt_NSStringToQString([[array objectAtIndex:i] userDefinedName]))); - - const QString ssidHash = QString::number(qHash(QLatin1String("corewlan:") + qt_NSStringToQString([[array objectAtIndex:i] ssid]))); - - if (id == networkNameHashCheck || id == ssidHash) { - const QString thisName = scanThread->getSsidFromNetworkName(id); - if (thisName.isEmpty()) - wantedSsid = id; - else - wantedSsid = thisName; - - [params setValue: [array objectAtIndex:i] forKey:kCWAssocKey8021XProfile]; - using8021X = true; - break; - } + QString wantedNetwork; + QMapIterator > i(scanThread->userProfiles); + while (i.hasNext()) { + i.next(); + wantedNetwork = i.key(); + const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork)); + if (id == networkNameHash) { + wantedSsid = scanThread->getSsidFromNetworkName(wantedNetwork); + break; } } - if (!using8021X) { - QString wantedNetwork; - QMapIterator > i(scanThread->userProfiles); - while (i.hasNext()) { - i.next(); - wantedNetwork = i.key(); - const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork)); - if (id == networkNameHash) { - wantedSsid = scanThread->getSsidFromNetworkName(wantedNetwork); - break; - } - } - } - NSDictionary *scanParameters = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:YES], kCWScanKeyMerge, - [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType, - [NSNumber numberWithInteger:100], kCWScanKeyRestTime, - qt_QStringToNSString(wantedSsid), kCWScanKeySSID, - nil]; - - NSArray *scanArray = [wifiInterface scanForNetworksWithParameters:scanParameters error:&err]; + NSSet *scanSet = [wifiInterface scanForNetworksWithName:(NSString *)QCFString::toCFStringRef(wantedSsid) error:&err]; if(!err) { - for(uint row=0; row < [scanArray count]; row++ ) { - CWNetwork *apNetwork = [scanArray objectAtIndex:row]; - - if(wantedSsid == qt_NSStringToQString([apNetwork ssid])) { - - if(!using8021X) { - SecKeychainAttribute attributes[3]; - - NSString *account = [apNetwork ssid]; - NSString *keyKind = @"AirPort network password"; - NSString *keyName = account; - - attributes[0].tag = kSecAccountItemAttr; - attributes[0].data = (void *)[account UTF8String]; - attributes[0].length = [account length]; - - attributes[1].tag = kSecDescriptionItemAttr; - attributes[1].data = (void *)[keyKind UTF8String]; - attributes[1].length = [keyKind length]; - - attributes[2].tag = kSecLabelItemAttr; - attributes[2].data = (void *)[keyName UTF8String]; - attributes[2].length = [keyName length]; - - SecKeychainAttributeList attributeList = {3,attributes}; - - SecKeychainSearchRef searchRef; - SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributeList, &searchRef); - - NSString *password = @""; - SecKeychainItemRef searchItem; - - if (SecKeychainSearchCopyNext(searchRef, &searchItem) == noErr) { - UInt32 realPasswordLength; - SecKeychainAttribute attributesW[8]; - attributesW[0].tag = kSecAccountItemAttr; - SecKeychainAttributeList listW = {1,attributesW}; - char *realPassword; - OSStatus status = SecKeychainItemCopyContent(searchItem, NULL, &listW, &realPasswordLength,(void **)&realPassword); - - if (status == noErr) { - if (realPassword != NULL) { - - QByteArray pBuf; - pBuf.resize(realPasswordLength); - pBuf.prepend(realPassword); - pBuf.insert(realPasswordLength,'\0'); - - password = [NSString stringWithUTF8String:pBuf]; - } - SecKeychainItemFreeContent(&listW, realPassword); - } - - CFRelease(searchItem); - } else { - qDebug() << "SecKeychainSearchCopyNext error"; - } - [params setValue: password forKey: kCWAssocKeyPassphrase]; - } // end using8021X - - - bool result = [wifiInterface associateToNetwork: apNetwork parameters:[NSDictionary dictionaryWithDictionary:params] error:&err]; + for (CWNetwork *apNetwork in scanSet) { + CFDataRef ssidData = (CFDataRef)[apNetwork ssidData]; + bool result = false; + + SecIdentityRef identity = 0; + // Check first whether we require IEEE 802.1X authentication for the wanted SSID + if (CWKeychainCopyEAPIdentity(ssidData, &identity) == errSecSuccess) { + CFStringRef username = 0; + CFStringRef password = 0; + if (CWKeychainCopyEAPUsernameAndPassword(ssidData, &username, &password) == errSecSuccess) { + result = [wifiInterface associateToEnterpriseNetwork:apNetwork + identity:identity username:(NSString *)username password:(NSString *)password + error:&err]; + CFRelease(username); + CFRelease(password); + } + CFRelease(identity); + } else { + CFStringRef password = 0; + if (CWKeychainCopyPassword(ssidData, &password) == errSecSuccess) { + result = [wifiInterface associateToNetwork:apNetwork password:(NSString *)password error:&err]; + CFRelease(password); + } + } - if(!err) { - if(!result) { - emit connectionError(id, ConnectError); - } else { - return; - } + if (!err) { + if (!result) { + emit connectionError(id, ConnectError); } else { - qDebug() <<"associate ERROR"<< qt_NSStringToQString([err localizedDescription ]); + return; } + } else { + qDebug() <<"associate ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ])); } } //end scan network } else { - qDebug() <<"scan ERROR"<< qt_NSStringToQString([err localizedDescription ]); + qDebug() <<"scan ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ])); } emit connectionError(id, InterfaceLookupError); } @@ -631,10 +543,10 @@ void QCoreWlanEngine::disconnectFromId(const QString &id) NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; CWInterface *wifiInterface = - [CWInterface interfaceWithName: qt_QStringToNSString(interfaceString)]; + [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)]; [wifiInterface disassociate]; - if ([[wifiInterface interfaceState]intValue] != kCWInterfaceStateInactive) { + if (wifiInterface.serviceActive) { locker.unlock(); emit connectionError(id, DisconnectionError); locker.relock(); @@ -654,9 +566,9 @@ void QCoreWlanEngine::doRequestUpdate() NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; - NSArray *wifiInterfaces = [CWInterface supportedInterfaces]; - for (uint row = 0; row < [wifiInterfaces count]; ++row) { - scanThread->interfaceName = qt_NSStringToQString([wifiInterfaces objectAtIndex:row]); + NSSet *wifiInterfaces = [CWInterface interfaceNames]; + for (NSString *ifName in wifiInterfaces) { + scanThread->interfaceName = QCFString::toQString(CFStringRef(ifName)); scanThread->start(); } locker.unlock(); @@ -669,8 +581,8 @@ bool QCoreWlanEngine::isWifiReady(const QString &wifiDeviceName) bool haswifi = false; if(hasWifi) { NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; - CWInterface *defaultInterface = [CWInterface interfaceWithName: qt_QStringToNSString(wifiDeviceName)]; - if([defaultInterface power]) { + CWInterface *defaultInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(wifiDeviceName)]; + if (defaultInterface.powerOn) { haswifi = true; } [autoreleasepool release]; @@ -898,7 +810,7 @@ quint64 QCoreWlanEngine::startTime(const QString &identifier) bool ok = false; for(int i = 0; i < dictSize; i++) { if([ssidStr isEqualToString:keys[i]]) { - const QString ident = QString::number(qHash(QLatin1String("corewlan:") + qt_NSStringToQString(objects[i]))); + const QString ident = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef(objects[i])))); if(ident == identifier) { ok = true; } @@ -944,3 +856,7 @@ quint64 QCoreWlanEngine::getBytes(const QString &interfaceName, bool b) } QT_END_NAMESPACE + +#else // QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE +#include "qcorewlanengine_10_6.mm" +#endif diff --git a/src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm b/src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm new file mode 100644 index 0000000..a3bf615 --- /dev/null +++ src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm @@ -0,0 +1,916 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +@interface QT_MANGLE_NAMESPACE(QNSListener) : NSObject +{ + NSNotificationCenter *notificationCenter; + CWInterface *currentInterface; + QCoreWlanEngine *engine; + NSLock *locker; +} +- (void)notificationHandler;//:(NSNotification *)notification; +- (void)remove; +- (void)setEngine:(QCoreWlanEngine *)coreEngine; +- (QCoreWlanEngine *)engine; +- (void)dealloc; + +@property (assign) QCoreWlanEngine* engine; + +@end + +@implementation QT_MANGLE_NAMESPACE(QNSListener) + +- (id) init +{ + [locker lock]; + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + notificationCenter = [NSNotificationCenter defaultCenter]; + currentInterface = [CWInterface interfaceWithName:nil]; + [notificationCenter addObserver:self selector:@selector(notificationHandler:) name:kCWPowerDidChangeNotification object:nil]; + [locker unlock]; + [autoreleasepool release]; + return self; +} + +-(void)dealloc +{ + [super dealloc]; +} + +-(void)setEngine:(QCoreWlanEngine *)coreEngine +{ + [locker lock]; + if(!engine) + engine = coreEngine; + [locker unlock]; +} + +-(QCoreWlanEngine *)engine +{ + return engine; +} + +-(void)remove +{ + [locker lock]; + [notificationCenter removeObserver:self]; + [locker unlock]; +} + +- (void)notificationHandler//:(NSNotification *)notification +{ + engine->requestUpdate(); +} +@end + +static QT_MANGLE_NAMESPACE(QNSListener) *listener = 0; + +QT_BEGIN_NAMESPACE + +void networkChangeCallback(SCDynamicStoreRef/* store*/, CFArrayRef changedKeys, void *info) +{ + for ( long i = 0; i < CFArrayGetCount(changedKeys); i++) { + + QString changed = QCFString::toQString(CFStringRef((CFStringRef)CFArrayGetValueAtIndex(changedKeys, i))); + if( changed.contains("/Network/Global/IPv4")) { + QCoreWlanEngine* wlanEngine = static_cast(info); + wlanEngine->requestUpdate(); + } + } + return; +} + + +QScanThread::QScanThread(QObject *parent) + :QThread(parent) +{ +} + +QScanThread::~QScanThread() +{ +} + +void QScanThread::quit() +{ + wait(); +} + +void QScanThread::run() +{ + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + QStringList found; + mutex.lock(); + CWInterface *currentInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceName)]; + mutex.unlock(); + + if([currentInterface power]) { + NSError *err = nil; + NSDictionary *parametersDict = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:YES], kCWScanKeyMerge, + [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType, + [NSNumber numberWithInteger:100], kCWScanKeyRestTime, nil]; + + NSArray* apArray = [currentInterface scanForNetworksWithParameters:parametersDict error:&err]; + CWNetwork *apNetwork; + + if (!err) { + + for(uint row=0; row < [apArray count]; row++ ) { + apNetwork = [apArray objectAtIndex:row]; + + const QString networkSsid = QCFString::toQString(CFStringRef([apNetwork ssid])); + const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkSsid)); + found.append(id); + + QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined; + bool known = isKnownSsid(networkSsid); + if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) { + if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) { + state = QNetworkConfiguration::Active; + } + } + if(state == QNetworkConfiguration::Undefined) { + if(known) { + state = QNetworkConfiguration::Discovered; + } else { + state = QNetworkConfiguration::Undefined; + } + } + QNetworkConfiguration::Purpose purpose = QNetworkConfiguration::UnknownPurpose; + if([[apNetwork securityMode] intValue] == kCWSecurityModeOpen) { + purpose = QNetworkConfiguration::PublicPurpose; + } else { + purpose = QNetworkConfiguration::PrivatePurpose; + } + + found.append(foundNetwork(id, networkSsid, state, interfaceName, purpose)); + + } + } + } + // add known configurations that are not around. + QMapIterator > i(userProfiles); + while (i.hasNext()) { + i.next(); + + QString networkName = i.key(); + const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkName)); + + if(!found.contains(id)) { + QString networkSsid = getSsidFromNetworkName(networkName); + const QString ssidId = QString::number(qHash(QLatin1String("corewlan:") + networkSsid)); + QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined; + QString interfaceName; + QMapIterator ij(i.value()); + while (ij.hasNext()) { + ij.next(); + interfaceName = ij.value(); + } + + if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) { + if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) { + state = QNetworkConfiguration::Active; + } + } + if(state == QNetworkConfiguration::Undefined) { + if( userProfiles.contains(networkName) + && found.contains(ssidId)) { + state = QNetworkConfiguration::Discovered; + } + } + + if(state == QNetworkConfiguration::Undefined) { + state = QNetworkConfiguration::Defined; + } + + found.append(foundNetwork(id, networkName, state, interfaceName, QNetworkConfiguration::UnknownPurpose)); + } + } + emit networksChanged(); + [autoreleasepool release]; +} + +QStringList QScanThread::foundNetwork(const QString &id, const QString &name, const QNetworkConfiguration::StateFlags state, const QString &interfaceName, const QNetworkConfiguration::Purpose purpose) +{ + QStringList found; + QMutexLocker locker(&mutex); + QNetworkConfigurationPrivate *ptr = new QNetworkConfigurationPrivate; + + ptr->name = name; + ptr->isValid = true; + ptr->id = id; + ptr->state = state; + ptr->type = QNetworkConfiguration::InternetAccessPoint; + ptr->bearerType = QNetworkConfiguration::BearerWLAN; + ptr->purpose = purpose; + + fetchedConfigurations.append( ptr); + configurationInterface.insert(ptr->id, interfaceName); + + locker.unlock(); + locker.relock(); + found.append(id); + return found; +} + +QList QScanThread::getConfigurations() +{ + QMutexLocker locker(&mutex); + + QList foundConfigurations = fetchedConfigurations; + fetchedConfigurations.clear(); + + return foundConfigurations; +} + +void QScanThread::getUserConfigurations() +{ + QMutexLocker locker(&mutex); + + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + userProfiles.clear(); + + NSArray *wifiInterfaces = [CWInterface supportedInterfaces]; + for(uint row=0; row < [wifiInterfaces count]; row++ ) { + + CWInterface *wifiInterface = [CWInterface interfaceWithName: [wifiInterfaces objectAtIndex:row]]; + if ( ![wifiInterface power] ) + continue; + + NSString *nsInterfaceName = [wifiInterface name]; +// add user configured system networks + SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, (CFStringRef)@"Qt corewlan", nil, nil); + NSDictionary * airportPlist = (NSDictionary *)SCDynamicStoreCopyValue(dynRef, (CFStringRef)[NSString stringWithFormat:@"Setup:/Network/Interface/%@/AirPort", nsInterfaceName]); + CFRelease(dynRef); + if(airportPlist != nil) { + NSDictionary *prefNetDict = [airportPlist objectForKey:@"PreferredNetworks"]; + + NSArray *thisSsidarray = [prefNetDict valueForKey:@"SSID_STR"]; + for(NSString *ssidkey in thisSsidarray) { + QString thisSsid = QCFString::toQString(CFStringRef(ssidkey)); + if(!userProfiles.contains(thisSsid)) { + QMap map; + map.insert(thisSsid, QCFString::toQString(CFStringRef(nsInterfaceName))); + userProfiles.insert(thisSsid, map); + } + } + CFRelease(airportPlist); + } + + // 802.1X user profiles + QString userProfilePath = QDir::homePath() + "/Library/Preferences/com.apple.eap.profiles.plist"; + NSDictionary* eapDict = [[[NSDictionary alloc] initWithContentsOfFile: (NSString *)QCFString::toCFStringRef(userProfilePath)] autorelease]; + if(eapDict != nil) { + NSString *profileStr= @"Profiles"; + NSString *nameStr = @"UserDefinedName"; + NSString *networkSsidStr = @"Wireless Network"; + for (id profileKey in eapDict) { + if ([profileStr isEqualToString:profileKey]) { + NSDictionary *itemDict = [eapDict objectForKey:profileKey]; + for (id itemKey in itemDict) { + + NSInteger dictSize = [itemKey count]; + id objects[dictSize]; + id keys[dictSize]; + + [itemKey getObjects:objects andKeys:keys]; + QString networkName; + QString ssid; + for(int i = 0; i < dictSize; i++) { + if([nameStr isEqualToString:keys[i]]) { + networkName = QCFString::toQString(CFStringRef(objects[i])); + } + if([networkSsidStr isEqualToString:keys[i]]) { + ssid = QCFString::toQString(CFStringRef(objects[i])); + } + if(!userProfiles.contains(networkName) + && !ssid.isEmpty()) { + QMap map; + map.insert(ssid, QCFString::toQString(CFStringRef(nsInterfaceName))); + userProfiles.insert(networkName, map); + } + } + } + } + } + } + } + [autoreleasepool release]; +} + +QString QScanThread::getSsidFromNetworkName(const QString &name) +{ + QMutexLocker locker(&mutex); + + QMapIterator > i(userProfiles); + while (i.hasNext()) { + i.next(); + QMap map = i.value(); + QMapIterator ij(i.value()); + while (ij.hasNext()) { + ij.next(); + const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") +i.key())); + if(name == i.key() || name == networkNameHash) { + return ij.key(); + } + } + } + return QString(); +} + +QString QScanThread::getNetworkNameFromSsid(const QString &ssid) +{ + QMutexLocker locker(&mutex); + + QMapIterator > i(userProfiles); + while (i.hasNext()) { + i.next(); + QMap map = i.value(); + QMapIterator ij(i.value()); + while (ij.hasNext()) { + ij.next(); + if(ij.key() == ssid) { + return i.key(); + } + } + } + return QString(); +} + +bool QScanThread::isKnownSsid(const QString &ssid) +{ + QMutexLocker locker(&mutex); + + QMapIterator > i(userProfiles); + while (i.hasNext()) { + i.next(); + QMap map = i.value(); + if(map.keys().contains(ssid)) { + return true; + } + } + return false; +} + + +QCoreWlanEngine::QCoreWlanEngine(QObject *parent) +: QBearerEngineImpl(parent), scanThread(0) +{ + scanThread = new QScanThread(this); + connect(scanThread, SIGNAL(networksChanged()), + this, SLOT(networksChanged())); +} + +QCoreWlanEngine::~QCoreWlanEngine() +{ + while (!foundConfigurations.isEmpty()) + delete foundConfigurations.takeFirst(); + [listener remove]; + [listener release]; +} + +void QCoreWlanEngine::initialize() +{ + QMutexLocker locker(&mutex); + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + + if([[CWInterface supportedInterfaces] count] > 0 && !listener) { + listener = [[QT_MANGLE_NAMESPACE(QNSListener) alloc] init]; + listener.engine = this; + hasWifi = true; + } else { + hasWifi = false; + } + storeSession = NULL; + + startNetworkChangeLoop(); + [autoreleasepool release]; +} + + +QString QCoreWlanEngine::getInterfaceFromId(const QString &id) +{ + QMutexLocker locker(&mutex); + + return scanThread->configurationInterface.value(id); +} + +bool QCoreWlanEngine::hasIdentifier(const QString &id) +{ + QMutexLocker locker(&mutex); + + return scanThread->configurationInterface.contains(id); +} + +void QCoreWlanEngine::connectToId(const QString &id) +{ + QMutexLocker locker(&mutex); + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + QString interfaceString = getInterfaceFromId(id); + + CWInterface *wifiInterface = + [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)]; + + if ([wifiInterface power]) { + NSError *err = nil; + NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0]; + + QString wantedSsid; + + QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); + + const QString idHash = QString::number(qHash(QLatin1String("corewlan:") + ptr->name)); + const QString idHash2 = QString::number(qHash(QLatin1String("corewlan:") + scanThread->getNetworkNameFromSsid(ptr->name))); + + bool using8021X = false; + if (idHash2 != id) { + NSArray *array = [CW8021XProfile allUser8021XProfiles]; + + for (NSUInteger i = 0; i < [array count]; ++i) { + const QString networkNameHashCheck = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef([[array objectAtIndex:i] userDefinedName])))); + + const QString ssidHash = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef([[array objectAtIndex:i] ssid])))); + + if (id == networkNameHashCheck || id == ssidHash) { + const QString thisName = scanThread->getSsidFromNetworkName(id); + if (thisName.isEmpty()) + wantedSsid = id; + else + wantedSsid = thisName; + + [params setValue: [array objectAtIndex:i] forKey:kCWAssocKey8021XProfile]; + using8021X = true; + break; + } + } + } + + if (!using8021X) { + QString wantedNetwork; + QMapIterator > i(scanThread->userProfiles); + while (i.hasNext()) { + i.next(); + wantedNetwork = i.key(); + const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork)); + if (id == networkNameHash) { + wantedSsid = scanThread->getSsidFromNetworkName(wantedNetwork); + break; + } + } + } + NSDictionary *scanParameters = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:YES], kCWScanKeyMerge, + [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType, + [NSNumber numberWithInteger:100], kCWScanKeyRestTime, + (NSString *)QCFString::toCFStringRef(wantedSsid), kCWScanKeySSID, + nil]; + + NSArray *scanArray = [wifiInterface scanForNetworksWithParameters:scanParameters error:&err]; + + if(!err) { + for(uint row=0; row < [scanArray count]; row++ ) { + CWNetwork *apNetwork = [scanArray objectAtIndex:row]; + + if(wantedSsid == QCFString::toQString(CFStringRef([apNetwork ssid]))) { + + if(!using8021X) { + SecKeychainAttribute attributes[3]; + + NSString *account = [apNetwork ssid]; + NSString *keyKind = @"AirPort network password"; + NSString *keyName = account; + + attributes[0].tag = kSecAccountItemAttr; + attributes[0].data = (void *)[account UTF8String]; + attributes[0].length = [account length]; + + attributes[1].tag = kSecDescriptionItemAttr; + attributes[1].data = (void *)[keyKind UTF8String]; + attributes[1].length = [keyKind length]; + + attributes[2].tag = kSecLabelItemAttr; + attributes[2].data = (void *)[keyName UTF8String]; + attributes[2].length = [keyName length]; + + SecKeychainAttributeList attributeList = {3,attributes}; + + SecKeychainSearchRef searchRef; + SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributeList, &searchRef); + + NSString *password = @""; + SecKeychainItemRef searchItem; + + if (SecKeychainSearchCopyNext(searchRef, &searchItem) == noErr) { + UInt32 realPasswordLength; + SecKeychainAttribute attributesW[8]; + attributesW[0].tag = kSecAccountItemAttr; + SecKeychainAttributeList listW = {1,attributesW}; + char *realPassword; + OSStatus status = SecKeychainItemCopyContent(searchItem, NULL, &listW, &realPasswordLength,(void **)&realPassword); + + if (status == noErr) { + if (realPassword != NULL) { + + QByteArray pBuf; + pBuf.resize(realPasswordLength); + pBuf.prepend(realPassword); + pBuf.insert(realPasswordLength,'\0'); + + password = [NSString stringWithUTF8String:pBuf]; + } + SecKeychainItemFreeContent(&listW, realPassword); + } + + CFRelease(searchItem); + } else { + qDebug() << "SecKeychainSearchCopyNext error"; + } + [params setValue: password forKey: kCWAssocKeyPassphrase]; + } // end using8021X + + + bool result = [wifiInterface associateToNetwork: apNetwork parameters:[NSDictionary dictionaryWithDictionary:params] error:&err]; + + if(!err) { + if(!result) { + emit connectionError(id, ConnectError); + } else { + return; + } + } else { + qDebug() <<"associate ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ])); + } + } + } //end scan network + } else { + qDebug() <<"scan ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ])); + } + emit connectionError(id, InterfaceLookupError); + } + + locker.unlock(); + emit connectionError(id, InterfaceLookupError); + [autoreleasepool release]; +} + +void QCoreWlanEngine::disconnectFromId(const QString &id) +{ + QMutexLocker locker(&mutex); + + QString interfaceString = getInterfaceFromId(id); + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + + CWInterface *wifiInterface = + [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)]; + + [wifiInterface disassociate]; + if ([[wifiInterface interfaceState]intValue] != kCWInterfaceStateInactive) { + locker.unlock(); + emit connectionError(id, DisconnectionError); + locker.relock(); + } + [autoreleasepool release]; +} + +void QCoreWlanEngine::requestUpdate() +{ + scanThread->getUserConfigurations(); + doRequestUpdate(); +} + +void QCoreWlanEngine::doRequestUpdate() +{ + QMutexLocker locker(&mutex); + + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + + NSArray *wifiInterfaces = [CWInterface supportedInterfaces]; + for (uint row = 0; row < [wifiInterfaces count]; ++row) { + scanThread->interfaceName = QCFString::toQString(CFStringRef([wifiInterfaces objectAtIndex:row])); + scanThread->start(); + } + locker.unlock(); + [autoreleasepool release]; +} + +bool QCoreWlanEngine::isWifiReady(const QString &wifiDeviceName) +{ + QMutexLocker locker(&mutex); + bool haswifi = false; + if(hasWifi) { + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + CWInterface *defaultInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(wifiDeviceName)]; + if([defaultInterface power]) { + haswifi = true; + } + [autoreleasepool release]; + } + return haswifi; +} + + +QNetworkSession::State QCoreWlanEngine::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 QCoreWlanEngine::capabilities() const +{ + return QNetworkConfigurationManager::ForcedRoaming; +} + +void QCoreWlanEngine::startNetworkChangeLoop() +{ + + SCDynamicStoreContext dynStoreContext = { 0, this/*(void *)storeSession*/, NULL, NULL, NULL }; + storeSession = SCDynamicStoreCreate(NULL, + CFSTR("networkChangeCallback"), + networkChangeCallback, + &dynStoreContext); + if (!storeSession ) { + qWarning() << "could not open dynamic store: error:" << SCErrorString(SCError()); + return; + } + + CFMutableArrayRef notificationKeys; + notificationKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFMutableArrayRef patternsArray; + patternsArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + CFStringRef storeKey; + storeKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, + kSCDynamicStoreDomainState, + kSCEntNetIPv4); + CFArrayAppendValue(notificationKeys, storeKey); + CFRelease(storeKey); + + storeKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, + kSCDynamicStoreDomainState, + kSCCompAnyRegex, + kSCEntNetIPv4); + CFArrayAppendValue(patternsArray, storeKey); + CFRelease(storeKey); + + if (!SCDynamicStoreSetNotificationKeys(storeSession , notificationKeys, patternsArray)) { + qWarning() << "register notification error:"<< SCErrorString(SCError()); + CFRelease(storeSession ); + CFRelease(notificationKeys); + CFRelease(patternsArray); + return; + } + CFRelease(notificationKeys); + CFRelease(patternsArray); + + runloopSource = SCDynamicStoreCreateRunLoopSource(NULL, storeSession , 0); + if (!runloopSource) { + qWarning() << "runloop source error:"<< SCErrorString(SCError()); + CFRelease(storeSession ); + return; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runloopSource, kCFRunLoopDefaultMode); + return; +} + +QNetworkSessionPrivate *QCoreWlanEngine::createSessionBackend() +{ + return new QNetworkSessionPrivateImpl; +} + +QNetworkConfigurationPrivatePointer QCoreWlanEngine::defaultConfiguration() +{ + return QNetworkConfigurationPrivatePointer(); +} + +bool QCoreWlanEngine::requiresPolling() const +{ + return true; +} + +void QCoreWlanEngine::networksChanged() +{ + QMutexLocker locker(&mutex); + + QStringList previous = accessPointConfigurations.keys(); + + QList foundConfigurations = scanThread->getConfigurations(); + while (!foundConfigurations.isEmpty()) { + QNetworkConfigurationPrivate *cpPriv = foundConfigurations.takeFirst(); + + previous.removeAll(cpPriv->id); + + if (accessPointConfigurations.contains(cpPriv->id)) { + QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(cpPriv->id); + + bool changed = false; + + ptr->mutex.lock(); + + if (ptr->isValid != cpPriv->isValid) { + ptr->isValid = cpPriv->isValid; + changed = true; + } + + if (ptr->name != cpPriv->name) { + ptr->name = cpPriv->name; + changed = true; + } + + if (ptr->bearerType != cpPriv->bearerType) { + ptr->bearerType = cpPriv->bearerType; + changed = true; + } + + if (ptr->state != cpPriv->state) { + ptr->state = cpPriv->state; + changed = true; + } + + ptr->mutex.unlock(); + + if (changed) { + locker.unlock(); + emit configurationChanged(ptr); + locker.relock(); + } + + delete cpPriv; + } else { + QNetworkConfigurationPrivatePointer ptr(cpPriv); + + accessPointConfigurations.insert(ptr->id, ptr); + + locker.unlock(); + emit configurationAdded(ptr); + locker.relock(); + } + } + + while (!previous.isEmpty()) { + QNetworkConfigurationPrivatePointer ptr = + accessPointConfigurations.take(previous.takeFirst()); + + locker.unlock(); + emit configurationRemoved(ptr); + locker.relock(); + } + + locker.unlock(); + emit updateCompleted(); + +} + +quint64 QCoreWlanEngine::bytesWritten(const QString &id) +{ + QMutexLocker locker(&mutex); + const QString interfaceStr = getInterfaceFromId(id); + return getBytes(interfaceStr,false); +} + +quint64 QCoreWlanEngine::bytesReceived(const QString &id) +{ + QMutexLocker locker(&mutex); + const QString interfaceStr = getInterfaceFromId(id); + return getBytes(interfaceStr,true); +} + +quint64 QCoreWlanEngine::startTime(const QString &identifier) +{ + QMutexLocker locker(&mutex); + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + quint64 timestamp = 0; + + NSString *filePath = @"/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist"; + NSDictionary* plistDict = [[[NSDictionary alloc] initWithContentsOfFile:filePath] autorelease]; + if(plistDict == nil) + return timestamp; + NSString *input = @"KnownNetworks"; + NSString *timeStampStr = @"_timeStamp"; + + NSString *ssidStr = @"SSID_STR"; + + for (id key in plistDict) { + if ([input isEqualToString:key]) { + + NSDictionary *knownNetworksDict = [plistDict objectForKey:key]; + if(knownNetworksDict == nil) + return timestamp; + for (id networkKey in knownNetworksDict) { + bool isFound = false; + NSDictionary *itemDict = [knownNetworksDict objectForKey:networkKey]; + if(itemDict == nil) + return timestamp; + NSInteger dictSize = [itemDict count]; + id objects[dictSize]; + id keys[dictSize]; + + [itemDict getObjects:objects andKeys:keys]; + bool ok = false; + for(int i = 0; i < dictSize; i++) { + if([ssidStr isEqualToString:keys[i]]) { + const QString ident = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef(objects[i])))); + if(ident == identifier) { + ok = true; + } + } + if(ok && [timeStampStr isEqualToString:keys[i]]) { + timestamp = (quint64)[objects[i] timeIntervalSince1970]; + isFound = true; + break; + } + } + if(isFound) + break; + } + } + } + [autoreleasepool release]; + return timestamp; +} + +quint64 QCoreWlanEngine::getBytes(const QString &interfaceName, bool b) +{ + struct ifaddrs *ifAddressList, *ifAddress; + struct if_data *if_data; + + quint64 bytes = 0; + ifAddressList = nil; + if(getifaddrs(&ifAddressList) == 0) { + for(ifAddress = ifAddressList; ifAddress; ifAddress = ifAddress->ifa_next) { + if(interfaceName == ifAddress->ifa_name) { + if_data = (struct if_data*)ifAddress->ifa_data; + if(b) { + bytes = if_data->ifi_ibytes; + break; + } else { + bytes = if_data->ifi_obytes; + break; + } + } + } + freeifaddrs(ifAddressList); + } + return bytes; +} + +QT_END_NAMESPACE