2016-09-12 01:21:14 +01:00
|
|
|
//
|
|
|
|
// ConfigureWindowController.m
|
|
|
|
// OverSight
|
|
|
|
//
|
|
|
|
// Created by Patrick Wardle on 9/01/16.
|
|
|
|
// Copyright (c) 2016 Objective-See. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "Consts.h"
|
2017-03-28 09:00:11 +01:00
|
|
|
#import "Logging.h"
|
2016-09-12 01:21:14 +01:00
|
|
|
#import "Configure.h"
|
|
|
|
#import "Utilities.h"
|
|
|
|
#import "ConfigureWindowController.h"
|
|
|
|
|
2017-04-03 00:12:42 +01:00
|
|
|
#import <Quartz/Quartz.h>
|
|
|
|
|
2016-09-12 01:21:14 +01:00
|
|
|
@implementation ConfigureWindowController
|
|
|
|
|
|
|
|
@synthesize statusMsg;
|
|
|
|
@synthesize moreInfoButton;
|
|
|
|
|
|
|
|
//automatically called when nib is loaded
|
|
|
|
// ->just center window
|
|
|
|
-(void)awakeFromNib
|
|
|
|
{
|
|
|
|
//center
|
|
|
|
[self.window center];
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//configure window/buttons
|
|
|
|
// ->also brings window to front
|
|
|
|
-(void)configure:(BOOL)isInstalled
|
|
|
|
{
|
|
|
|
//set window title
|
|
|
|
[self window].title = [NSString stringWithFormat:@"version %@", getAppVersion()];
|
|
|
|
|
|
|
|
//yosemite doesn't support emojis :P
|
|
|
|
if(getVersion(gestaltSystemVersionMinor) <= OS_MINOR_VERSION_YOSEMITE)
|
|
|
|
{
|
|
|
|
//init status msg
|
|
|
|
[self.statusMsg setStringValue:@"monitor audio & video access"];
|
|
|
|
}
|
|
|
|
//el capitan supports emojis
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//init status msg
|
|
|
|
[self.statusMsg setStringValue:@"monitor 🎤 and 📸 access"];
|
|
|
|
}
|
|
|
|
|
|
|
|
//enable 'uninstall' button when app is installed already
|
|
|
|
if(YES == isInstalled)
|
|
|
|
{
|
|
|
|
//enable
|
|
|
|
self.uninstallButton.enabled = YES;
|
|
|
|
}
|
|
|
|
//otherwise disable
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//disable
|
|
|
|
self.uninstallButton.enabled = NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
//make 'install' have focus
|
|
|
|
// ->more likely they'll be upgrading, but do after a delay so it sticks...
|
|
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
|
|
|
|
|
|
|
//set responder
|
|
|
|
[self.window makeFirstResponder:self.installButton];
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
//set delegate
|
|
|
|
[self.window setDelegate:self];
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//display (show) window
|
|
|
|
// ->center, make front, set bg to white, etc
|
|
|
|
-(void)display
|
|
|
|
{
|
2017-09-25 07:45:02 +01:00
|
|
|
//indicated title bar is tranparent (too)
|
|
|
|
self.window.titlebarAppearsTransparent = YES;
|
|
|
|
|
2016-09-12 01:21:14 +01:00
|
|
|
//center window
|
|
|
|
[[self window] center];
|
|
|
|
|
|
|
|
//show (now configured) windows
|
|
|
|
[self showWindow:self];
|
|
|
|
|
|
|
|
//make it key window
|
|
|
|
[self.window makeKeyAndOrderFront:self];
|
|
|
|
|
|
|
|
//make window front
|
|
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
|
|
|
|
|
|
//make white
|
|
|
|
[self.window setBackgroundColor: NSColor.whiteColor];
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//button handler for uninstall/install
|
|
|
|
-(IBAction)buttonHandler:(id)sender
|
|
|
|
{
|
|
|
|
//button title
|
|
|
|
NSString* buttonTitle = nil;
|
|
|
|
|
|
|
|
//extact button title
|
|
|
|
buttonTitle = ((NSButton*)sender).title;
|
|
|
|
|
|
|
|
//action
|
|
|
|
NSUInteger action = 0;
|
|
|
|
|
2017-03-28 09:00:11 +01:00
|
|
|
//dbg msg
|
2017-04-03 00:12:42 +01:00
|
|
|
#ifdef DEBUG
|
2017-03-28 09:00:11 +01:00
|
|
|
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"handling action click: %@", buttonTitle]);
|
2017-04-03 00:12:42 +01:00
|
|
|
#endif
|
2016-09-12 01:21:14 +01:00
|
|
|
|
2017-04-04 08:26:11 +01:00
|
|
|
//Close/No?
|
2017-03-28 09:00:11 +01:00
|
|
|
// ->just exit
|
2017-04-03 00:12:42 +01:00
|
|
|
if( (YES == [buttonTitle isEqualToString:ACTION_CLOSE]) ||
|
|
|
|
(YES == [buttonTitle isEqualToString:ACTION_NO]) )
|
|
|
|
{
|
|
|
|
//close
|
|
|
|
[self.window close];
|
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2017-04-04 08:26:11 +01:00
|
|
|
//Next >>?
|
|
|
|
// ->show 'support us' view
|
2017-04-03 00:12:42 +01:00
|
|
|
if(YES == [buttonTitle isEqualToString:ACTION_NEXT])
|
|
|
|
{
|
|
|
|
//frame
|
|
|
|
NSRect frame = {0};
|
|
|
|
|
|
|
|
//unset window title
|
|
|
|
self.window.title = @"";
|
|
|
|
|
|
|
|
//get main window's frame
|
|
|
|
frame = self.window.contentView.frame;
|
|
|
|
|
|
|
|
//set origin to 0/0
|
|
|
|
frame.origin = CGPointZero;
|
|
|
|
|
|
|
|
//increase y offset
|
|
|
|
frame.origin.y += 5;
|
|
|
|
|
|
|
|
//reduce height
|
|
|
|
frame.size.height -= 5;
|
|
|
|
|
|
|
|
//pre-req
|
|
|
|
[self.supportView setWantsLayer:YES];
|
|
|
|
|
|
|
|
//update overlay to take up entire window
|
|
|
|
self.supportView.frame = frame;
|
|
|
|
|
|
|
|
//set overlay's view color to white
|
|
|
|
self.supportView.layer.backgroundColor = [NSColor whiteColor].CGColor;
|
|
|
|
|
|
|
|
//nap for UI purposes
|
|
|
|
[NSThread sleepForTimeInterval:0.10f];
|
|
|
|
|
|
|
|
//add to main window
|
|
|
|
[self.window.contentView addSubview:self.supportView];
|
|
|
|
|
|
|
|
//show
|
|
|
|
self.supportView.hidden = NO;
|
|
|
|
|
|
|
|
//make 'yes!' button active
|
|
|
|
[self.window makeFirstResponder:self.supportButton];
|
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2017-04-04 08:26:11 +01:00
|
|
|
//'Yes' for support
|
2017-04-03 00:12:42 +01:00
|
|
|
// ->load supprt in URL
|
|
|
|
if(YES == [buttonTitle isEqualToString:ACTION_YES])
|
2016-09-12 01:21:14 +01:00
|
|
|
{
|
2017-04-03 00:12:42 +01:00
|
|
|
//open URL
|
|
|
|
// ->invokes user's default browser
|
|
|
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:PATREON_URL]];
|
|
|
|
|
2017-03-28 09:00:11 +01:00
|
|
|
//close
|
|
|
|
[self.window close];
|
2017-04-03 00:12:42 +01:00
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
2016-09-12 01:21:14 +01:00
|
|
|
}
|
2017-03-28 09:00:11 +01:00
|
|
|
|
|
|
|
//install/uninstall logic handlers
|
2016-09-12 01:21:14 +01:00
|
|
|
else
|
|
|
|
{
|
2017-03-28 09:00:11 +01:00
|
|
|
//hide 'get more info' button
|
|
|
|
self.moreInfoButton.hidden = YES;
|
|
|
|
|
|
|
|
//set action
|
|
|
|
// ->install daemon
|
|
|
|
if(YES == [buttonTitle isEqualToString:ACTION_INSTALL])
|
|
|
|
{
|
|
|
|
//set
|
|
|
|
action = ACTION_INSTALL_FLAG;
|
|
|
|
}
|
|
|
|
//set action
|
|
|
|
// ->uninstall daemon
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//set
|
|
|
|
action = ACTION_UNINSTALL_FLAG;
|
|
|
|
}
|
|
|
|
|
|
|
|
//disable 'x' button
|
|
|
|
// ->don't want user killing app during install/upgrade
|
|
|
|
[[self.window standardWindowButton:NSWindowCloseButton] setEnabled:NO];
|
|
|
|
|
|
|
|
//clear status msg
|
|
|
|
[self.statusMsg setStringValue:@""];
|
|
|
|
|
|
|
|
//force redraw of status msg
|
|
|
|
// ->sometime doesn't refresh (e.g. slow VM)
|
|
|
|
[self.statusMsg setNeedsDisplay:YES];
|
|
|
|
|
|
|
|
//invoke logic to install/uninstall
|
|
|
|
// ->do in background so UI doesn't block
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
|
|
|
^{
|
|
|
|
//install/uninstall
|
|
|
|
[self lifeCycleEvent:action];
|
|
|
|
});
|
2016-09-12 01:21:14 +01:00
|
|
|
}
|
2017-04-03 00:12:42 +01:00
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
2016-09-12 01:21:14 +01:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//button handler for '?' button (on an error)
|
|
|
|
// ->load objective-see's documentation for error(s) in default browser
|
|
|
|
-(IBAction)info:(id)sender
|
|
|
|
{
|
|
|
|
//url
|
|
|
|
NSURL *helpURL = nil;
|
|
|
|
|
|
|
|
//build help URL
|
|
|
|
helpURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@#errors", PRODUCT_URL]];
|
|
|
|
|
|
|
|
//open URL
|
|
|
|
// ->invokes user's default browser
|
|
|
|
[[NSWorkspace sharedWorkspace] openURL:helpURL];
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//perform install | uninstall via Control obj
|
|
|
|
// ->invoked on background thread so that UI doesn't block
|
|
|
|
-(void)lifeCycleEvent:(NSUInteger)event
|
|
|
|
{
|
|
|
|
//status var
|
|
|
|
BOOL status = NO;
|
|
|
|
|
|
|
|
//configure object
|
|
|
|
Configure* configureObj = nil;
|
|
|
|
|
|
|
|
//alloc control object
|
|
|
|
configureObj = [[Configure alloc] init];
|
|
|
|
|
|
|
|
//begin event
|
|
|
|
// ->updates ui on main thread
|
|
|
|
dispatch_sync(dispatch_get_main_queue(),
|
|
|
|
^{
|
|
|
|
//complete
|
|
|
|
[self beginEvent:event];
|
|
|
|
});
|
|
|
|
|
|
|
|
//sleep
|
|
|
|
// ->allow 'install' || 'uninstall' msg to show up
|
2017-03-28 09:00:11 +01:00
|
|
|
[NSThread sleepForTimeInterval:1.0f];
|
2016-09-12 01:21:14 +01:00
|
|
|
|
|
|
|
//perform action (install | uninstall)
|
|
|
|
// ->perform background actions
|
|
|
|
if(YES == [configureObj configure:event])
|
|
|
|
{
|
|
|
|
//set flag
|
|
|
|
status = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
//error occurred
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//err msg
|
|
|
|
//logMsg(LOG_ERR, @"ERROR: failed to perform life cycle event");
|
|
|
|
|
|
|
|
//set flag
|
|
|
|
status = NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
//complet event
|
|
|
|
// ->updates ui on main thread
|
|
|
|
dispatch_async(dispatch_get_main_queue(),
|
|
|
|
^{
|
|
|
|
//complete
|
|
|
|
[self completeEvent:status event:event];
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//begin event
|
|
|
|
// ->basically just update UI
|
|
|
|
-(void)beginEvent:(NSUInteger)event
|
|
|
|
{
|
|
|
|
//align text left
|
|
|
|
[self.statusMsg setAlignment:NSLeftTextAlignment];
|
|
|
|
|
|
|
|
//install msg
|
|
|
|
if(ACTION_INSTALL_FLAG == event)
|
|
|
|
{
|
|
|
|
//update status msg
|
2016-09-25 04:08:38 +01:00
|
|
|
// ->with space to avoid spinner
|
|
|
|
[self.statusMsg setStringValue:@"\t Installing..."];
|
2016-09-12 01:21:14 +01:00
|
|
|
}
|
|
|
|
//uninstall msg
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//update status msg
|
2016-09-25 04:08:38 +01:00
|
|
|
// ->with space to avoid spinner
|
|
|
|
[self.statusMsg setStringValue:@"\t Uninstalling..."];
|
2016-09-12 01:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//disable action button
|
|
|
|
self.uninstallButton.enabled = NO;
|
|
|
|
|
|
|
|
//disable cancel button
|
|
|
|
self.installButton.enabled = NO;
|
|
|
|
|
|
|
|
//show spinner
|
|
|
|
[self.activityIndicator setHidden:NO];
|
|
|
|
|
|
|
|
//start spinner
|
|
|
|
[self.activityIndicator startAnimation:nil];
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//complete event
|
|
|
|
// ->update UI after background event has finished
|
|
|
|
-(void)completeEvent:(BOOL)success event:(NSUInteger)event
|
|
|
|
{
|
|
|
|
//action
|
|
|
|
NSString* action = nil;
|
|
|
|
|
|
|
|
//result msg
|
|
|
|
NSMutableString* resultMsg = nil;
|
|
|
|
|
|
|
|
//msg font
|
|
|
|
NSColor* resultMsgColor = nil;
|
|
|
|
|
|
|
|
//generally want centered text
|
|
|
|
[self.statusMsg setAlignment:NSCenterTextAlignment];
|
|
|
|
|
|
|
|
//set action msg for install
|
|
|
|
if(ACTION_INSTALL_FLAG == event)
|
|
|
|
{
|
|
|
|
//set msg
|
|
|
|
action = @"install";
|
|
|
|
}
|
|
|
|
//set action msg for uninstall
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//set msg
|
|
|
|
action = @"uninstall";
|
|
|
|
}
|
|
|
|
|
|
|
|
//success
|
|
|
|
if(YES == success)
|
|
|
|
{
|
|
|
|
//set result msg
|
2016-09-16 09:13:06 +01:00
|
|
|
resultMsg = [NSMutableString stringWithFormat:@"OverSight %@ed!", action];
|
2016-09-12 01:21:14 +01:00
|
|
|
|
|
|
|
//add extra info when installed
|
|
|
|
if(ACTION_INSTALL_FLAG == event)
|
|
|
|
{
|
|
|
|
//append
|
2016-09-16 09:13:06 +01:00
|
|
|
[resultMsg appendString:@"\nconfig via ☔️ in status bar"];
|
2016-09-12 01:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//set font to black
|
|
|
|
resultMsgColor = [NSColor blackColor];
|
|
|
|
}
|
|
|
|
//failure
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//set result msg
|
|
|
|
resultMsg = [NSMutableString stringWithFormat:@"error: %@ failed", action];
|
|
|
|
|
|
|
|
//set font to red
|
|
|
|
resultMsgColor = [NSColor redColor];
|
|
|
|
|
|
|
|
//show 'get more info' button
|
|
|
|
self.moreInfoButton.hidden = NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
//stop/hide spinner
|
|
|
|
[self.activityIndicator stopAnimation:nil];
|
|
|
|
|
|
|
|
//hide spinner
|
|
|
|
[self.activityIndicator setHidden:YES];
|
|
|
|
|
|
|
|
//set font to bold
|
|
|
|
[self.statusMsg setFont:[NSFont fontWithName:@"Menlo-Bold" size:13]];
|
|
|
|
|
|
|
|
//set msg color
|
|
|
|
[self.statusMsg setTextColor:resultMsgColor];
|
|
|
|
|
|
|
|
//set status msg
|
|
|
|
[self.statusMsg setStringValue:resultMsg];
|
|
|
|
|
2017-03-28 09:00:11 +01:00
|
|
|
//update button
|
2017-04-04 08:26:11 +01:00
|
|
|
// ->after install change butter to 'Next'
|
2016-09-12 01:21:14 +01:00
|
|
|
if(ACTION_INSTALL_FLAG == event)
|
|
|
|
{
|
2017-03-28 09:00:11 +01:00
|
|
|
//set button title to 'close'
|
2017-04-03 00:12:42 +01:00
|
|
|
self.installButton.title = ACTION_NEXT;
|
2017-03-28 09:00:11 +01:00
|
|
|
|
|
|
|
//enable
|
|
|
|
self.installButton.enabled = YES;
|
2016-09-12 01:21:14 +01:00
|
|
|
|
2017-03-28 09:00:11 +01:00
|
|
|
//make it active
|
|
|
|
[self.window makeFirstResponder:self.installButton];
|
2016-09-12 01:21:14 +01:00
|
|
|
}
|
2017-03-28 09:00:11 +01:00
|
|
|
//update button
|
|
|
|
// ->after uninstall change butter to 'close'
|
2016-09-12 01:21:14 +01:00
|
|
|
else
|
|
|
|
{
|
2017-03-28 09:00:11 +01:00
|
|
|
//set button title to 'close'
|
|
|
|
self.uninstallButton.title = ACTION_CLOSE;
|
2016-09-12 01:21:14 +01:00
|
|
|
|
2017-03-28 09:00:11 +01:00
|
|
|
//enable
|
|
|
|
self.uninstallButton.enabled = YES;
|
|
|
|
|
|
|
|
//make it active
|
|
|
|
[self.window makeFirstResponder:self.uninstallButton];
|
2016-09-12 01:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//ok to re-enable 'x' button
|
|
|
|
[[self.window standardWindowButton:NSWindowCloseButton] setEnabled:YES];
|
|
|
|
|
|
|
|
//(re)make window window key
|
|
|
|
[self.window makeKeyAndOrderFront:self];
|
|
|
|
|
|
|
|
//(re)make window front
|
|
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//automatically invoked when window is closing
|
|
|
|
// ->just exit application
|
|
|
|
-(void)windowWillClose:(NSNotification *)notification
|
|
|
|
{
|
|
|
|
//exit
|
|
|
|
[NSApp terminate:self];
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@end
|