* We now store passwords for network servers securely instead of storing them in plain text in the Growl preferences. Heh.
* Remove unneeded code related to resolving Bonjour clients immediately, since we want to resolve them as-needed rather than storing a static IP/port.
1.1 --- a/Core/Source/GrowlBrowserEntry.h Mon Jul 28 15:23:14 2008 +0000
1.2 +++ b/Core/Source/GrowlBrowserEntry.h Mon Jul 28 21:53:02 2008 +0000
1.3 @@ -12,10 +12,12 @@
1.4
1.5 @interface GrowlBrowserEntry : NSObject {
1.6 CFMutableDictionaryRef properties;
1.7 + NSString *password;
1.8 + BOOL didPasswordLookup;
1.9 GrowlPreferencePane *owner;
1.10 }
1.11 - (id) initWithDictionary:(NSDictionary *)dict;
1.12 -- (id) initWithComputerName:(NSString *)name netService:(NSNetService *)service;
1.13 +- (id) initWithComputerName:(NSString *)name;
1.14
1.15 - (BOOL) use;
1.16 - (void) setUse:(BOOL)flag;
1.17 @@ -26,15 +28,11 @@
1.18 - (NSString *) computerName;
1.19 - (void) setComputerName:(NSString *)name;
1.20
1.21 -- (NSNetService *) netService;
1.22 -- (void) setNetService:(NSNetService *)service;
1.23 -
1.24 - (NSString *) password;
1.25 - (void) setPassword:(NSString *)password;
1.26
1.27 - (NSDictionary *) properties;
1.28
1.29 -- (void) setAddress:(NSData *)address;
1.30 - (void) setOwner:(GrowlPreferencePane *)pref;
1.31
1.32 @end
2.1 --- a/Core/Source/GrowlBrowserEntry.m Mon Jul 28 15:23:14 2008 +0000
2.2 +++ b/Core/Source/GrowlBrowserEntry.m Mon Jul 28 21:53:02 2008 +0000
2.3 @@ -10,6 +10,10 @@
2.4 #import "GrowlPreferencePane.h"
2.5 #include "CFDictionaryAdditions.h"
2.6 #include "CFMutableDictionaryAdditions.h"
2.7 +#include <Security/SecKeychain.h>
2.8 +#include <Security/SecKeychainItem.h>
2.9 +
2.10 +#define GrowlBrowserEntryKeychainServiceName "GrowlOutgoingNetworkConnection"
2.11
2.12 @implementation GrowlBrowserEntry
2.13
2.14 @@ -21,11 +25,10 @@
2.15 return self;
2.16 }
2.17
2.18 -- (id) initWithComputerName:(NSString *)name netService:(NSNetService *)service {
2.19 +- (id) initWithComputerName:(NSString *)name {
2.20 if ((self = [self init])) {
2.21 properties = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2.22 CFDictionarySetValue(properties, CFSTR("computer"), name);
2.23 - CFDictionarySetValue(properties, CFSTR("netservice"), service);
2.24 CFDictionarySetValue(properties, CFSTR("use"), kCFBooleanFalse);
2.25 CFDictionarySetValue(properties, CFSTR("active"), kCFBooleanTrue);
2.26 }
2.27 @@ -60,29 +63,76 @@
2.28 [owner writeForwardDestinations];
2.29 }
2.30
2.31 -- (NSNetService *) netService {
2.32 - return (NSNetService *)CFDictionaryGetValue(properties, CFSTR("netservice"));
2.33 +- (NSString *) password {
2.34 + if (!didPasswordLookup) {
2.35 + unsigned char *passwordChars;
2.36 + UInt32 passwordLength;
2.37 + OSStatus status;
2.38 + const char *computerNameChars = [[self computerName] UTF8String];
2.39 + status = SecKeychainFindGenericPassword(NULL,
2.40 + strlen(GrowlBrowserEntryKeychainServiceName), GrowlBrowserEntryKeychainServiceName,
2.41 + strlen(computerNameChars), computerNameChars,
2.42 + &passwordLength, (void **)&passwordChars, NULL);
2.43 + if (status == noErr) {
2.44 + password = [[NSString alloc] initWithBytes:passwordChars
2.45 + length:passwordLength
2.46 + encoding:NSUTF8StringEncoding];
2.47 + SecKeychainItemFreeContent(NULL, password);
2.48 + } else {
2.49 + if (status != errSecItemNotFound)
2.50 + NSLog(@"Failed to retrieve password for %@ from keychain. Error: %d", status);
2.51 + password = nil;
2.52 + }
2.53 +
2.54 + didPasswordLookup = YES;
2.55 + }
2.56 +
2.57 +
2.58 + return password;
2.59 }
2.60
2.61 -- (void) setNetService:(NSNetService *)service {
2.62 - CFDictionarySetValue(properties, CFSTR("netservice"), service);
2.63 -}
2.64 +- (void) setPassword:(NSString *)inPassword {
2.65 + if (password != inPassword) {
2.66 + [password release];
2.67 + password = [inPassword copy];
2.68 + }
2.69
2.70 -- (NSString *) password {
2.71 - return (NSString *)CFDictionaryGetValue(properties, CFSTR("password"));
2.72 -}
2.73 -
2.74 -- (void) setPassword:(NSString *)password {
2.75 - if (password)
2.76 - CFDictionarySetValue(properties, CFSTR("password"), password);
2.77 - else
2.78 - CFDictionaryRemoveValue(properties, CFSTR("password"));
2.79 - [owner writeForwardDestinations];
2.80 -}
2.81 -
2.82 -- (void) setAddress:(NSData *)address {
2.83 - CFDictionarySetValue(properties, CFSTR("address"), address);
2.84 - CFDictionaryRemoveValue(properties, CFSTR("netservice"));
2.85 + // Store the password to the keychain
2.86 + // XXX TODO Use AIKeychain
2.87 + const char *passwordChars = password ? [password UTF8String] : "";
2.88 + OSStatus status;
2.89 + SecKeychainItemRef itemRef = nil;
2.90 + const char *computerNameChars = [[self computerName] UTF8String];
2.91 + status = SecKeychainFindGenericPassword(NULL,
2.92 + strlen(GrowlBrowserEntryKeychainServiceName), GrowlBrowserEntryKeychainServiceName,
2.93 + strlen(computerNameChars), computerNameChars,
2.94 + NULL, NULL, &itemRef);
2.95 + if (status == errSecItemNotFound) {
2.96 + // add new item
2.97 + status = SecKeychainAddGenericPassword(NULL,
2.98 + strlen(GrowlBrowserEntryKeychainServiceName), GrowlBrowserEntryKeychainServiceName,
2.99 + strlen(computerNameChars), computerNameChars,
2.100 + strlen(passwordChars), passwordChars, NULL);
2.101 + if (status)
2.102 + NSLog(@"Failed to add password to keychain.");
2.103 + } else {
2.104 + // change existing password
2.105 + SecKeychainAttribute attrs[] = {
2.106 + { kSecAccountItemAttr, strlen(computerNameChars), (char *)computerNameChars },
2.107 + { kSecServiceItemAttr, strlen(GrowlBrowserEntryKeychainServiceName), (char *)GrowlBrowserEntryKeychainServiceName }
2.108 + };
2.109 + const SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
2.110 + status = SecKeychainItemModifyAttributesAndData(itemRef, // the item reference
2.111 + &attributes, // no change to attributes
2.112 + strlen(passwordChars), // length of password
2.113 + passwordChars // pointer to password data
2.114 + );
2.115 + if (itemRef)
2.116 + CFRelease(itemRef);
2.117 + if (status)
2.118 + NSLog(@"Failed to change password in keychain.");
2.119 + }
2.120 +
2.121 [owner writeForwardDestinations];
2.122 }
2.123
2.124 @@ -95,6 +145,8 @@
2.125 }
2.126
2.127 - (void) dealloc {
2.128 + [password release];
2.129 +
2.130 CFRelease(properties);
2.131 [super dealloc];
2.132 }
3.1 --- a/Core/Source/GrowlPreferencePane.h Mon Jul 28 15:23:14 2008 +0000
3.2 +++ b/Core/Source/GrowlPreferencePane.h Mon Jul 28 21:53:02 2008 +0000
3.3 @@ -80,7 +80,6 @@
3.4 //"Network" tab pane
3.5 NSMutableArray *services;
3.6 NSNetServiceBrowser *browser;
3.7 - NSNetService *serviceBeingResolved;
3.8 int currentServiceIndex;
3.9 IBOutlet NSArrayController *servicesArrayController;
3.10 IBOutlet NSTableColumn *servicePasswordColumn;
4.1 --- a/Core/Source/GrowlPreferencePane.m Mon Jul 28 15:23:14 2008 +0000
4.2 +++ b/Core/Source/GrowlPreferencePane.m Mon Jul 28 21:53:02 2008 +0000
4.3 @@ -478,8 +478,7 @@
4.4 NSEnumerator *enumerator = [services objectEnumerator];
4.5 GrowlBrowserEntry *entry;
4.6 while ((entry = [enumerator nextObject]))
4.7 - if (![entry netService])
4.8 - [destinations addObject:[entry properties]];
4.9 + [destinations addObject:[entry properties]];
4.10 [preferencesController setObject:destinations forKey:GrowlForwardDestinationsKey];
4.11 [destinations release];
4.12 }
4.13 @@ -949,7 +948,7 @@
4.14 return;
4.15
4.16 // add a new entry at the end
4.17 - entry = [[GrowlBrowserEntry alloc] initWithComputerName:name netService:aNetService];
4.18 + entry = [[GrowlBrowserEntry alloc] initWithComputerName:name];
4.19 [self willChangeValueForKey:@"services"];
4.20 [services addObject:entry];
4.21 [self didChangeValueForKey:@"services"];
4.22 @@ -972,52 +971,14 @@
4.23 }
4.24 }
4.25
4.26 - if (serviceBeingResolved && [serviceBeingResolved isEqual:aNetService]) {
4.27 - [serviceBeingResolved stop];
4.28 - [serviceBeingResolved release];
4.29 - serviceBeingResolved = nil;
4.30 - }
4.31 -
4.32 if (!moreComing)
4.33 [self writeForwardDestinations];
4.34 }
4.35
4.36 -- (void) netServiceDidResolveAddress:(NSNetService *)sender {
4.37 - NSArray *addresses = [sender addresses];
4.38 - if ([addresses count] > 0U) {
4.39 - NSData *address = [addresses objectAtIndex:0U];
4.40 - GrowlBrowserEntry *entry = [services objectAtIndex:currentServiceIndex];
4.41 - [entry setAddress:address];
4.42 - [self writeForwardDestinations];
4.43 - }
4.44 -}
4.45 -
4.46 #pragma mark Bonjour
4.47
4.48 - (void) resolveService:(id)sender {
4.49 - int row = [sender selectedRow];
4.50 - if (row != -1) {
4.51 - GrowlBrowserEntry *entry = [services objectAtIndex:row];
4.52 - NSNetService *serviceToResolve = [entry netService];
4.53 - if (serviceToResolve) {
4.54 - // Make sure to cancel any previous resolves.
4.55 - if (serviceBeingResolved) {
4.56 - [serviceBeingResolved stop];
4.57 - [serviceBeingResolved release];
4.58 - serviceBeingResolved = nil;
4.59 - }
4.60 -
4.61 - currentServiceIndex = row;
4.62 - serviceBeingResolved = serviceToResolve;
4.63 - [serviceBeingResolved retain];
4.64 - [serviceBeingResolved setDelegate:self];
4.65 - if ([serviceBeingResolved respondsToSelector:@selector(resolveWithTimeout:)])
4.66 - [serviceBeingResolved resolveWithTimeout:5.0];
4.67 - else
4.68 - // this selector is deprecated in 10.4
4.69 - [serviceBeingResolved resolve];
4.70 - }
4.71 - }
4.72 + NSLog(@"What calls resolveService:?");
4.73 }
4.74
4.75 - (NSMutableArray *) services {