Core/Source/GrowlPreferencesController.m
author Peter Hosey <hg@boredzo.org>
Sun Aug 02 13:03:53 2009 -0700 (2009-08-02)
changeset 4270 5c1d8f5e64c2
parent 4261 48b7c994f6c8
child 4273 b846a7b597a6
permissions -rw-r--r--
Burninating LoginItemsAE. We use LSSharedFileList now.

Also, first use of fast enumeration in Growl code! Woo!
boredzo@2434
     1
//
boredzo@2434
     2
//  GrowlPreferencesController.m
boredzo@2434
     3
//  Growl
boredzo@2434
     4
//
boredzo@2434
     5
//  Created by Nelson Elhage on 8/24/04.
boredzo@2434
     6
//  Renamed from GrowlPreferences.m by Mac-arena the Bored Zo on 2005-06-27.
ingmarstein@3040
     7
//  Copyright 2004-2006 The Growl Project. All rights reserved.
boredzo@2434
     8
//
boredzo@2434
     9
// This file is under the BSD License, refer to License.txt for details
boredzo@2434
    10
boredzo@2434
    11
boredzo@2434
    12
#import "GrowlPreferencesController.h"
boredzo@2434
    13
#import "GrowlDefinesInternal.h"
boredzo@2434
    14
#import "GrowlDefines.h"
boredzo@2461
    15
#import "GrowlPathUtilities.h"
ingmarstein@2517
    16
#import "NSStringAdditions.h"
ingmarstein@2639
    17
#include "CFURLAdditions.h"
ingmarstein@2641
    18
#include "CFDictionaryAdditions.h"
ingmarstein@2721
    19
#include "LoginItemsAE.h"
ingmarstein@2639
    20
#include <Security/SecKeychain.h>
ingmarstein@2639
    21
#include <Security/SecKeychainItem.h>
boredzo@2434
    22
ingmarstein@2517
    23
#define keychainServiceName "Growl"
ingmarstein@2517
    24
#define keychainAccountName "Growl"
ingmarstein@2517
    25
ingmarstein@2602
    26
CFTypeRef GrowlPreferencesController_objectForKey(CFTypeRef key) {
ingmarstein@2602
    27
	return [[GrowlPreferencesController sharedController] objectForKey:(id)key];
ingmarstein@2602
    28
}
ingmarstein@2602
    29
Rudy@4246
    30
CFIndex GrowlPreferencesController_integerForKey(CFTypeRef key) {
ingmarstein@2646
    31
	Boolean keyExistsAndHasValidFormat;
boredzo@4041
    32
	return CFPreferencesGetAppIntegerValue((CFStringRef)key, (CFStringRef)GROWL_HELPERAPP_BUNDLE_IDENTIFIER, &keyExistsAndHasValidFormat);
ingmarstein@2602
    33
}
ingmarstein@2602
    34
ingmarstein@2602
    35
Boolean GrowlPreferencesController_boolForKey(CFTypeRef key) {
ingmarstein@2646
    36
	Boolean keyExistsAndHasValidFormat;
boredzo@4041
    37
	return CFPreferencesGetAppBooleanValue((CFStringRef)key, (CFStringRef)GROWL_HELPERAPP_BUNDLE_IDENTIFIER, &keyExistsAndHasValidFormat);
ingmarstein@2602
    38
}
ingmarstein@2602
    39
Rudy@4246
    40
unsigned short GrowlPreferencesController_unsignedShortForKey(CFTypeRef key)
Rudy@4246
    41
{
Rudy@4246
    42
	CFIndex theIndex = GrowlPreferencesController_integerForKey(key);
Rudy@4246
    43
	
Rudy@4246
    44
	if (theIndex > USHRT_MAX)
Rudy@4246
    45
		return USHRT_MAX;
Rudy@4246
    46
	else if (theIndex < 0)
Rudy@4246
    47
		return 0;
Rudy@4246
    48
	return (unsigned short)index;
Rudy@4246
    49
}
Rudy@4246
    50
boredzo@2434
    51
@implementation GrowlPreferencesController
boredzo@2434
    52
boredzo@2496
    53
+ (GrowlPreferencesController *) sharedController {
ofri@2581
    54
	return [self sharedInstance];
ofri@2581
    55
}
ofri@2581
    56
ofri@2581
    57
- (id) initSingleton {
ofri@2581
    58
	if ((self = [super initSingleton])) {
evands@2514
    59
		[[NSDistributedNotificationCenter defaultCenter] addObserver:self
evands@2514
    60
															selector:@selector(growlPreferencesChanged:)
evands@2514
    61
																name:GrowlPreferencesChanged
ingmarstein@2591
    62
															  object:nil];
hg@4270
    63
		loginItems = LSSharedFileListCreate(kCFAllocatorDefault, kLSSharedFileListSessionLoginItems, /*options*/ NULL);
boredzo@2434
    64
	}
boredzo@2434
    65
	return self;
boredzo@2434
    66
}
boredzo@2434
    67
ofri@2581
    68
- (void) destroy {
evands@2514
    69
	[[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
hg@4270
    70
	CFRelease(loginItems);
ingmarstein@2591
    71
ofri@2581
    72
	[super destroy];
boredzo@2434
    73
}
boredzo@2434
    74
boredzo@2434
    75
#pragma mark -
boredzo@2434
    76
boredzo@2434
    77
- (void) registerDefaults:(NSDictionary *)inDefaults {
ingmarstein@2561
    78
	NSUserDefaults *helperAppDefaults = [[NSUserDefaults alloc] init];
boredzo@4041
    79
	[helperAppDefaults addSuiteNamed:GROWL_HELPERAPP_BUNDLE_IDENTIFIER];
boredzo@4041
    80
	NSDictionary *existing = [helperAppDefaults persistentDomainForName:GROWL_HELPERAPP_BUNDLE_IDENTIFIER];
boredzo@2434
    81
	if (existing) {
boredzo@2434
    82
		NSMutableDictionary *domain = [inDefaults mutableCopy];
boredzo@2434
    83
		[domain addEntriesFromDictionary:existing];
boredzo@4041
    84
		[helperAppDefaults setPersistentDomain:domain forName:GROWL_HELPERAPP_BUNDLE_IDENTIFIER];
boredzo@2434
    85
		[domain release];
boredzo@2434
    86
	} else {
boredzo@4041
    87
		[helperAppDefaults setPersistentDomain:inDefaults forName:GROWL_HELPERAPP_BUNDLE_IDENTIFIER];
boredzo@2434
    88
	}
ingmarstein@2561
    89
	[helperAppDefaults release];
ingmarstein@2561
    90
	SYNCHRONIZE_GROWL_PREFS();
boredzo@2434
    91
}
boredzo@2434
    92
boredzo@2434
    93
- (id) objectForKey:(NSString *)key {
boredzo@4041
    94
	id value = (id)CFPreferencesCopyAppValue((CFStringRef)key, (CFStringRef)GROWL_HELPERAPP_BUNDLE_IDENTIFIER);
Rudy@4261
    95
	if(value)
Rudy@4261
    96
		CFMakeCollectable(value);
ingmarstein@2561
    97
	return [value autorelease];
boredzo@2434
    98
}
boredzo@2434
    99
boredzo@2434
   100
- (void) setObject:(id)object forKey:(NSString *)key {
boredzo@2494
   101
	CFPreferencesSetAppValue((CFStringRef)key,
boredzo@2494
   102
							 (CFPropertyListRef)object,
boredzo@4041
   103
							 (CFStringRef)GROWL_HELPERAPP_BUNDLE_IDENTIFIER);
boredzo@2434
   104
ingmarstein@2561
   105
	SYNCHRONIZE_GROWL_PREFS();
boredzo@2434
   106
ingmarstein@2672
   107
	int pid = getpid();
ingmarstein@2672
   108
	CFNumberRef pidValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid);
ingmarstein@2672
   109
	CFStringRef pidKey = CFSTR("pid");
ingmarstein@2672
   110
	CFDictionaryRef userInfo = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&pidKey, (const void **)&pidValue, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
ingmarstein@2672
   111
	CFRelease(pidValue);
ingmarstein@2672
   112
	CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(),
ingmarstein@2672
   113
										 (CFStringRef)GrowlPreferencesChanged,
ingmarstein@2672
   114
										 /*object*/ key,
ingmarstein@2672
   115
										 /*userInfo*/ userInfo,
ingmarstein@2672
   116
										 /*deliverImmediately*/ false);
ingmarstein@2672
   117
	CFRelease(userInfo);
boredzo@2434
   118
}
boredzo@2434
   119
boredzo@2434
   120
- (BOOL) boolForKey:(NSString *)key {
ingmarstein@2646
   121
	return GrowlPreferencesController_boolForKey((CFTypeRef)key);
boredzo@2434
   122
}
boredzo@2434
   123
boredzo@2434
   124
- (void) setBool:(BOOL)value forKey:(NSString *)key {
boredzo@2434
   125
	NSNumber *object = [[NSNumber alloc] initWithBool:value];
boredzo@2434
   126
	[self setObject:object forKey:key];
boredzo@2434
   127
	[object release];
boredzo@2434
   128
}
boredzo@2434
   129
Rudy@4246
   130
- (CFIndex) integerForKey:(NSString *)key {
ingmarstein@2646
   131
	return GrowlPreferencesController_integerForKey((CFTypeRef)key);
boredzo@2434
   132
}
boredzo@2434
   133
Rudy@4246
   134
- (void) setInteger:(CFIndex)value forKey:(NSString *)key {
Rudy@4246
   135
#ifdef __LP64__
Rudy@4246
   136
	NSNumber *object = [[NSNumber alloc] initWithInteger:value];
Rudy@4246
   137
#else
boredzo@2434
   138
	NSNumber *object = [[NSNumber alloc] initWithInt:value];
Rudy@4246
   139
#endif
boredzo@2434
   140
	[self setObject:object forKey:key];
boredzo@2434
   141
	[object release];
boredzo@2434
   142
}
boredzo@2434
   143
Rudy@4246
   144
- (unsigned short)unsignedShortForKey:(NSString *)key
Rudy@4246
   145
{
Rudy@4246
   146
	return GrowlPreferencesController_unsignedShortForKey((CFTypeRef)key);
Rudy@4246
   147
}
Rudy@4246
   148
Rudy@4246
   149
Rudy@4246
   150
- (void)setUnsignedShort:(unsigned short)theShort forKey:(NSString *)key
Rudy@4246
   151
{
Rudy@4246
   152
	[self setObject:[NSNumber numberWithUnsignedShort:theShort] forKey:key];
Rudy@4246
   153
}
Rudy@4246
   154
boredzo@2434
   155
- (void) synchronize {
boredzo@2434
   156
	SYNCHRONIZE_GROWL_PREFS();
boredzo@2434
   157
}
boredzo@2434
   158
boredzo@2434
   159
#pragma mark -
boredzo@2434
   160
#pragma mark Start-at-login control
boredzo@2434
   161
boredzo@2496
   162
- (BOOL) shouldStartGrowlAtLogin {
ingmarstein@3011
   163
	Boolean    foundIt = false;
boredzo@2434
   164
boredzo@2434
   165
	//get the prefpane bundle and find GHA within it.
evands@3619
   166
	NSString *pathToGHA      = [[NSBundle bundleWithIdentifier:GROWL_PREFPANE_BUNDLE_IDENTIFIER] pathForResource:@"GrowlHelperApp" ofType:@"app"];
ingmarstein@2721
   167
	//get the file url to GHA.
ingmarstein@2721
   168
	CFURLRef urlToGHA = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)pathToGHA, kCFURLPOSIXPathStyle, true);
ingmarstein@2721
   169
hg@4270
   170
	UInt32 seed = 0U;
hg@4270
   171
	NSArray *currentLoginItems = [NSMakeCollectable(LSSharedFileListCopySnapshot(loginItems, &seed)) autorelease];
hg@4270
   172
	for (id itemObject in currentLoginItems) {
hg@4270
   173
		LSSharedFileListItemRef item = (LSSharedFileListItemRef)itemObject;
hg@4270
   174
hg@4270
   175
		UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
hg@4270
   176
		CFURLRef URL = NULL;
hg@4270
   177
		OSStatus err = LSSharedFileListItemResolve(item, resolutionFlags, &URL, /*outRef*/ NULL);
hg@4270
   178
		if (err == noErr) {
hg@4270
   179
			foundIt = CFEqual(URL, urlToGHA);
hg@4270
   180
			CFRelease(URL);
hg@4270
   181
ingmarstein@3011
   182
			if (foundIt)
ingmarstein@3011
   183
				break;
ingmarstein@3011
   184
		}
ingmarstein@3011
   185
	}
ingmarstein@2721
   186
ingmarstein@2721
   187
	CFRelease(urlToGHA);
boredzo@2434
   188
boredzo@2434
   189
	return foundIt;
boredzo@2434
   190
}
boredzo@2434
   191
boredzo@2496
   192
- (void) setShouldStartGrowlAtLogin:(BOOL)flag {
boredzo@2434
   193
	//get the prefpane bundle and find GHA within it.
evands@3619
   194
	NSString *pathToGHA = [[NSBundle bundleWithIdentifier:GROWL_PREFPANE_BUNDLE_IDENTIFIER] pathForResource:@"GrowlHelperApp" ofType:@"app"];
boredzo@2434
   195
	[self setStartAtLogin:pathToGHA enabled:flag];
boredzo@2434
   196
}
boredzo@2434
   197
evands@3616
   198
- (void) setStartAtLogin:(NSString *)path enabled:(BOOL)enabled {
ingmarstein@2721
   199
	OSStatus status;
hg@4270
   200
	CFURLRef URLToToggle = (CFURLRef)[NSURL fileURLWithPath:path];
hg@4270
   201
	LSSharedFileListItemRef existingItem = NULL;
hg@4270
   202
hg@4270
   203
	UInt32 seed = 0U;
hg@4270
   204
	NSArray *currentLoginItems = [NSMakeCollectable(LSSharedFileListCopySnapshot(loginItems, &seed)) autorelease];
hg@4270
   205
	for (id itemObject in currentLoginItems) {
hg@4270
   206
		LSSharedFileListItemRef item = (LSSharedFileListItemRef)itemObject;
hg@4270
   207
hg@4270
   208
		UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
hg@4270
   209
		CFURLRef URL = NULL;
hg@4270
   210
		OSStatus err = LSSharedFileListItemResolve(item, resolutionFlags, &URL, /*outRef*/ NULL);
hg@4270
   211
		if (err == noErr) {
hg@4270
   212
			Boolean foundIt = CFEqual(URL, URLToToggle);
hg@4270
   213
			CFRelease(URL);
hg@4270
   214
hg@4270
   215
			if (foundIt) {
hg@4270
   216
				existingItem = item;
evands@3616
   217
				break;
evands@3616
   218
			}
evands@3616
   219
		}
evands@3616
   220
	}
evands@3616
   221
hg@4270
   222
	if (enabled && (existingItem == NULL)) {
hg@4270
   223
		NSString *displayName = [[NSFileManager defaultManager] displayNameAtPath:path];
hg@4270
   224
		IconRef icon = NULL;
hg@4270
   225
		FSRef ref;
hg@4270
   226
		Boolean gotRef = CFURLGetFSRef(URLToToggle, &ref);
hg@4270
   227
		if (gotRef) {
hg@4270
   228
			status = GetIconRefFromFileInfo(&ref,
hg@4270
   229
											/*fileNameLength*/ 0, /*fileName*/ NULL,
hg@4270
   230
											kFSCatInfoNone, /*catalogInfo*/ NULL,
hg@4270
   231
											kIconServicesNormalUsageFlag,
hg@4270
   232
											&icon,
hg@4270
   233
											/*outLabel*/ NULL);
hg@4270
   234
			if (status != noErr)
hg@4270
   235
				icon = NULL;
hg@4270
   236
		}
hg@4270
   237
hg@4270
   238
		LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, (CFStringRef)displayName, icon, URLToToggle, /*propertiesToSet*/ NULL, /*propertiesToClear*/ NULL);
hg@4270
   239
	} else if (!enabled && (existingItem != NULL))
hg@4270
   240
		LSSharedFileListItemRemove(loginItems, existingItem);
boredzo@2434
   241
}
boredzo@2434
   242
boredzo@2434
   243
#pragma mark -
boredzo@2496
   244
#pragma mark GrowlMenu running state
boredzo@2496
   245
boredzo@2496
   246
- (void) enableGrowlMenu {
ingmarstein@2517
   247
	NSBundle *bundle = [NSBundle bundleForClass:[GrowlPreferencesController class]];
ingmarstein@2517
   248
	NSString *growlMenuPath = [bundle pathForResource:@"GrowlMenu" ofType:@"app"];
boredzo@2496
   249
	NSURL *growlMenuURL = [NSURL fileURLWithPath:growlMenuPath];
boredzo@2496
   250
	[[NSWorkspace sharedWorkspace] openURLs:[NSArray arrayWithObject:growlMenuURL]
boredzo@2496
   251
	                withAppBundleIdentifier:nil
boredzo@2496
   252
	                                options:NSWorkspaceLaunchWithoutAddingToRecents | NSWorkspaceLaunchWithoutActivation | NSWorkspaceLaunchAsync
boredzo@2496
   253
	         additionalEventParamDescriptor:nil
boredzo@2496
   254
	                      launchIdentifiers:NULL];
boredzo@2496
   255
}
boredzo@2496
   256
boredzo@2496
   257
- (void) disableGrowlMenu {
boredzo@2496
   258
	// Ask GrowlMenu to shutdown via the DNC
ingmarstein@2672
   259
	CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(),
ingmarstein@2672
   260
										 CFSTR("GrowlMenuShutdown"),
ingmarstein@2672
   261
										 /*object*/ NULL,
ingmarstein@2672
   262
										 /*userInfo*/ NULL,
ingmarstein@2672
   263
										 /*deliverImmediately*/ false);
boredzo@2496
   264
}
boredzo@2496
   265
boredzo@2496
   266
#pragma mark -
boredzo@2434
   267
#pragma mark Growl running state
boredzo@2434
   268
boredzo@2434
   269
- (void) setGrowlRunning:(BOOL)flag noMatterWhat:(BOOL)nmw {
boredzo@2434
   270
	// Store the desired running-state of the helper app for use by GHA.
boredzo@2434
   271
	[self setBool:flag forKey:GrowlEnabledKey];
boredzo@2434
   272
boredzo@2434
   273
	//now launch or terminate as appropriate.
boredzo@2434
   274
	if (flag)
boredzo@2434
   275
		[self launchGrowl:nmw];
boredzo@2434
   276
	else
boredzo@2434
   277
		[self terminateGrowl];
boredzo@2434
   278
}
boredzo@2434
   279
boredzo@2434
   280
- (BOOL) isRunning:(NSString *)theBundleIdentifier {
boredzo@2434
   281
	BOOL isRunning = NO;
boredzo@2434
   282
	ProcessSerialNumber PSN = { kNoProcess, kNoProcess };
boredzo@2434
   283
boredzo@2434
   284
	while (GetNextProcess(&PSN) == noErr) {
boredzo@2434
   285
		NSDictionary *infoDict = (NSDictionary *)ProcessInformationCopyDictionary(&PSN, kProcessDictionaryIncludeAllInformationMask);
Rudy@4261
   286
		if(infoDict) {
Rudy@4261
   287
			NSString *bundleID = [infoDict objectForKey:(NSString *)kCFBundleIdentifierKey];
Rudy@4261
   288
			isRunning = bundleID && [bundleID isEqualToString:theBundleIdentifier];
Rudy@4261
   289
			CFMakeCollectable(infoDict);
Rudy@4261
   290
			[infoDict release];
Rudy@4261
   291
		}
boredzo@2434
   292
		if (isRunning)
boredzo@2434
   293
			break;
boredzo@2434
   294
	}
boredzo@2434
   295
boredzo@2434
   296
	return isRunning;
boredzo@2434
   297
}
boredzo@2434
   298
boredzo@2434
   299
- (BOOL) isGrowlRunning {
boredzo@2434
   300
	return [self isRunning:@"com.Growl.GrowlHelperApp"];
boredzo@2434
   301
}
boredzo@2434
   302
boredzo@2434
   303
- (void) launchGrowl:(BOOL)noMatterWhat {
boredzo@2677
   304
	NSString *helperPath = [[GrowlPathUtilities helperAppBundle] bundlePath];
boredzo@2496
   305
	NSURL *helperURL = [NSURL fileURLWithPath:helperPath];
boredzo@2496
   306
boredzo@2496
   307
	unsigned options = NSWorkspaceLaunchWithoutAddingToRecents | NSWorkspaceLaunchWithoutActivation | NSWorkspaceLaunchAsync;
boredzo@2496
   308
	if (noMatterWhat)
boredzo@2496
   309
		options |= NSWorkspaceLaunchNewInstance;
boredzo@2496
   310
	[[NSWorkspace sharedWorkspace] openURLs:[NSArray arrayWithObject:helperURL]
boredzo@2496
   311
	                withAppBundleIdentifier:nil
boredzo@2496
   312
	                                options:options
boredzo@2496
   313
	         additionalEventParamDescriptor:nil
boredzo@2496
   314
	                      launchIdentifiers:NULL];
boredzo@2434
   315
}
boredzo@2434
   316
boredzo@2434
   317
- (void) terminateGrowl {
boredzo@2434
   318
	// Ask the Growl Helper App to shutdown via the DNC
ingmarstein@2672
   319
	CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(),
ingmarstein@2672
   320
										 (CFStringRef)GROWL_SHUTDOWN,
ingmarstein@2672
   321
										 /*object*/ NULL,
ingmarstein@2672
   322
										 /*userInfo*/ NULL,
ingmarstein@2672
   323
										 /*deliverImmediately*/ false);
boredzo@2434
   324
}
boredzo@2434
   325
boredzo@2496
   326
#pragma mark -
boredzo@2496
   327
//Simplified accessors
boredzo@2496
   328
boredzo@2496
   329
#pragma mark UI
boredzo@2496
   330
Rudy@4246
   331
- (CFIndex)selectedPosition {
bgannin@3317
   332
	return [self integerForKey:GROWL_POSITION_PREFERENCE_KEY];
bgannin@3317
   333
}
bgannin@3317
   334
boredzo@2496
   335
- (BOOL) isBackgroundUpdateCheckEnabled {
boredzo@2496
   336
	return [self boolForKey:GrowlUpdateCheckKey];
boredzo@2496
   337
}
boredzo@2496
   338
- (void) setIsBackgroundUpdateCheckEnabled:(BOOL)flag {
boredzo@2496
   339
	[self setBool:flag forKey:GrowlUpdateCheckKey];
boredzo@2496
   340
}
boredzo@2496
   341
boredzo@2496
   342
- (NSString *) defaultDisplayPluginName {
boredzo@2496
   343
	return [self objectForKey:GrowlDisplayPluginKey];
boredzo@2496
   344
}
boredzo@2496
   345
- (void) setDefaultDisplayPluginName:(NSString *)name {
boredzo@2496
   346
	[self setObject:name forKey:GrowlDisplayPluginKey];
boredzo@2496
   347
}
boredzo@2496
   348
boredzo@2496
   349
- (BOOL) squelchMode {
boredzo@2496
   350
	return [self boolForKey:GrowlSquelchModeKey];
boredzo@2496
   351
}
boredzo@2496
   352
- (void) setSquelchMode:(BOOL)flag {
boredzo@2496
   353
	[self setBool:flag forKey:GrowlSquelchModeKey];
boredzo@2496
   354
}
boredzo@2496
   355
boredzo@2496
   356
- (BOOL) stickyWhenAway {
boredzo@2496
   357
	return [self boolForKey:GrowlStickyWhenAwayKey];
boredzo@2496
   358
}
boredzo@2496
   359
- (void) setStickyWhenAway:(BOOL)flag {
boredzo@2496
   360
	[self setBool:flag forKey:GrowlStickyWhenAwayKey];
boredzo@2496
   361
}
boredzo@2496
   362
rudy@2968
   363
- (NSNumber*) idleThreshold {
Rudy@4246
   364
#ifdef __LP64__
Rudy@4246
   365
	return [NSNumber numberWithInteger:[self integerForKey:GrowlStickyIdleThresholdKey]];
Rudy@4246
   366
#else
rudy@2968
   367
	return [NSNumber numberWithInt:[self integerForKey:GrowlStickyIdleThresholdKey]];
Rudy@4246
   368
#endif
rudy@2968
   369
}
rudy@2968
   370
rudy@2968
   371
- (void) setIdleThreshold:(NSNumber*)value {
rudy@2968
   372
	[self setInteger:[value intValue] forKey:GrowlStickyIdleThresholdKey];
rudy@2968
   373
}
boredzo@2496
   374
#pragma mark Status Item
boredzo@2496
   375
ingmarstein@2540
   376
- (BOOL) isGrowlMenuEnabled {
boredzo@2496
   377
	return [self boolForKey:GrowlMenuExtraKey];
boredzo@2496
   378
}
boredzo@2496
   379
boredzo@2496
   380
- (void) setGrowlMenuEnabled:(BOOL)state {
ingmarstein@2540
   381
	if (state != [self isGrowlMenuEnabled]) {
boredzo@2496
   382
		[self setBool:state forKey:GrowlMenuExtraKey];
boredzo@2496
   383
		if (state)
boredzo@2496
   384
			[self enableGrowlMenu];
boredzo@2496
   385
		else
boredzo@2496
   386
			[self disableGrowlMenu];
boredzo@2496
   387
	}
boredzo@2496
   388
}
boredzo@2496
   389
boredzo@2496
   390
#pragma mark Logging
boredzo@2496
   391
boredzo@2496
   392
- (BOOL) loggingEnabled {
ingmarstein@2635
   393
	return [self boolForKey:GrowlLoggingEnabledKey];
boredzo@2496
   394
}
boredzo@2496
   395
boredzo@2496
   396
- (void) setLoggingEnabled:(BOOL)flag {
ingmarstein@2635
   397
	[self setBool:flag forKey:GrowlLoggingEnabledKey];
ingmarstein@2635
   398
}
boredzo@2496
   399
boredzo@2496
   400
- (BOOL) isGrowlServerEnabled {
ingmarstein@2635
   401
	return [self boolForKey:GrowlStartServerKey];
boredzo@2496
   402
}
boredzo@2496
   403
boredzo@2496
   404
- (void) setGrowlServerEnabled:(BOOL)enabled {
ingmarstein@2635
   405
	[self setBool:enabled forKey:GrowlStartServerKey];
boredzo@2496
   406
}
boredzo@2496
   407
boredzo@2496
   408
#pragma mark Remote Growling
boredzo@2496
   409
boredzo@2496
   410
- (BOOL) isRemoteRegistrationAllowed {
ingmarstein@2635
   411
	return [self boolForKey:GrowlRemoteRegistrationKey];
boredzo@2496
   412
}
boredzo@2496
   413
boredzo@2496
   414
- (void) setRemoteRegistrationAllowed:(BOOL)flag {
ingmarstein@2635
   415
	[self setBool:flag forKey:GrowlRemoteRegistrationKey];
boredzo@2496
   416
}
boredzo@2496
   417
boredzo@2496
   418
- (NSString *) remotePassword {
ingmarstein@2634
   419
	unsigned char *password;
boredzo@2496
   420
	UInt32 passwordLength;
boredzo@2496
   421
	OSStatus status;
boredzo@2496
   422
	status = SecKeychainFindGenericPassword(NULL,
Rudy@4246
   423
											(UInt32)strlen(keychainServiceName), keychainServiceName,
Rudy@4246
   424
											(UInt32)strlen(keychainAccountName), keychainAccountName,
boredzo@2496
   425
											&passwordLength, (void **)&password, NULL);
boredzo@2496
   426
boredzo@2496
   427
	NSString *passwordString;
boredzo@2496
   428
	if (status == noErr) {
ingmarstein@2634
   429
		passwordString = (NSString *)CFStringCreateWithBytes(kCFAllocatorDefault, password, passwordLength, kCFStringEncodingUTF8, false);
Rudy@4261
   430
		if(passwordString) {
Rudy@4261
   431
			CFMakeCollectable(passwordString);
Rudy@4261
   432
			[passwordString autorelease];
Rudy@4261
   433
			SecKeychainItemFreeContent(NULL, password);
Rudy@4261
   434
		}
boredzo@2496
   435
	} else {
boredzo@2496
   436
		if (status != errSecItemNotFound)
boredzo@2496
   437
			NSLog(@"Failed to retrieve password from keychain. Error: %d", status);
boredzo@2496
   438
		passwordString = @"";
boredzo@2496
   439
	}
boredzo@2496
   440
boredzo@2496
   441
	return passwordString;
boredzo@2496
   442
}
boredzo@2496
   443
boredzo@2496
   444
- (void) setRemotePassword:(NSString *)value {
boredzo@2496
   445
	const char *password = value ? [value UTF8String] : "";
Rudy@4246
   446
	size_t length = strlen(password);
boredzo@2496
   447
	OSStatus status;
boredzo@2496
   448
	SecKeychainItemRef itemRef = nil;
boredzo@2496
   449
	status = SecKeychainFindGenericPassword(NULL,
Rudy@4246
   450
											(UInt32)strlen(keychainServiceName), keychainServiceName,
Rudy@4246
   451
											(UInt32)strlen(keychainAccountName), keychainAccountName,
boredzo@2496
   452
											NULL, NULL, &itemRef);
boredzo@2496
   453
	if (status == errSecItemNotFound) {
boredzo@2496
   454
		// add new item
boredzo@2496
   455
		status = SecKeychainAddGenericPassword(NULL,
Rudy@4246
   456
											   (UInt32)strlen(keychainServiceName), keychainServiceName,
Rudy@4246
   457
											   (UInt32)strlen(keychainAccountName), keychainAccountName,
Rudy@4246
   458
											   (UInt32)length, password, NULL);
boredzo@2496
   459
		if (status)
boredzo@2496
   460
			NSLog(@"Failed to add password to keychain.");
boredzo@2496
   461
	} else {
boredzo@2496
   462
		// change existing password
boredzo@2496
   463
		SecKeychainAttribute attrs[] = {
Rudy@4246
   464
			{ kSecAccountItemAttr, (UInt32)strlen(keychainAccountName), (char *)keychainAccountName },
Rudy@4246
   465
			{ kSecServiceItemAttr, (UInt32)strlen(keychainServiceName), (char *)keychainServiceName }
boredzo@2496
   466
		};
Rudy@4246
   467
		const SecKeychainAttributeList attributes = { (UInt32)sizeof(attrs) / (UInt32)sizeof(attrs[0]), attrs };
boredzo@2496
   468
		status = SecKeychainItemModifyAttributesAndData(itemRef,		// the item reference
boredzo@2496
   469
														&attributes,	// no change to attributes
Rudy@4246
   470
														(UInt32)length,			// length of password
boredzo@2496
   471
														password		// pointer to password data
boredzo@2496
   472
														);
boredzo@2496
   473
		if (itemRef)
boredzo@2496
   474
			CFRelease(itemRef);
boredzo@2496
   475
		if (status)
boredzo@2496
   476
			NSLog(@"Failed to change password in keychain.");
boredzo@2496
   477
	}
boredzo@2496
   478
}
boredzo@2496
   479
Rudy@4246
   480
- (unsigned short) UDPPort {
Rudy@4246
   481
	return [self unsignedShortForKey:GrowlUDPPortKey];
Rudy@4246
   482
}
Rudy@4246
   483
- (void) setUDPPort:(unsigned short)value {
Rudy@4246
   484
	[self setUnsignedShort:value forKey:GrowlUDPPortKey];
boredzo@2496
   485
}
boredzo@2496
   486
boredzo@2496
   487
- (BOOL) isForwardingEnabled {
ingmarstein@2635
   488
	return [self boolForKey:GrowlEnableForwardKey];
boredzo@2496
   489
}
boredzo@2496
   490
- (void) setForwardingEnabled:(BOOL)enabled {
ingmarstein@2635
   491
	[self setBool:enabled forKey:GrowlEnableForwardKey];
boredzo@2496
   492
}
boredzo@2496
   493
evands@2514
   494
#pragma mark -
evands@2514
   495
/*
evands@2514
   496
 * @brief Growl preferences changed
evands@2514
   497
 *
evands@2514
   498
 * Synchronize our NSUserDefaults to immediately get any changes from the disk
evands@2514
   499
 */
ingmarstein@2517
   500
- (void) growlPreferencesChanged:(NSNotification *)notification {
evands@4016
   501
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
evands@4016
   502
ingmarstein@2672
   503
	NSString *object = [notification object];
ingmarstein@2672
   504
//	NSLog(@"%s: %@\n", __func__, object);
ingmarstein@2672
   505
	SYNCHRONIZE_GROWL_PREFS();
ingmarstein@2672
   506
	if (!object || [object isEqualToString:GrowlDisplayPluginKey]) {
ingmarstein@2672
   507
		[self willChangeValueForKey:@"defaultDisplayPluginName"];
ingmarstein@2672
   508
		[self didChangeValueForKey:@"defaultDisplayPluginName"];
ingmarstein@2672
   509
	}
ingmarstein@2672
   510
	if (!object || [object isEqualToString:GrowlSquelchModeKey]) {
ingmarstein@2672
   511
		[self willChangeValueForKey:@"squelchMode"];
ingmarstein@2672
   512
		[self didChangeValueForKey:@"squelchMode"];
ingmarstein@2672
   513
	}
ingmarstein@2672
   514
	if (!object || [object isEqualToString:GrowlMenuExtraKey]) {
ingmarstein@2672
   515
		[self willChangeValueForKey:@"growlMenuEnabled"];
ingmarstein@2672
   516
		[self didChangeValueForKey:@"growlMenuEnabled"];
ingmarstein@2672
   517
	}
ingmarstein@2672
   518
	if (!object || [object isEqualToString:GrowlEnableForwardKey]) {
ingmarstein@2672
   519
		[self willChangeValueForKey:@"forwardingEnabled"];
ingmarstein@2672
   520
		[self didChangeValueForKey:@"forwardingEnabled"];
ingmarstein@2672
   521
	}
ingmarstein@2672
   522
	if (!object || [object isEqualToString:GrowlUpdateCheckKey]) {
ingmarstein@2672
   523
		[self willChangeValueForKey:@"backgroundUpdateCheckEnabled"];
ingmarstein@2672
   524
		[self didChangeValueForKey:@"backgroundUpdateCheckEnabled"];
ingmarstein@2672
   525
	}
ingmarstein@2672
   526
	if (!object || [object isEqualToString:GrowlStickyWhenAwayKey]) {
ingmarstein@2672
   527
		[self willChangeValueForKey:@"stickyWhenAway"];
ingmarstein@2672
   528
		[self didChangeValueForKey:@"stickyWhenAway"];
ingmarstein@2672
   529
	}
rudy@2968
   530
	if (!object || [object isEqualToString:GrowlStickyIdleThresholdKey]) {
rudy@2968
   531
		[self willChangeValueForKey:@"idleThreshold"];
rudy@2968
   532
		[self didChangeValueForKey:@"idleThreshold"];
rudy@2968
   533
	}
ingmarstein@2672
   534
	if (!object || [object isEqualToString:GrowlRemoteRegistrationKey]) {
ingmarstein@2672
   535
		[self willChangeValueForKey:@"remoteRegistrationAllowed"];
ingmarstein@2672
   536
		[self didChangeValueForKey:@"remoteRegistrationAllowed"];
ingmarstein@2672
   537
	}
evands@4016
   538
	
evands@4016
   539
	[pool release];
evands@2514
   540
}
boredzo@2496
   541
boredzo@2434
   542
@end