285 lines
6.8 KiB
Objective-C
285 lines
6.8 KiB
Objective-C
//
|
|
// AppDelegate.m
|
|
// Login Item, (app helper)
|
|
//
|
|
// 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"
|
|
#import "XPCProtocol.h"
|
|
|
|
@interface AppDelegate ()
|
|
|
|
@property (weak) IBOutlet NSWindow *window;
|
|
@end
|
|
|
|
@implementation AppDelegate
|
|
|
|
@synthesize avMonitor;
|
|
@synthesize infoWindowController;
|
|
@synthesize statusBarMenuController;
|
|
|
|
//app's main interface
|
|
// ->load status bar and kick off monitor
|
|
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
|
{
|
|
//preferences
|
|
NSDictionary* preferences = nil;
|
|
|
|
//logged in user info
|
|
NSMutableDictionary* userInfo = nil;
|
|
|
|
//dbg msg
|
|
#ifdef DEBUG
|
|
logMsg(LOG_DEBUG, @"starting login item app logic");
|
|
#endif
|
|
|
|
//get user
|
|
userInfo = loggedinUser();
|
|
if(nil == userInfo[@"user"])
|
|
{
|
|
//err msg
|
|
logMsg(LOG_ERR, @"failed to determine logged-in user");
|
|
|
|
//bail
|
|
goto bail;
|
|
}
|
|
|
|
//drop group privs
|
|
setgid([userInfo[@"gid"] intValue]);
|
|
|
|
//drop user privs
|
|
setuid([userInfo[@"uid"] intValue]);
|
|
|
|
//load preferences
|
|
preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
|
|
|
|
//init/load status bar
|
|
// ->but only if user didn't say: 'run in headless mode'
|
|
if(YES != [preferences[PREF_RUN_HEADLESS] boolValue])
|
|
{
|
|
//load
|
|
[self loadStatusBar];
|
|
|
|
//dbg msg
|
|
#ifdef DEBUG
|
|
logMsg(LOG_DEBUG, @"initialized/loaded status bar (icon/menu)");
|
|
#endif
|
|
}
|
|
#ifdef DEBUG
|
|
//dbg msg
|
|
else
|
|
{
|
|
//dbg msg
|
|
logMsg(LOG_DEBUG, @"running in headless mode");
|
|
}
|
|
#endif
|
|
|
|
//check for updates
|
|
// ->but only when user has not disabled that feature
|
|
if(YES == [preferences[PREF_CHECK_4_UPDATES] boolValue])
|
|
{
|
|
//after a minute
|
|
//->check for updates in background
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
|
|
{
|
|
//dbg msg
|
|
#ifdef DEBUG
|
|
logMsg(LOG_DEBUG, @"checking for update");
|
|
#endif
|
|
|
|
//check
|
|
[self isThereAnUpdate];
|
|
|
|
});
|
|
}
|
|
|
|
//when logging is enabled
|
|
// ->open/create log file
|
|
if(YES == [preferences[PREF_LOG_ACTIVITY] boolValue])
|
|
{
|
|
//init
|
|
if(YES != initLogging())
|
|
{
|
|
//err msg
|
|
logMsg(LOG_ERR, @"failed to init logging");
|
|
|
|
//bail
|
|
goto bail;
|
|
}
|
|
|
|
//dbg msg
|
|
// ->and to file
|
|
logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging intialized (login item)");
|
|
}
|
|
|
|
//spawn 'heartbeat' thread to XPC to keep it open
|
|
[NSThread detachNewThreadSelector:@selector(heartBeat) toTarget:self withObject:nil];
|
|
|
|
//create/init av event monitor
|
|
avMonitor = [[AVMonitor alloc] init];
|
|
|
|
//dbg msg
|
|
#ifdef DEBUG
|
|
logMsg(LOG_DEBUG, @"alloc/init'd AV monitor");
|
|
#endif
|
|
|
|
//start monitoring
|
|
// ->sets up audio/video callbacks
|
|
[avMonitor monitor];
|
|
|
|
//log msg
|
|
logMsg(LOG_DEBUG|LOG_TO_FILE, @"OverSight starting");
|
|
|
|
//bail
|
|
bail:
|
|
|
|
return;
|
|
}
|
|
|
|
//initialize status menu bar
|
|
-(void)loadStatusBar
|
|
{
|
|
//alloc/load nib
|
|
statusBarMenuController = [[StatusBarMenu alloc] init];
|
|
|
|
//init menu
|
|
[self.statusBarMenuController setupStatusItem];
|
|
|
|
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))
|
|
{
|
|
//new version!
|
|
// ->show update popup on main thread
|
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
|
|
//dbg msg
|
|
#ifdef DEBUG
|
|
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"a new version (%@) is available", versionString]);
|
|
#endif
|
|
|
|
//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);
|
|
|
|
});
|
|
|
|
});
|
|
}
|
|
|
|
//no new version
|
|
// ->just (debug) log msg
|
|
#ifdef DEBUG
|
|
else
|
|
{
|
|
//dbg msg
|
|
logMsg(LOG_DEBUG, @"no updates available");
|
|
}
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
//ping XPC service to keep it alive
|
|
-(void)heartBeat
|
|
{
|
|
//pool
|
|
@autoreleasepool {
|
|
|
|
//xpc connection
|
|
__block NSXPCConnection* xpcConnection = nil;
|
|
|
|
//wait semaphore
|
|
dispatch_semaphore_t waitSema = nil;
|
|
|
|
//alloc XPC connection
|
|
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
|
|
|
|
//set remote object interface
|
|
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
|
|
|
|
//resume
|
|
[xpcConnection resume];
|
|
|
|
//forever
|
|
while(YES)
|
|
{
|
|
//init wait semaphore
|
|
waitSema = dispatch_semaphore_create(0);
|
|
|
|
#ifdef DEBUG
|
|
//dbg msg
|
|
logMsg(LOG_DEBUG, @"sending XPC heart beat request");
|
|
#endif
|
|
|
|
//XPC service to begin baselining mach messages
|
|
// ->wait, since want this to compelete before doing other things!
|
|
[[xpcConnection remoteObjectProxy] heartBeat:^(BOOL reply)
|
|
{
|
|
//signal sema
|
|
dispatch_semaphore_signal(waitSema);
|
|
|
|
}];
|
|
|
|
//wait until XPC is done
|
|
// ->XPC reply block will signal semaphore
|
|
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
|
|
|
|
//nap
|
|
[NSThread sleepForTimeInterval:3.0f];
|
|
}
|
|
|
|
}//pool
|
|
|
|
return;
|
|
}
|
|
|
|
//going bye-bye
|
|
// ->close logging
|
|
-(void)applicationWillTerminate:(NSNotification *)notification
|
|
{
|
|
//log msg
|
|
logMsg(LOG_DEBUG|LOG_TO_FILE, @"OverSight ending");
|
|
|
|
//log msg
|
|
logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized (login item)");
|
|
|
|
//stop logz
|
|
deinitLogging();
|
|
|
|
return;
|
|
}
|
|
|
|
@end
|