SoundNote/Classes/Watchers/MGMNetworkWatcher.m
2011-03-03 10:29:24 -06:00

315 lines
14 KiB
Objective-C

//
// MGMNetworkWatcher.m
// SoundNote
//
// Created by Mr. Gecko on 2/16/11.
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/
//
#import "MGMNetworkWatcher.h"
#import "MGMController.h"
#import <sys/socket.h>
#import <sys/sockio.h>
#import <sys/ioctl.h>
#import <net/if.h>
#import <net/if_media.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <unistd.h>
NSString * const MGMPrimaryInterface = @"PrimaryInterface";
NSString * const MGMAddresses = @"Addresses";
NSString * const MGMBSSID = @"BSSID";
NSString * const MGMSSID = @"SSID_STR";
NSString * const MGMCHANNEL = @"CHANNEL";
NSString * const MGMIPv4Info = @"State:/Network/Interface/%@/IPv4";
NSString * const MGMIPv6Info = @"State:/Network/Interface/%@/IPv6";
NSString * const MGMEthernetLink = @"State:/Network/Interface/en0/Link";
NSString * const MGMIPv4State = @"State:/Network/Global/IPv4";
NSString * const MGMIPv6State = @"State:/Network/Global/IPv6";
NSString * const MGMAirPortInfo = @"State:/Network/Interface/en1/AirPort";
static struct ifmedia_description ifm_subtype_ethernet_descriptions[] = IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
static struct ifmedia_description ifm_shared_option_descriptions[] = IFM_SHARED_OPTION_DESCRIPTIONS;
static void systemNotification(SCDynamicStoreRef store, NSArray *changedKeys, void *info) {
for (int i=0; i<[changedKeys count]; ++i) {
NSString *key = [changedKeys objectAtIndex:i];
if ([key isEqual:MGMEthernetLink]) {
NSDictionary *value = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)key);
[(MGMNetworkWatcher *)info ethernetLinkChanged:value];
[value release];
} else if ([key isEqual:MGMIPv4State]) {
NSDictionary *value = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)key);
[(MGMNetworkWatcher *)info ipv4Changed:value];
[value release];
} else if ([key isEqual:MGMIPv6State]) {
NSDictionary *value = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)key);
[(MGMNetworkWatcher *)info ipv6Changed:value];
[value release];
} else if ([key isEqual:MGMAirPortInfo]) {
NSDictionary *value = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)key);
[(MGMNetworkWatcher *)info airportChanged:value];
[value release];
}
}
}
@implementation MGMNetworkWatcher
- (id)init {
if ((self = [super init])) {
SCDynamicStoreContext context = {0, self, NULL, NULL, NULL};
store = SCDynamicStoreCreate(kCFAllocatorDefault, CFBundleGetIdentifier(CFBundleGetMainBundle()), (SCDynamicStoreCallBack)systemNotification, &context);
if (!store) {
NSLog(@"Unable to create store for system configuration %s", SCErrorString(SCError()));
} else {
NSArray *keys = [NSArray arrayWithObjects:MGMEthernetLink, MGMIPv4State, MGMIPv6State, MGMAirPortInfo, nil];
if (!SCDynamicStoreSetNotificationKeys(store, (CFArrayRef)keys, NULL)) {
NSLog(@"faild to set the store for notifications %s", SCErrorString(SCError()));
CFRelease(store);
store = NULL;
} else {
runLoop = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, store, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoop, kCFRunLoopDefaultMode);
[lastMediaInfo release];
lastMediaInfo = [[self getEthernetMedia] retain];
NSDictionary *IPv4State = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)MGMIPv4State);
NSDictionary *IPv4Info = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)[NSString stringWithFormat:MGMIPv4Info, [IPv4State objectForKey:MGMPrimaryInterface]]);
NSArray *addressesArray = [IPv4Info objectForKey:MGMAddresses];
if ([addressesArray count]>0) {
NSMutableString *addresses = [NSMutableString string];
for (int i=0; i<[addressesArray count]; i++) {
if (![addresses isEqual:@""])
[addresses appendString:@"\n"];
[addresses appendString:[addressesArray objectAtIndex:i]];
}
IPv4Addresses = [addresses retain];
}
[IPv4Info release];
[IPv4State release];
NSDictionary *IPv6State = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)MGMIPv6State);
NSDictionary *IPv6Info = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)[NSString stringWithFormat:MGMIPv6Info, [IPv6State objectForKey:MGMPrimaryInterface]]);
addressesArray = [IPv6Info objectForKey:MGMAddresses];
if ([addressesArray count]>0) {
NSMutableString *addresses = [NSMutableString string];
for (int i=0; i<[addressesArray count]; i++) {
if (![addresses isEqual:@""])
[addresses appendString:@"\n"];
[addresses appendString:[addressesArray objectAtIndex:i]];
}
IPv6Addresses = [addresses retain];
}
[IPv6Info release];
[IPv6State release];
lastAirPortState = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)MGMAirPortInfo);
}
}
}
return self;
}
- (void)dealloc {
if (store!=NULL) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoop, kCFRunLoopDefaultMode);
CFRelease(store);
}
[lastAirPortState release];
[super dealloc];
}
- (NSString *)getEthernetMedia {
int testSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (testSocket<0) {
NSLog(@"Can't open datagram socket");
return NULL;
}
struct ifmediareq mediaRequest;
memset(&mediaRequest, 0, sizeof(mediaRequest));
strncpy(mediaRequest.ifm_name, "en0", sizeof(mediaRequest.ifm_name));
if (ioctl(testSocket, SIOCGIFMEDIA, (caddr_t)&mediaRequest) < 0) {
close(testSocket);
return NULL;
}
close(testSocket);
const char *type = "Unknown";
struct ifmedia_description *description;
for (description = ifm_subtype_ethernet_descriptions; description->ifmt_string; description++) {
if (IFM_SUBTYPE(mediaRequest.ifm_active) == description->ifmt_word) {
type = description->ifmt_string;
break;
}
}
NSMutableString *options = [NSMutableString string];
for (description = ifm_shared_option_descriptions; description->ifmt_string; description++) {
if (mediaRequest.ifm_active & description->ifmt_word) {
if (![options isEqual:@""])
[options appendString:@","];
[options appendString:[NSString stringWithUTF8String:description->ifmt_string]];
}
}
if ([options isEqual:@""])
return [NSString stringWithUTF8String:type];
return [NSString stringWithFormat:@"%s <%@>", type, options];
}
- (void)ethernetLinkChanged:(NSDictionary *)theState {
if ([[theState objectForKey:@"Active"] boolValue]) {
[lastMediaInfo release];
lastMediaInfo = [[self getEthernetMedia] retain];
[[MGMController sharedController] startNotificationWithInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"ethernetconnected", MGMNName, @"Ethernet Connected", MGMNTitle, lastMediaInfo, MGMNDescription, [NSImage imageNamed:@"Ethernet"], MGMNIcon, nil]];
} else{
[[MGMController sharedController] startNotificationWithInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"ethernetdisconnected", MGMNName, @"Ethernet Disconnected", MGMNTitle, lastMediaInfo, MGMNDescription, [NSImage imageNamed:@"Ethernet"], MGMNIcon, nil]];
}
}
- (void)checkIPAddresses {
if (lastCheck!=nil && [lastCheck earlierDate:[NSDate dateWithTimeIntervalSinceNow:-2]]==lastCheck)
return;
[lastCheck release];
lastCheck = [NSDate new];
[connection cancel];
[connection release];
[data release];
data = [NSMutableData new];
connection = [[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://ipv4.mrgeckosmedia.net/ip.php"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5] delegate:self] retain];
[connection2 cancel];
[connection2 release];
[data2 release];
data2 = [NSMutableData new];
connection2 = [[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://ipv6.mrgeckosmedia.net/ip.php"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5] delegate:self] retain];
}
- (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)theError {
NSLog(@"%@", theError);
}
- (void)connection:(NSURLConnection *)theConnection didReceiveResponse:(NSHTTPURLResponse *)theResponse {
if (![[[theResponse MIMEType] lowercaseString] isEqual:@"text/plain"]) {
if (theConnection==connection) {
[connection cancel];
[connection release];
connection = nil;
[data release];
data = nil;
} else if (theConnection==connection2) {
[connection2 cancel];
[connection2 release];
connection2 = nil;
[data2 release];
data2 = nil;
}
}
}
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)theData {
if (theConnection==connection)
[data appendData:theData];
else if (theConnection==connection2)
[data2 appendData:theData];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)theConnection {
if (theConnection==connection) {
NSString *ip = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
[[MGMController sharedController] startNotificationWithInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"ipv4address", MGMNName, @"External IPv4 Address", MGMNTitle, ip, MGMNDescription, [NSImage imageNamed:@"Ethernet"], MGMNIcon, nil]];
[data release];
data = nil;
[connection release];
connection = nil;
} else if (theConnection==connection2) {
NSString *ip = [[[NSString alloc] initWithData:data2 encoding:NSUTF8StringEncoding] autorelease];
[[MGMController sharedController] startNotificationWithInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"ipv6address", MGMNName, @"External IPv6 Address", MGMNTitle, ip, MGMNDescription, [NSImage imageNamed:@"Ethernet"], MGMNIcon, nil]];
[data2 release];
data2 = nil;
[connection2 release];
connection2 = nil;
}
}
- (void)ipv4Changed:(NSDictionary *)theInfo {
NSDictionary *info = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)[NSString stringWithFormat:MGMIPv4Info, [theInfo objectForKey:MGMPrimaryInterface]]);
NSArray *addressesArray = [info objectForKey:MGMAddresses];
if ([addressesArray count]>0) {
NSMutableString *addresses = [NSMutableString string];
for (int i=0; i<[addressesArray count]; i++) {
if (![addresses isEqual:@""])
[addresses appendString:@"\n"];
[addresses appendString:[addressesArray objectAtIndex:i]];
}
[IPv4Addresses release];
IPv4Addresses = [addresses retain];
[[MGMController sharedController] startNotificationWithInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"ipv4acquired", MGMNName, @"IPv4 Acquired", MGMNTitle, addresses, MGMNDescription, [NSImage imageNamed:@"Ethernet"], MGMNIcon, nil]];
[self checkIPAddresses];
} else {
[[MGMController sharedController] startNotificationWithInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"ipv4released", MGMNName, @"IPv4 Released", MGMNTitle, IPv4Addresses, MGMNDescription, [NSImage imageNamed:@"Ethernet"], MGMNIcon, nil]];
}
[info release];
}
- (void)ipv6Changed:(NSDictionary *)theInfo {
NSDictionary *info = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)[NSString stringWithFormat:MGMIPv6Info, [theInfo objectForKey:MGMPrimaryInterface]]);
NSArray *addressesArray = [info objectForKey:MGMAddresses];
if ([addressesArray count]>0) {
NSMutableString *addresses = [NSMutableString string];
for (int i=0; i<[addressesArray count]; i++) {
if (![addresses isEqual:@""])
[addresses appendString:@"\n"];
[addresses appendString:[addressesArray objectAtIndex:i]];
}
[IPv6Addresses release];
IPv6Addresses = [addresses retain];
[[MGMController sharedController] startNotificationWithInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"ipv6acquired", MGMNName, @"IPv6 Acquired", MGMNTitle, addresses, MGMNDescription, [NSImage imageNamed:@"Ethernet"], MGMNIcon, nil]];
[self checkIPAddresses];
} else {
[[MGMController sharedController] startNotificationWithInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"ipv6released", MGMNName, @"IPv6 Released", MGMNTitle, IPv6Addresses, MGMNDescription, [NSImage imageNamed:@"Ethernet"], MGMNIcon, nil]];
}
[info release];
}
- (void)airportChanged:(NSDictionary *)theInfo {
if (theInfo!=nil && ![theInfo isEqual:lastAirPortState]) {
NSData *BSSID = [theInfo objectForKey:MGMBSSID];
if (![BSSID isEqual:[NSData dataWithBytes:"\x00" length:1]]) {
NSNumber *linkStatus = [theInfo objectForKey:@"Link Status"];
NSNumber *powerStatus = [theInfo objectForKey:@"Power Status"];
if (linkStatus || powerStatus) {
int status = 0;
if (linkStatus) {
status = [linkStatus intValue];
} else if (powerStatus) {
status = [powerStatus intValue];
status = !status;
}
if ([BSSID isEqual:[lastAirPortState objectForKey:MGMBSSID]] && status!=1 && [[theInfo objectForKey:@"Busy"] boolValue])
return;
if (status==1) {
NSString *SSID = [lastAirPortState objectForKey:MGMSSID];
const unsigned char *BSSIDBytes = [[lastAirPortState objectForKey:MGMBSSID] bytes];
NSString *BSSIDString = nil;
if (BSSIDBytes!=NULL)
BSSIDString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", BSSIDBytes[0], BSSIDBytes[1], BSSIDBytes[2], BSSIDBytes[3], BSSIDBytes[4], BSSIDBytes[5]];
int channel = [[[lastAirPortState objectForKey:MGMCHANNEL] objectForKey:MGMCHANNEL] intValue];
NSString *name = [NSString stringWithFormat:@"SSID: %@\nBSSID: %@\nChannel: %d", SSID, BSSIDString, channel];
[[MGMController sharedController] startNotificationWithInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"airportdisconnected", MGMNName, @"AirPort Disconnected", MGMNTitle, name, MGMNDescription, [NSImage imageNamed:@"AirPort"], MGMNIcon, nil]];
} else {
NSString *SSID = [theInfo objectForKey:MGMSSID];
const unsigned char *BSSIDBytes = [BSSID bytes];
NSString *BSSIDString = nil;
if (BSSIDBytes!=NULL)
BSSIDString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", BSSIDBytes[0], BSSIDBytes[1], BSSIDBytes[2], BSSIDBytes[3], BSSIDBytes[4], BSSIDBytes[5]];
int channel = [[[theInfo objectForKey:MGMCHANNEL] objectForKey:MGMCHANNEL] intValue];
NSString *name = [NSString stringWithFormat:@"SSID: %@\nBSSID: %@\nChannel: %d", SSID, BSSIDString, channel];
[[MGMController sharedController] startNotificationWithInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"airportconnected", MGMNName, @"AirPort Connected", MGMNTitle, name, MGMNDescription, [NSImage imageNamed:@"AirPort"], MGMNIcon, nil]];
}
[lastAirPortState release];
lastAirPortState = [theInfo retain];
}
}
}
}
@end