/core/externals/google-toolbox-for-mac/AppKit/GTMLoginItems.m
Objective C | 342 lines | 268 code | 29 blank | 45 comment | 56 complexity | 6794bcb4adac07b3bbfa11041df248f5 MD5 | raw file
1// 2// GTMLoginItems.m 3// Based on AELoginItems from DTS. 4// 5// Copyright 2007-2008 Google Inc. 6// 7// Licensed under the Apache License, Version 2.0 (the "License"); you may not 8// use this file except in compliance with the License. You may obtain a copy 9// of the License at 10// 11// http://www.apache.org/licenses/LICENSE-2.0 12// 13// Unless required by applicable law or agreed to in writing, software 14// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16// License for the specific language governing permissions and limitations under 17// the License. 18// 19 20#import "GTMLoginItems.h" 21#import "GTMDefines.h" 22 23#include <Carbon/Carbon.h> 24 25// Exposed constants 26NSString * const kGTMLoginItemsNameKey = @"Name"; 27NSString * const kGTMLoginItemsPathKey = @"Path"; 28NSString * const kGTMLoginItemsHiddenKey = @"Hide"; 29 30// kLSSharedFileListLoginItemHidden is supported on 31// 10.5, but missing from the 10.5 headers. 32// http://openradar.appspot.com/6482251 33#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 34static NSString * const kLSSharedFileListLoginItemHidden = 35 @"com.apple.loginitem.HideOnLaunch"; 36#endif // MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 37 38@interface GTMLoginItems (PrivateMethods) 39+ (NSInteger)indexOfLoginItemWithValue:(id)value 40 forKey:(NSString *)key 41 loginItems:(NSArray *)items; 42#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 43+ (LSSharedFileListRef)loginItemsFileListRef; 44+ (NSArray *)loginItemsArrayForFileListRef:(LSSharedFileListRef)fileListRef; 45#else // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 46+ (BOOL)compileAndRunScript:(NSString *)script 47 withError:(NSError **)errorInfo; 48#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 49@end 50 51@implementation GTMLoginItems (PrivateMethods) 52 53+ (NSInteger)indexOfLoginItemWithValue:(id)value 54 forKey:(NSString *)key 55 loginItems:(NSArray *)items { 56 if (!value || !key || !items) return NSNotFound; 57 NSDictionary *item = nil; 58 NSInteger found = -1; 59 GTM_FOREACH_OBJECT(item, items) { 60 ++found; 61 id itemValue = [item objectForKey:key]; 62 if (itemValue && [itemValue isEqual:value]) { 63 return found; 64 } 65 } 66 return NSNotFound; 67} 68 69#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 70 71+ (LSSharedFileListRef)loginItemsFileListRef { 72 LSSharedFileListRef loginItemsRef = 73 LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); 74 return (LSSharedFileListRef)GTMCFAutorelease(loginItemsRef); 75} 76 77+ (NSArray *)loginItemsArrayForFileListRef:(LSSharedFileListRef)fileListRef { 78 UInt32 seedValue; 79 CFArrayRef filelistArrayRef = LSSharedFileListCopySnapshot(fileListRef, 80 &seedValue); 81 return GTMCFAutorelease(filelistArrayRef); 82} 83 84#else // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 85 86+ (BOOL)compileAndRunScript:(NSString *)script 87 withError:(NSError **)errorInfo { 88 if ([script length] == 0) { 89 // COV_NF_START - no real way to test this 90 if (errorInfo) 91 *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" code:-90 userInfo:nil]; 92 return NO; 93 // COV_NF_END 94 } 95 NSAppleScript *query = [[[NSAppleScript alloc] initWithSource:script] autorelease]; 96 NSDictionary *errDict = nil; 97 if ( ![query compileAndReturnError:&errDict]) { 98 // COV_NF_START - no real way to test this 99 if (errorInfo) 100 *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" code:-91 userInfo:errDict]; 101 return NO; 102 // COV_NF_END 103 } 104 NSAppleEventDescriptor *scriptResult = [query executeAndReturnError:&errDict]; 105 if (!scriptResult) { 106 // COV_NF_START - no real way to test this 107 if (errorInfo) 108 *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" code:-92 userInfo:errDict]; 109 return NO; 110 // COV_NF_END 111 } 112 // we don't process the result 113 return YES; 114} 115 116#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 117 118@end 119 120@implementation GTMLoginItems 121 122#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 123 124+ (NSArray*)loginItems:(NSError **)errorInfo { 125 // get the login items from LaunchServices 126 LSSharedFileListRef loginItemsRef = [self loginItemsFileListRef]; 127 if (!loginItemsRef) { 128 // COV_NF_START - no real way to test this 129 if (errorInfo) { 130 *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" 131 code:-1 132 userInfo:nil]; 133 } 134 return nil; 135 // COV_NF_END 136 } 137 NSArray *fileList = [self loginItemsArrayForFileListRef:loginItemsRef]; 138 139 // build our results 140 NSMutableArray *result = [NSMutableArray array]; 141 for (id fileItem in fileList) { 142 LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)fileItem; 143 // name 144 NSMutableDictionary *item = [NSMutableDictionary dictionary]; 145 CFStringRef nameRef = LSSharedFileListItemCopyDisplayName(itemRef); 146 if (nameRef) { 147 [item setObject:[(NSString *)nameRef stringByDeletingPathExtension] 148 forKey:kGTMLoginItemsNameKey]; 149 CFRelease(nameRef); 150 } 151 // path 152 CFURLRef urlRef = NULL; 153 if (LSSharedFileListItemResolve(itemRef, 0, &urlRef, NULL) == noErr) { 154 if (urlRef) { 155 NSString *path = [(NSURL *)urlRef path]; 156 if (path) { 157 [item setObject:path forKey:kGTMLoginItemsPathKey]; 158 } 159 CFRelease(urlRef); 160 } 161 } 162 // hidden 163 CFBooleanRef hiddenRef = LSSharedFileListItemCopyProperty(itemRef, 164 (CFStringRef)kLSSharedFileListLoginItemHidden); 165 if (hiddenRef) { 166 if (hiddenRef == kCFBooleanTrue) { 167 [item setObject:[NSNumber numberWithBool:YES] 168 forKey:kGTMLoginItemsHiddenKey]; 169 } 170 CFRelease(hiddenRef); 171 } 172 [result addObject:item]; 173 } 174 175 return result; 176} 177 178#else // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 179 180+ (NSArray*)loginItems:(NSError **)errorInfo { 181 NSDictionary *errDict = nil; 182 // get the script compiled and saved off 183 static NSAppleScript *query = nil; 184 if (!query) { 185 NSString *querySource = @"tell application \"System Events\" to get properties of login items"; 186 query = [[NSAppleScript alloc] initWithSource:querySource]; 187 if ( ![query compileAndReturnError:&errDict]) { 188 // COV_NF_START - no real way to test this 189 if (errorInfo) 190 *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" code:-1 userInfo:errDict]; 191 [query release]; 192 query = nil; 193 return nil; 194 // COV_NF_END 195 } 196 } 197 // run the script 198 NSAppleEventDescriptor *scriptResult = [query executeAndReturnError:&errDict]; 199 if (!scriptResult) { 200 // COV_NF_START - no real way to test this 201 if (errorInfo) 202 *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" code:-2 userInfo:errDict]; 203 return nil; 204 // COV_NF_END 205 } 206 // build our results 207 NSMutableArray *result = [NSMutableArray array]; 208 NSInteger count = [scriptResult numberOfItems]; 209 for (NSInteger i = 0; i < count; ++i) { 210 NSAppleEventDescriptor *aeItem = [scriptResult descriptorAtIndex:i+1]; 211 NSAppleEventDescriptor *hidn = [aeItem descriptorForKeyword:kAEHidden]; 212 NSAppleEventDescriptor *nam = [aeItem descriptorForKeyword:pName]; 213 NSAppleEventDescriptor *ppth = [aeItem descriptorForKeyword:'ppth']; 214 NSMutableDictionary *item = [NSMutableDictionary dictionary]; 215 if (hidn && [hidn booleanValue]) { 216 [item setObject:[NSNumber numberWithBool:YES] forKey:kGTMLoginItemsHiddenKey]; 217 } 218 if (nam) { 219 NSString *name = [nam stringValue]; 220 if (name) { 221 [item setObject:name forKey:kGTMLoginItemsNameKey]; 222 } 223 } 224 if (ppth) { 225 NSString *path = [ppth stringValue]; 226 if (path) { 227 [item setObject:path forKey:kGTMLoginItemsPathKey]; 228 } 229 } 230 [result addObject:item]; 231 } 232 233 return result; 234} 235 236#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 237 238+ (BOOL)pathInLoginItems:(NSString *)path { 239 NSArray *loginItems = [self loginItems:nil]; 240 NSInteger itemIndex = [self indexOfLoginItemWithValue:path 241 forKey:kGTMLoginItemsPathKey 242 loginItems:loginItems]; 243 return (itemIndex != NSNotFound) ? YES : NO; 244} 245 246+ (BOOL)itemWithNameInLoginItems:(NSString *)name { 247 NSArray *loginItems = [self loginItems:nil]; 248 NSInteger itemIndex = [self indexOfLoginItemWithValue:name 249 forKey:kGTMLoginItemsNameKey 250 loginItems:loginItems]; 251 return (itemIndex != NSNotFound) ? YES : NO; 252} 253 254+ (void)addPathToLoginItems:(NSString*)path hide:(BOOL)hide { 255 if (!path) return; 256 // make sure it isn't already there 257 if ([self pathInLoginItems:path]) return; 258 // now append it 259#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 260 NSURL *url = [NSURL fileURLWithPath:path]; 261 if (url) { 262 LSSharedFileListRef loginItemsRef = [self loginItemsFileListRef]; 263 if (loginItemsRef) { 264 NSDictionary *setProperties = 265 [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:hide] 266 forKey:(id)kLSSharedFileListLoginItemHidden]; 267 LSSharedFileListItemRef itemRef = 268 LSSharedFileListInsertItemURL(loginItemsRef, 269 kLSSharedFileListItemLast, NULL, NULL, 270 (CFURLRef)url, 271 (CFDictionaryRef)setProperties, NULL); 272 if (itemRef) CFRelease(itemRef); 273 } 274 } 275#else // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 276 NSString *scriptSource = 277 [NSString stringWithFormat: 278 @"tell application \"System Events\" to make new login item with properties { path:\"%s\", hidden:%s } at end", 279 [path UTF8String], 280 (hide ? "yes" : "no")]; 281 [self compileAndRunScript:scriptSource withError:nil]; 282#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 283} 284 285+ (void)removePathFromLoginItems:(NSString*)path { 286 if ([path length] == 0) return; 287#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 288 NSURL *url = [NSURL fileURLWithPath:path]; 289 LSSharedFileListRef loginItemsRef = [self loginItemsFileListRef]; 290 if (loginItemsRef) { 291 NSArray *fileList = [self loginItemsArrayForFileListRef:loginItemsRef]; 292 for (id item in fileList) { 293 LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)item; 294 CFURLRef urlRef = NULL; 295 if (LSSharedFileListItemResolve(itemRef, 0, &urlRef, NULL) == noErr) { 296 if (urlRef) { 297 if (CFEqual(urlRef, (CFURLRef)url)) { 298 LSSharedFileListItemRemove(loginItemsRef, itemRef); 299 } 300 CFRelease(urlRef); 301 } 302 } 303 } 304 } 305#else // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 306 NSString *scriptSource = 307 [NSString stringWithFormat: 308 @"tell application \"System Events\" to delete (login items whose path is \"%s\")", 309 [path UTF8String]]; 310 [self compileAndRunScript:scriptSource withError:nil]; 311#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 312} 313 314+ (void)removeItemWithNameFromLoginItems:(NSString *)name { 315 if ([name length] == 0) return; 316#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 317 LSSharedFileListRef loginItemsRef = [self loginItemsFileListRef]; 318 if (loginItemsRef) { 319 NSArray *fileList = [self loginItemsArrayForFileListRef:loginItemsRef]; 320 for (id item in fileList) { 321 LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)item; 322 CFStringRef itemNameRef = LSSharedFileListItemCopyDisplayName(itemRef); 323 if (itemNameRef) { 324 NSString *itemName = 325 [(NSString *)itemNameRef stringByDeletingPathExtension]; 326 if ([itemName isEqual:name]) { 327 LSSharedFileListItemRemove(loginItemsRef, itemRef); 328 } 329 CFRelease(itemNameRef); 330 } 331 } 332 } 333#else // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 334 NSString *scriptSource = 335 [NSString stringWithFormat: 336 @"tell application \"System Events\" to delete (login items whose name is \"%s\")", 337 [name UTF8String]]; 338 [self compileAndRunScript:scriptSource withError:nil]; 339#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 340} 341 342@end