Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

additional fixes/tweaks:

* removed `LSRegisterURL`. In a dev forum discussion an apple employee admitted the documentation was incorrect and that `LSRegisterURL` should _never_ be called by a sandboxed application.
* replaced startAtLogin getter with one that's more sandbox friendly, if slightly slower and messier
* added semi-cached 'enabled' property that's more intuitive to track via KVO. `startAtLogin` and `setStartAtLogin:` save state to this property as it changes, allowing one to instead track `enabled` and avoid API calls altogether on read. writing to `enabled` simply calls `setStartAtLogin:`.
* inform KVO of the non-automatic behavior for properties 'startAtLogin' and 'enabled'
* remove potentially non-sandbox-safe `remove` method.
* re-indent everything like the whitespace nazi i am
* figured I might as well add my name to the copyright at this point
  • Loading branch information...
commit 2b3e69f838c77b3a56e28b69a83af19568670894 1 parent 81dc9d1
@ttilley ttilley authored
Showing with 121 additions and 65 deletions.
  1. +22 −19 StartAtLoginController.h
  2. +99 −46 StartAtLoginController.m
View
41 StartAtLoginController.h
@@ -1,37 +1,40 @@
// Copyright (c) 2011 Alex Zielenski
+// Copyright (c) 2012 Travis Tilley
// All Rights Reserved
//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
-// The above copyright notice and this permission notice shall be
+// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
@interface StartAtLoginController : NSObject {
- NSString *_identifier;
- NSURL *_url;
+ NSString *_identifier;
+ NSURL *_url;
+ BOOL _enabled;
}
-@property (assign, nonatomic) BOOL startAtLogin;
-@property (copy, atomic) NSString *identifier;
-@property (copy, atomic) NSURL *url;
+@property (assign, nonatomic, readwrite) BOOL startAtLogin;
+@property (assign, nonatomic, readwrite) BOOL enabled;
+@property (copy, nonatomic, readwrite) NSString *identifier;
+@property (copy, nonatomic, readwrite) NSURL *url;
+- (id)initWithBundle:(NSBundle*)bndl;
- (void)setBundle:(NSBundle*)bndl;
-- (void)remove;
@end
View
145 StartAtLoginController.m
@@ -1,23 +1,24 @@
// Copyright (c) 2011 Alex Zielenski
+// Copyright (c) 2012 Travis Tilley
// All Rights Reserved
//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
-// The above copyright notice and this permission notice shall be
+// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "StartAtLoginController.h"
@@ -27,56 +28,108 @@ @implementation StartAtLoginController
@synthesize identifier = _identifier;
@synthesize url = _url;
-@dynamic startAtLogin;
#if !__has_feature(objc_arc)
- (void)dealloc {
- self.identifier = nil;
- self.url = nil;
- [super dealloc];
+ self.identifier = nil;
+ self.url = nil;
+ [super dealloc];
+}
+#endif
+
++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
+ BOOL automatic = NO;
+
+ if ([theKey isEqualToString:@"startAtLogin"]) {
+ automatic = NO;
+ } else if ([theKey isEqualToString:@"enabled"]) {
+ automatic = NO;
+ } else {
+ automatic=[super automaticallyNotifiesObserversForKey:theKey];
+ }
+
+ return automatic;
}
+
+-(id)initWithBundle:(NSBundle*)bndl
+{
+ self = [super init];
+ if (self) {
+ _enabled = NO;
+ [self setBundle:bndl];
+
+ // this method call initializes _enabled to the correct value as a side effect.
+ [self startAtLogin];
+#if !defined(NDEBUG)
+ NSLog(@"Launcher '%@' %@ configured to start at login",
+ self.identifier, (_enabled ? @"is" : @"is not"));
#endif
+ }
+ return self;
+}
- (void)setBundle:(NSBundle*)bndl {
- self.identifier = [bndl bundleIdentifier];
- self.url = [bndl bundleURL];
+ self.identifier = [bndl bundleIdentifier];
+ self.url = [bndl bundleURL];
}
- (BOOL)startAtLogin {
- if (!_identifier)
- return NO;
+ if (!_identifier)
+ return NO;
- CFDictionaryRef cfdict = SMJobCopyDictionary(kSMDomainUserLaunchd, (__bridge CFStringRef)_identifier);
- NSDictionary *dict = (NSDictionary*)CFBridgingRelease(cfdict);
- BOOL contains = (dict!=NULL);
- return contains;
+ BOOL isEnabled = NO;
+
+ // the easy and sane method (SMJobCopyDictionary) can pose problems when sandboxed. -_-
+ CFArrayRef cfJobDicts = SMCopyAllJobDictionaries(kSMDomainUserLaunchd);
+ NSArray* jobDicts = CFBridgingRelease(cfJobDicts);
+
+ if (jobDicts && [jobDicts count] > 0) {
+ for (NSDictionary* job in jobDicts) {
+ if ([_identifier isEqualToString:[job objectForKey:@"Label"]]) {
+ isEnabled = [[job objectForKey:@"OnDemand"] boolValue];
+ break;
+ }
+ }
+ }
+
+ if (isEnabled != _enabled) {
+ [self willChangeValueForKey:@"enabled"];
+ _enabled = isEnabled;
+ [self didChangeValueForKey:@"enabled"];
+ }
+
+ return isEnabled;
}
- (void)setStartAtLogin:(BOOL)flag {
- if (!_identifier||!_url)
- return;
- [self willChangeValueForKey:@"startAtLogin"];
- // Let ServiceManagement know that we exist
- if (LSRegisterURL((__bridge CFURLRef)_url, true) != noErr) {
- NSLog(@"LSRegisterURL failed!");
- }
+ if (!_identifier||!_url)
+ return;
+
+ [self willChangeValueForKey:@"startAtLogin"];
+
+ if (!SMLoginItemSetEnabled((__bridge CFStringRef)_identifier, (flag) ? true : false)) {
+ NSLog(@"SMLoginItemSetEnabled failed!");
+
+ [self willChangeValueForKey:@"enabled"];
+ _enabled = NO;
+ [self didChangeValueForKey:@"enabled"];
+ } else {
+ [self willChangeValueForKey:@"enabled"];
+ _enabled = YES;
+ [self didChangeValueForKey:@"enabled"];
+ }
+
+ [self didChangeValueForKey:@"startAtLogin"];
+}
- // Make the setting
- if (!SMLoginItemSetEnabled((__bridge CFStringRef)_identifier, (flag) ? true : false)) {
- NSLog(@"SMLoginItemSetEnabled failed!");
- }
- [self didChangeValueForKey:@"startAtLogin"];
+- (BOOL)enabled
+{
+ return _enabled;
}
-- (void)remove {
- if (!_identifier)
- return;
- [self willChangeValueForKey:@"startAtLogin"];
- CFErrorRef error = NULL;
- if (!SMJobRemove(kSMDomainUserLaunchd, (__bridge CFStringRef)_identifier, NULL, true, &error)) {
- NSLog(@"Could not remove job entry: %@", (__bridge NSError*)error);
- }
- [self didChangeValueForKey:@"startAtLogin"];
+- (void)setEnabled:(BOOL)enabled
+{
+ [self setStartAtLogin:enabled];
}
@end
Please sign in to comment.
Something went wrong with that request. Please try again.