432 lines
10 KiB
Objective-C
432 lines
10 KiB
Objective-C
//
|
|
// AppDelegate.m
|
|
// Test Application
|
|
//
|
|
// Created by Patrick Wardle on 9/10/16.
|
|
// Copyright (c) 2016 Objective-See. All rights reserved.
|
|
//
|
|
|
|
#import "Consts.h"
|
|
#import "Logging.h"
|
|
#import "Utilities.h"
|
|
#import "AppDelegate.h"
|
|
|
|
|
|
@interface AppDelegate ()
|
|
|
|
@property (weak) IBOutlet NSWindow *window;
|
|
@end
|
|
|
|
@implementation AppDelegate
|
|
|
|
@synthesize infoWindowController;
|
|
@synthesize aboutWindowController;
|
|
|
|
//center window
|
|
// ->also make front, init title bar, etc
|
|
-(void)awakeFromNib
|
|
{
|
|
//center
|
|
[self.window center];
|
|
|
|
//make it key window
|
|
[self.window makeKeyAndOrderFront:self];
|
|
|
|
//make window front
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
|
|
//set title
|
|
self.window.title = [NSString stringWithFormat:@"OverSight Preferences (v. %@)", getAppVersion()];
|
|
|
|
return;
|
|
}
|
|
|
|
//app interface
|
|
// ->init user interface
|
|
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
|
{
|
|
//set button states
|
|
[self setButtonStates];
|
|
|
|
//register for hotkey presses
|
|
// ->for now, just cmd+q to quit app
|
|
[self registerKeypressHandler];
|
|
|
|
//start login item in background
|
|
// ->checks if already running though
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
|
^{
|
|
//start
|
|
[self startLoginItem:NO];
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
//automatically close when user closes window
|
|
-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
//set button states from preferences
|
|
-(void)setButtonStates
|
|
{
|
|
//preferences
|
|
NSDictionary* preferences = nil;
|
|
|
|
//load preferences
|
|
preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
|
|
|
|
//set 'log activity' button state
|
|
self.logActivity.state = [preferences[PREF_LOG_ACTIVITY] boolValue];
|
|
|
|
//set 'start at login' button state
|
|
self.startAtLogin.state = [preferences[PREF_START_AT_LOGIN] boolValue];
|
|
|
|
//set 'run headless' button state
|
|
self.runHeadless.state = [preferences[PREF_RUN_HEADLESS] boolValue];
|
|
|
|
//set 'automatically check for updates' button state
|
|
self.check4Updates.state = [preferences[PREF_CHECK_4_UPDATES] boolValue];
|
|
|
|
return;
|
|
}
|
|
|
|
//register handler for hot keys
|
|
-(void)registerKeypressHandler
|
|
{
|
|
//event handler
|
|
NSEvent* (^keypressHandler)(NSEvent *) = nil;
|
|
|
|
//init handler block
|
|
// ->just call helper function
|
|
keypressHandler = ^NSEvent * (NSEvent * theEvent){
|
|
|
|
//invoke helper
|
|
return [self handleKeypress:theEvent];
|
|
};
|
|
|
|
//register for key-down events
|
|
[NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:keypressHandler];
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//helper function for keypresses
|
|
// ->for now, only handle cmd+q, to quit
|
|
-(NSEvent*)handleKeypress:(NSEvent*)event
|
|
{
|
|
//flag indicating event was handled
|
|
BOOL wasHandled = NO;
|
|
|
|
//only care about 'cmd' + something
|
|
if(NSCommandKeyMask != (event.modifierFlags & NSCommandKeyMask))
|
|
{
|
|
//bail
|
|
goto bail;
|
|
}
|
|
|
|
//handle key-code
|
|
// command+q: quite
|
|
switch ([event keyCode])
|
|
{
|
|
//'q' (quit)
|
|
case KEYCODE_Q:
|
|
|
|
//bye!
|
|
[[NSApplication sharedApplication] terminate:nil];
|
|
|
|
//set flag
|
|
wasHandled = YES;
|
|
|
|
break;
|
|
|
|
//default
|
|
// ->do nothing
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
//bail
|
|
bail:
|
|
|
|
//nil out event if it was handled
|
|
if(YES == wasHandled)
|
|
{
|
|
//nil
|
|
event = nil;
|
|
}
|
|
|
|
return event;
|
|
|
|
}
|
|
|
|
//toggle/set preferences
|
|
-(IBAction)togglePreference:(NSButton *)sender
|
|
{
|
|
//preferences
|
|
NSMutableDictionary* preferences = nil;
|
|
|
|
//load preferences
|
|
preferences = [NSMutableDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
|
|
|
|
//set 'log activity' button
|
|
if(sender == self.logActivity)
|
|
{
|
|
//set
|
|
preferences[PREF_LOG_ACTIVITY] = [NSNumber numberWithBool:[sender state]];
|
|
}
|
|
|
|
//set 'automatically check for updates'
|
|
else if(sender == self.check4Updates)
|
|
{
|
|
//set
|
|
preferences[PREF_CHECK_4_UPDATES] = [NSNumber numberWithBool:[sender state]];
|
|
|
|
}
|
|
|
|
//set 'start at login'
|
|
// ->then also toggle for current user
|
|
else if(sender == self.startAtLogin)
|
|
{
|
|
//set
|
|
preferences[PREF_START_AT_LOGIN] = [NSNumber numberWithBool:[sender state]];
|
|
|
|
//toggle
|
|
|
|
}
|
|
|
|
//set 'run in headless mode'
|
|
// ->then restart login item to realize this
|
|
else if(sender == self.runHeadless)
|
|
{
|
|
//set
|
|
preferences[PREF_RUN_HEADLESS] = [NSNumber numberWithBool:[sender state]];
|
|
|
|
//restart login item in background
|
|
// ->will read prefs, and run in headless mode
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
|
^{
|
|
//start
|
|
[self startLoginItem:YES];
|
|
});
|
|
}
|
|
|
|
//save em
|
|
[preferences writeToFile:[APP_PREFERENCES stringByExpandingTildeInPath] atomically:YES];
|
|
|
|
return;
|
|
}
|
|
|
|
//'about' button/menu handler
|
|
-(IBAction)about:(id)sender
|
|
{
|
|
//alloc/init settings window
|
|
if(nil == self.aboutWindowController)
|
|
{
|
|
//alloc/init
|
|
aboutWindowController = [[AboutWindowController alloc] initWithWindowNibName:@"AboutWindow"];
|
|
}
|
|
|
|
//center window
|
|
[[self.aboutWindowController window] center];
|
|
|
|
//show it
|
|
[self.aboutWindowController showWindow:self];
|
|
|
|
//invoke function in background that will make window modal
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
//make modal
|
|
makeModal(self.aboutWindowController);
|
|
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
//'check for update' (now) button handler
|
|
-(IBAction)check4Update:(id)sender
|
|
{
|
|
//disable button
|
|
self.check4UpdatesNow.enabled = NO;
|
|
|
|
//hide version msg
|
|
self.versionLabel.hidden = YES;
|
|
|
|
//show spinner
|
|
[self.spinner startAnimation:self];
|
|
|
|
//check for update
|
|
[self isThereAnUpdate];
|
|
|
|
return;
|
|
}
|
|
|
|
//check for an update
|
|
-(void)isThereAnUpdate
|
|
{
|
|
//version string
|
|
NSMutableString* versionString = nil;
|
|
|
|
//alloc string
|
|
versionString = [NSMutableString string];
|
|
|
|
//check if available version is newer
|
|
// ->show update window
|
|
if(YES == isNewVersion(versionString))
|
|
{
|
|
//dbg msg
|
|
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"a new version (%@) is available", versionString]);
|
|
|
|
//alloc/init about window
|
|
infoWindowController = [[InfoWindowController alloc] initWithWindowNibName:@"InfoWindow"];
|
|
|
|
//configure
|
|
[self.infoWindowController configure:[NSString stringWithFormat:@"a new version (%@) is available!", versionString] buttonTitle:@"update"];
|
|
|
|
//center window
|
|
[[self.infoWindowController window] center];
|
|
|
|
//show it
|
|
[self.infoWindowController showWindow:self];
|
|
|
|
//invoke function in background that will make window modal
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
//make modal
|
|
makeModal(self.infoWindowController);
|
|
|
|
});
|
|
|
|
//stop/hide spinner
|
|
[self.spinner stopAnimation:self];
|
|
|
|
//re-enable button
|
|
self.check4UpdatesNow.enabled = YES;
|
|
}
|
|
|
|
//no new version
|
|
// ->just (debug) log msg
|
|
else
|
|
{
|
|
//dbg msg
|
|
logMsg(LOG_DEBUG, @"no updates available");
|
|
|
|
//stop/hide spinner
|
|
[self.spinner stopAnimation:self];
|
|
|
|
//re-enable button
|
|
self.check4UpdatesNow.enabled = YES;
|
|
|
|
//show now new version message
|
|
self.versionLabel.hidden = NO;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//start the login item
|
|
-(void)startLoginItem:(BOOL)shouldRestart
|
|
{
|
|
//path to login item
|
|
NSString* loginItem = nil;
|
|
|
|
//login item's pid
|
|
pid_t loginItemPID = -1;
|
|
|
|
//get pid of login item
|
|
loginItemPID = getProcessID(@"OverSight Helper", getuid());
|
|
|
|
//already running?
|
|
// ->kill the login item
|
|
if( (YES == shouldRestart) &&
|
|
(-1 != loginItemPID) )
|
|
{
|
|
//kill
|
|
kill(loginItemPID, SIGTERM);
|
|
|
|
//sleep
|
|
sleep(2);
|
|
|
|
//really kill
|
|
kill(loginItemPID, SIGKILL);
|
|
|
|
//reset pid
|
|
loginItemPID = -1;
|
|
}
|
|
|
|
//start not already running
|
|
if(-1 == loginItemPID)
|
|
{
|
|
//dbg msg
|
|
logMsg(LOG_DEBUG, @"starting login item");
|
|
|
|
//show overlay view on main thread
|
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
|
|
//pre-req
|
|
[self.overlay setWantsLayer:YES];
|
|
|
|
//set overlay's view color to black
|
|
self.overlay.layer.backgroundColor = [NSColor whiteColor].CGColor;
|
|
|
|
//make it semi-transparent
|
|
self.overlay.alphaValue = 0.85;
|
|
|
|
//show it
|
|
self.overlay.hidden = NO;
|
|
|
|
//TODO:
|
|
//set message
|
|
|
|
//show spinner
|
|
self.progressIndicator.hidden = NO;
|
|
|
|
//animate it
|
|
[self.progressIndicator startAnimation:nil];
|
|
|
|
});
|
|
}
|
|
|
|
|
|
//init path
|
|
loginItem = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"/Contents/Library/LoginItems/OverSight Helper.app"];
|
|
|
|
//launch it
|
|
if(YES != [[NSWorkspace sharedWorkspace] launchApplication:loginItem])
|
|
{
|
|
//err msg
|
|
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to start login item, %@", loginItem]);
|
|
|
|
//bail
|
|
goto bail;
|
|
}
|
|
|
|
//(re)obtain focus for app
|
|
[[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
|
|
|
|
//TODO:
|
|
//hide overlay
|
|
|
|
//bail
|
|
bail:
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//manage rules
|
|
-(IBAction)manageRules:(id)sender
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
@end
|