logic for version 1.1.0 :)
-whitelist / rules logic -improved audio process enumeration
This commit is contained in:
parent
eb635e54d9
commit
c6eaba30f2
|
@ -29,7 +29,7 @@
|
|||
-(BOOL)install;
|
||||
|
||||
//uninstall
|
||||
-(BOOL)uninstall;
|
||||
-(BOOL)uninstall:(NSUInteger)type;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -41,7 +41,8 @@
|
|||
[self stop];
|
||||
|
||||
//uninstall
|
||||
if(YES != [self uninstall])
|
||||
// ->but do partial (leave whitelist)
|
||||
if(YES != [self uninstall:UNINSTALL_PARIAL])
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
|
@ -89,7 +90,7 @@
|
|||
logMsg(LOG_DEBUG, @"uninstalling...");
|
||||
|
||||
//uninstall
|
||||
if(YES != [self uninstall])
|
||||
if(YES != [self uninstall:UNINSTALL_FULL])
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
|
@ -185,7 +186,7 @@ bail:
|
|||
|
||||
//call into login item to install itself
|
||||
// ->runs as logged in user, so can access user's login items, etc
|
||||
execTask(SUDO, @[@"-u", user, loginItem, ACTION_INSTALL]);
|
||||
execTask(SUDO, @[@"-u", user, loginItem, [NSString stringWithUTF8String:CMD_INSTALL]]);
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"persisted %@", loginItem]);
|
||||
|
@ -277,7 +278,7 @@ bail:
|
|||
|
||||
//uninstall
|
||||
// ->delete app
|
||||
-(BOOL)uninstall
|
||||
-(BOOL)uninstall:(NSUInteger)type
|
||||
{
|
||||
//return/status var
|
||||
BOOL wasUninstalled = NO;
|
||||
|
@ -286,23 +287,26 @@ bail:
|
|||
// ->since want to try all uninstall steps, but record if any fail
|
||||
BOOL bAnyErrors = NO;
|
||||
|
||||
//path to finder sync
|
||||
NSString* appPath = nil;
|
||||
//path to login item
|
||||
NSString* loginItem = nil;
|
||||
|
||||
//path to installed app
|
||||
NSString* installedAppPath = nil;
|
||||
|
||||
//error
|
||||
NSError* error = nil;
|
||||
|
||||
//path to login item
|
||||
NSString* loginItem = nil;
|
||||
|
||||
//logged in user
|
||||
NSString* user = nil;
|
||||
|
||||
//init path
|
||||
appPath = [APPS_FOLDER stringByAppendingPathComponent:APP_NAME];
|
||||
|
||||
//init path to login item
|
||||
loginItem = [appPath stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"];
|
||||
loginItem = [[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:APP_NAME] stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"];
|
||||
|
||||
//init path to installed app
|
||||
installedAppPath = [APPS_FOLDER stringByAppendingPathComponent:APP_NAME];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"uninstalling login item");
|
||||
|
||||
//get user
|
||||
user = loggedinUser();
|
||||
|
@ -315,25 +319,30 @@ bail:
|
|||
bAnyErrors = YES;
|
||||
|
||||
//keep uninstalling...
|
||||
|
||||
}
|
||||
|
||||
//unistall login item
|
||||
else
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"telling login item %@, to uninstall itself", loginItem]);
|
||||
|
||||
//call into login item to uninstall itself
|
||||
// ->runs as logged in user, so can access user's login items, etc
|
||||
execTask(SUDO, @[@"-u", user, loginItem, ACTION_UNINSTALL]);
|
||||
execTask(SUDO, @[@"-u", user, loginItem, [NSString stringWithUTF8String:CMD_UNINSTALL]]);
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"unpersisted %@", loginItem]);
|
||||
}
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"deleting app");
|
||||
|
||||
//delete folder
|
||||
if(YES != [[NSFileManager defaultManager] removeItemAtPath:appPath error:&error])
|
||||
if(YES != [[NSFileManager defaultManager] removeItemAtPath:installedAppPath error:&error])
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete app %@ (%@)", appPath, error]);
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete app %@ (%@)", installedAppPath, error]);
|
||||
|
||||
//set flag
|
||||
bAnyErrors = YES;
|
||||
|
@ -341,6 +350,30 @@ bail:
|
|||
//keep uninstalling...
|
||||
}
|
||||
|
||||
//full uninstall?
|
||||
// ->remove app support directory too
|
||||
if(UNINSTALL_FULL == type)
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"full, so also deleting app support directory");
|
||||
|
||||
//delete app support folder
|
||||
if(YES == [[NSFileManager defaultManager] fileExistsAtPath:[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath]])
|
||||
{
|
||||
//delete
|
||||
if(YES != [self removeAppSupport])
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete app support directory %@", APP_SUPPORT_DIRECTORY]);
|
||||
|
||||
//set flag
|
||||
bAnyErrors = YES;
|
||||
|
||||
//keep uninstalling...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//only success when there were no errors
|
||||
if(YES != bAnyErrors)
|
||||
{
|
||||
|
@ -351,6 +384,49 @@ bail:
|
|||
return wasUninstalled;
|
||||
}
|
||||
|
||||
//remove ~/Library/Application Support/Objective-See/OverSight
|
||||
// and also ~/Library/Application Support/Objective-See/ if nothing else is in there (no other products)
|
||||
-(BOOL)removeAppSupport
|
||||
{
|
||||
//flag
|
||||
BOOL removedDirectory = NO;
|
||||
|
||||
//error
|
||||
NSError* error = nil;
|
||||
|
||||
//delete OverSight directory
|
||||
if(YES != [[NSFileManager defaultManager] removeItemAtPath:[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] error:&error])
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete OverSight's app support directory %@ (%@)", APP_SUPPORT_DIRECTORY, error]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//anything left in ~/Library/Application Support/Objective-See/?
|
||||
// ->nope: delete it
|
||||
if(0 == [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:[[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] stringByDeletingLastPathComponent] error:nil] count])
|
||||
{
|
||||
if(YES != [[NSFileManager defaultManager] removeItemAtPath:[[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] stringByDeletingLastPathComponent] error:&error])
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete Objective-See's app support directory %@ (%@)", [APP_SUPPORT_DIRECTORY stringByDeletingLastPathComponent], error]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
//happy
|
||||
removedDirectory = YES;
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return removedDirectory;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
#import "Consts.h"
|
||||
#import "Logging.h"
|
||||
#import "Configure.h"
|
||||
#import "Utilities.h"
|
||||
#import "ConfigureWindowController.h"
|
||||
|
@ -108,6 +109,21 @@
|
|||
//action
|
||||
NSUInteger action = 0;
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"handling action click: %@", buttonTitle]);
|
||||
|
||||
//close?
|
||||
// ->just exit
|
||||
if(YES == [buttonTitle isEqualToString:ACTION_CLOSE])
|
||||
{
|
||||
//close
|
||||
[self.window close];
|
||||
}
|
||||
|
||||
//install/uninstall logic handlers
|
||||
else
|
||||
{
|
||||
|
||||
//hide 'get more info' button
|
||||
self.moreInfoButton.hidden = YES;
|
||||
|
||||
|
@ -145,9 +161,7 @@
|
|||
[self lifeCycleEvent:action];
|
||||
|
||||
});
|
||||
|
||||
//bail
|
||||
bail:
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -195,7 +209,7 @@ bail:
|
|||
|
||||
//sleep
|
||||
// ->allow 'install' || 'uninstall' msg to show up
|
||||
sleep(1);
|
||||
[NSThread sleepForTimeInterval:1.0f];
|
||||
|
||||
//perform action (install | uninstall)
|
||||
// ->perform background actions
|
||||
|
@ -336,27 +350,34 @@ bail:
|
|||
//set status msg
|
||||
[self.statusMsg setStringValue:resultMsg];
|
||||
|
||||
//toggle buttons
|
||||
// ->after install turn on 'uninstall' and off 'install'
|
||||
//update button
|
||||
// ->after install change butter to 'close'
|
||||
if(ACTION_INSTALL_FLAG == event)
|
||||
{
|
||||
//enable uninstall
|
||||
self.uninstallButton.enabled = YES;
|
||||
//set button title to 'close'
|
||||
self.installButton.title = ACTION_CLOSE;
|
||||
|
||||
//disable install
|
||||
self.installButton.enabled = NO;
|
||||
//enable
|
||||
self.installButton.enabled = YES;
|
||||
|
||||
//make it active
|
||||
[self.window makeFirstResponder:self.installButton];
|
||||
}
|
||||
//toggle buttons
|
||||
// ->after uninstall turn off 'uninstall' and on 'install'
|
||||
//update button
|
||||
// ->after uninstall change butter to 'close'
|
||||
else
|
||||
{
|
||||
//disable
|
||||
self.uninstallButton.enabled = NO;
|
||||
//set button title to 'close'
|
||||
self.uninstallButton.title = ACTION_CLOSE;
|
||||
|
||||
//enable close button
|
||||
self.installButton.enabled = YES;
|
||||
//enable
|
||||
self.uninstallButton.enabled = YES;
|
||||
|
||||
//make it active
|
||||
[self.window makeFirstResponder:self.uninstallButton];
|
||||
}
|
||||
|
||||
|
||||
//ok to re-enable 'x' button
|
||||
[[self.window standardWindowButton:NSWindowCloseButton] setEnabled:YES];
|
||||
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
<string>1.1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<string>1.1.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright (c) 2016 Objective-See. All rights reserved.</string>
|
||||
<string>Copyright (c) 2017 Objective-See. All rights reserved.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#import "Configure.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
//TODO: wrap debug msgs!
|
||||
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
//return var
|
||||
|
|
|
@ -43,6 +43,12 @@
|
|||
//flag indicating video (camera) is active
|
||||
@property BOOL videoActive;
|
||||
|
||||
//flag indicating an audio active alert was shown
|
||||
@property BOOL showAudioDeactivation;
|
||||
|
||||
//flag indicating a video active alert was shown
|
||||
@property BOOL showVideoDeactivation;
|
||||
|
||||
//monitor thread
|
||||
@property(nonatomic, retain)NSThread* videoMonitorThread;
|
||||
|
||||
|
@ -55,10 +61,16 @@
|
|||
//last notification
|
||||
@property(nonatomic, retain)NSString* lastNotification;
|
||||
|
||||
//whitelisted procs
|
||||
@property(nonatomic, retain)NSMutableArray* whiteList;
|
||||
|
||||
|
||||
|
||||
/* METHODS */
|
||||
|
||||
//load whitelist
|
||||
-(void)loadWhitelist;
|
||||
|
||||
//kicks off thread to monitor
|
||||
-(BOOL)monitor;
|
||||
|
||||
|
|
|
@ -19,12 +19,16 @@
|
|||
@synthesize mic;
|
||||
@synthesize camera;
|
||||
@synthesize lastEvent;
|
||||
@synthesize whiteList;
|
||||
@synthesize audioActive;
|
||||
@synthesize videoActive;
|
||||
@synthesize lastNotification;
|
||||
@synthesize videoMonitorThread;
|
||||
@synthesize showAudioDeactivation;
|
||||
@synthesize showVideoDeactivation;
|
||||
@synthesize rememberWindowController;
|
||||
|
||||
//TODO: fix hang!!
|
||||
|
||||
//init
|
||||
-(id)init
|
||||
{
|
||||
|
@ -32,12 +36,48 @@
|
|||
self = [super init];
|
||||
if(nil != self)
|
||||
{
|
||||
|
||||
//load whitelist
|
||||
[self loadWhitelist];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//load whitelist
|
||||
-(void)loadWhitelist
|
||||
{
|
||||
//path
|
||||
NSString* path = nil;
|
||||
|
||||
//init path
|
||||
path = [[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] stringByAppendingPathComponent:FILE_WHITELIST];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"loading whitelist %@", path]);
|
||||
|
||||
//since file is created by priv'd XPC, it shouldn't be writeable
|
||||
// ...unless somebody maliciously creates it, so we check if that here
|
||||
if(YES == [[NSFileManager defaultManager] isWritableFileAtPath:path])
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, @"whitelist is writable, so ignoring!");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//load
|
||||
self.whiteList = [NSMutableArray arrayWithContentsOfFile:path];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"whitelist: %@", self.whiteList]);
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//grab first apple camera, or default
|
||||
// ->saves into iVar 'camera'
|
||||
-(void)findAppleCamera
|
||||
|
@ -397,7 +437,6 @@ bail:
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
//helper function
|
||||
// ->determines if video went active/inactive then invokes notification generator method
|
||||
-(void)handleVideoNotification:(CMIOObjectID)deviceID addresses:(const CMIOObjectPropertyAddress[]) addresses
|
||||
|
@ -479,6 +518,7 @@ bail:
|
|||
|
||||
}];
|
||||
|
||||
//TODO: maybe add timeout here?
|
||||
//wait until XPC is done
|
||||
// ->XPC reply block will signal semaphore
|
||||
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
|
||||
|
@ -864,7 +904,8 @@ bail:
|
|||
}
|
||||
|
||||
//build and display notification
|
||||
-(void)generateNotification:(NSDictionary*)event
|
||||
// ->handles extra logic like ignore whitelisted apps, disable alerts (if user has turned that off), etc
|
||||
-(void)generateNotification:(NSMutableDictionary*)event
|
||||
{
|
||||
//notification
|
||||
NSUserNotification* notification = nil;
|
||||
|
@ -883,6 +924,9 @@ bail:
|
|||
//process name
|
||||
NSString* processName = nil;
|
||||
|
||||
//process path
|
||||
NSString* processPath = nil;
|
||||
|
||||
//log msg
|
||||
NSMutableString* sysLogMsg = nil;
|
||||
|
||||
|
@ -898,44 +942,18 @@ bail:
|
|||
//alloc log msg
|
||||
sysLogMsg = [NSMutableString string];
|
||||
|
||||
//check if event is essentially a duplicate (facetime, etc)
|
||||
if(nil != self.lastEvent)
|
||||
{
|
||||
//TODO: remove
|
||||
//NSLog(@"difference %f", fabs([self.lastEvent[EVENT_TIMESTAMP] timeIntervalSinceDate:event[EVENT_TIMESTAMP]]));
|
||||
|
||||
//less than 10 second ago?
|
||||
if(fabs([self.lastEvent[EVENT_TIMESTAMP] timeIntervalSinceDate:event[EVENT_TIMESTAMP]]) < 10)
|
||||
{
|
||||
//same process/device/action
|
||||
if( (YES == [self.lastEvent[EVENT_PROCESS_ID] isEqual:event[EVENT_PROCESS_ID]]) &&
|
||||
(YES == [self.lastEvent[EVENT_DEVICE] isEqual:event[EVENT_DEVICE]]) &&
|
||||
(YES == [self.lastEvent[EVENT_DEVICE_STATUS] isEqual:event[EVENT_DEVICE_STATUS]]) )
|
||||
{
|
||||
//update
|
||||
self.lastEvent = event;
|
||||
|
||||
//bail to ignore
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
}//'same' event check
|
||||
|
||||
//update last event
|
||||
self.lastEvent = event;
|
||||
|
||||
//always (manually) load preferences
|
||||
preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
|
||||
|
||||
//check if user wants to ingnore inactive alerts
|
||||
if( (YES == [preferences[PREF_DISABLE_INACTIVE] boolValue]) &&
|
||||
(YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) )
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"user has decided to ingore 'inactive' events, so bailing");
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"generating notification for %@", event]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
//get process name
|
||||
processName = getProcessName([event[EVENT_PROCESS_ID] intValue]);
|
||||
|
||||
//get process path
|
||||
processPath = getProcessPath([event[EVENT_PROCESS_ID] intValue]);
|
||||
if(nil == processPath)
|
||||
{
|
||||
//set to something
|
||||
processPath = PROCESS_UNKNOWN;
|
||||
}
|
||||
|
||||
//set device and title for audio
|
||||
|
@ -958,6 +976,88 @@ bail:
|
|||
deviceType = SOURCE_VIDEO;
|
||||
}
|
||||
|
||||
//ignore whitelisted processes
|
||||
// ->for activation events, can check process path
|
||||
if( (YES == [DEVICE_ACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) &&
|
||||
(YES == [self.whiteList containsObject:processPath]) )
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"activation alert for process %@ is whitelisted, so ignoring", processPath]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
//ignore whitelisted processes
|
||||
// ->for deactivation, ignore when no activation alert was shown (cuz process will have likely died, so no pid/path, etc)
|
||||
if(YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]])
|
||||
{
|
||||
//ignore audio inactive event, if no active event was shown
|
||||
if( (SOURCE_AUDIO.intValue == deviceType.intValue) &&
|
||||
(YES != self.showAudioDeactivation) )
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"deactivation audio alert doesn't have an activation alert (whitelisted?), so ignoring");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//ignore video inactive event, if no active event was shown
|
||||
if( (SOURCE_VIDEO.intValue == deviceType.intValue) &&
|
||||
(YES != self.showVideoDeactivation) )
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"deactivation video alert doesn't have an activation alert (whitelisted?), so ignoring");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"got deactivation alert, but neither showVideoDeactivation nor showVideoDeactivation is set...");
|
||||
}
|
||||
|
||||
//check if event is essentially a duplicate (facetime, etc)
|
||||
if(nil != self.lastEvent)
|
||||
{
|
||||
//less than 10 second ago?
|
||||
if(fabs([self.lastEvent[EVENT_TIMESTAMP] timeIntervalSinceDate:event[EVENT_TIMESTAMP]]) < 10)
|
||||
{
|
||||
//same process/device/action
|
||||
if( (YES == [self.lastEvent[EVENT_PROCESS_ID] isEqual:event[EVENT_PROCESS_ID]]) &&
|
||||
(YES == [self.lastEvent[EVENT_DEVICE] isEqual:event[EVENT_DEVICE]]) &&
|
||||
(YES == [self.lastEvent[EVENT_DEVICE_STATUS] isEqual:event[EVENT_DEVICE_STATUS]]) )
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"alert for %@ would be same as previous (%@), so ignoring", event, self.lastEvent]);
|
||||
|
||||
//update
|
||||
self.lastEvent = event;
|
||||
|
||||
//bail to ignore
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
}//'same' event check
|
||||
|
||||
//update last event
|
||||
self.lastEvent = event;
|
||||
|
||||
//always (manually) load preferences
|
||||
preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
|
||||
|
||||
//check if user wants to ingnore inactive alerts
|
||||
if( (YES == [preferences[PREF_DISABLE_INACTIVE] boolValue]) &&
|
||||
(YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) )
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"user has decided to ingore 'inactive' events, so ingoring A/V going to disable state");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//add action
|
||||
// ->device went inactive
|
||||
if(YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]])
|
||||
|
@ -993,9 +1093,6 @@ bail:
|
|||
// ->for activated audio/video; allow/block
|
||||
else
|
||||
{
|
||||
//get process name
|
||||
processName = getProcessName([event[EVENT_PROCESS_ID] intValue]);
|
||||
|
||||
//set other button title
|
||||
notification.otherButtonTitle = @"allow";
|
||||
|
||||
|
@ -1004,7 +1101,7 @@ bail:
|
|||
|
||||
//set pid/name/device into user info
|
||||
// ->allows code to whitelist proc and/or kill proc (later) if user clicks 'block'
|
||||
notification.userInfo = @{EVENT_PROCESS_ID:event[EVENT_PROCESS_ID], EVENT_PROCESS_NAME:processName, EVENT_DEVICE:deviceType};
|
||||
notification.userInfo = @{EVENT_PROCESS_ID:event[EVENT_PROCESS_ID], EVENT_PROCESS_PATH:processPath, EVENT_PROCESS_NAME:processName, EVENT_DEVICE:deviceType};
|
||||
|
||||
//set details
|
||||
// ->name of process using it / icon too?
|
||||
|
@ -1019,18 +1116,19 @@ bail:
|
|||
|
||||
//no process?
|
||||
// ->just add title / details
|
||||
if(nil == processName)
|
||||
if( (nil == processName) ||
|
||||
(YES == [processName isEqualToString:PROCESS_UNKNOWN]) )
|
||||
{
|
||||
//add
|
||||
[sysLogMsg appendFormat:@"%@ (%@)", title, details];
|
||||
}
|
||||
|
||||
//process
|
||||
// ->add title / details / process path
|
||||
// ->add title / details / process name / process path
|
||||
else
|
||||
{
|
||||
//add
|
||||
[sysLogMsg appendFormat:@"%@ (process: %@, %@)", title, details, processName];
|
||||
[sysLogMsg appendFormat:@"%@ (%@, process: %@/%@)", title, details, processName, processPath];
|
||||
}
|
||||
|
||||
//write it out to syslog
|
||||
|
@ -1052,6 +1150,41 @@ bail:
|
|||
//deliver notification
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
|
||||
|
||||
//set flag saying we showed an 'activated' alert
|
||||
// ->allows us to ignore 'inactive' events that had a whitelisted 'activate' event
|
||||
if(YES == [DEVICE_ACTIVE isEqual:event[EVENT_DEVICE_STATUS]])
|
||||
{
|
||||
//audio
|
||||
if(SOURCE_AUDIO.intValue == deviceType.intValue)
|
||||
{
|
||||
//set
|
||||
self.showAudioDeactivation = YES;
|
||||
}
|
||||
//video
|
||||
else
|
||||
{
|
||||
//set
|
||||
self.showVideoDeactivation = YES;
|
||||
}
|
||||
}
|
||||
//inactive alert
|
||||
// ->unset flags
|
||||
else
|
||||
{
|
||||
//audio
|
||||
if(SOURCE_AUDIO.intValue == deviceType.intValue)
|
||||
{
|
||||
//set
|
||||
self.showAudioDeactivation = NO;
|
||||
}
|
||||
//video
|
||||
else
|
||||
{
|
||||
//set
|
||||
self.showVideoDeactivation = NO;
|
||||
}
|
||||
}
|
||||
|
||||
//for 'went inactive' notification
|
||||
// ->automatically close after some time
|
||||
if(YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]])
|
||||
|
@ -1128,7 +1261,7 @@ bail:
|
|||
if(YES != notification.hasActionButton)
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"popup w/o an action, no need to do anything");
|
||||
logMsg(LOG_DEBUG, @"popup without an action, no need to do anything");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
|
@ -1159,9 +1292,23 @@ bail:
|
|||
|
||||
//check if user clicked 'allow' via user info (since OS doesn't directly deliver this)
|
||||
// ->if allow was clicked, show a popup w/ option to rember ('whitelist') the application
|
||||
// don't do this for 'block' since that kills the app, so obv, that'd be bad to always do!
|
||||
if( (nil != notification.userInfo) &&
|
||||
(NSUserNotificationActivationTypeAdditionalActionClicked == [notification.userInfo[@"activationType"] integerValue]) )
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"user clicked 'allow'");
|
||||
|
||||
//can't remember process that we didn't find the path for
|
||||
if(YES == [notification.userInfo[EVENT_PROCESS_PATH] isEqualToString:PROCESS_UNKNOWN])
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"don't have a process path, so not displaying whitelisting popup");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//alloc/init settings window
|
||||
if(nil == self.rememberWindowController)
|
||||
{
|
||||
|
@ -1177,16 +1324,13 @@ bail:
|
|||
|
||||
//manually configure
|
||||
// ->invoke here as the outlets will be set
|
||||
[self.rememberWindowController configure:notification];
|
||||
[self.rememberWindowController configure:notification avMonitor:self];
|
||||
|
||||
//make it key window
|
||||
[self.rememberWindowController.window makeKeyAndOrderFront:self];
|
||||
|
||||
//make window front
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"user clicked 'allow'");
|
||||
}
|
||||
|
||||
//when user clicks 'block'
|
||||
|
@ -1367,7 +1511,7 @@ bail:
|
|||
}
|
||||
|
||||
//generate notification
|
||||
[self generateNotification:@{EVENT_TIMESTAMP:[NSDate date], EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:DEVICE_ACTIVE, EVENT_PROCESS_ID:processID}];
|
||||
[self generateNotification:[@{EVENT_TIMESTAMP:[NSDate date], EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:DEVICE_ACTIVE, EVENT_PROCESS_ID:processID} mutableCopy]];
|
||||
}
|
||||
|
||||
//signal sema
|
||||
|
|
|
@ -17,17 +17,17 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
<string>1.1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<string>1.1.0</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright (c) 2016 Objective-See. All rights reserved.</string>
|
||||
<string>Copyright (c) 2017 Objective-See. All rights reserved.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@class AVMonitor;
|
||||
|
||||
@interface RememberWindowController : NSWindowController <NSWindowDelegate>
|
||||
{
|
||||
|
||||
|
@ -15,12 +17,21 @@
|
|||
|
||||
/* PROPERTIES */
|
||||
|
||||
//process path
|
||||
// ->used for whitelisting
|
||||
@property (nonatomic, retain)NSString* processPath;
|
||||
|
||||
//instance of av monitor
|
||||
@property (nonatomic, retain)AVMonitor* avMonitor;
|
||||
|
||||
//version label/string
|
||||
@property (weak) IBOutlet NSTextField *windowText;
|
||||
|
||||
|
||||
/* METHODS */
|
||||
|
||||
//configure window w/ dynamic text
|
||||
-(void)configure:(NSUserNotification*)notification;
|
||||
//save stuff into iVars
|
||||
// ->configure window w/ dynamic text
|
||||
-(void)configure:(NSUserNotification*)notification avMonitor:(AVMonitor*)monitor;
|
||||
|
||||
@end
|
||||
|
|
|
@ -7,11 +7,18 @@
|
|||
//
|
||||
|
||||
#import "Consts.h"
|
||||
#import "Logging.h"
|
||||
#import "AVMonitor.h"
|
||||
#import "Utilities.h"
|
||||
#import "../Shared/XPCProtocol.h"
|
||||
#import "RemeberWindowController.h"
|
||||
|
||||
|
||||
@implementation RememberWindowController
|
||||
|
||||
@synthesize avMonitor;
|
||||
@synthesize processPath;
|
||||
|
||||
//@synthesize versionLabel;
|
||||
|
||||
//automatically called when nib is loaded
|
||||
|
@ -50,8 +57,9 @@
|
|||
}
|
||||
*/
|
||||
|
||||
//configure window w/ dynamic text
|
||||
-(void)configure:(NSUserNotification*)notification
|
||||
//save stuff into iVars
|
||||
// ->configure window w/ dynamic text
|
||||
-(void)configure:(NSUserNotification*)notification avMonitor:(AVMonitor*)monitor;
|
||||
{
|
||||
//process ID
|
||||
NSNumber* processID = nil;
|
||||
|
@ -62,12 +70,19 @@
|
|||
//device type
|
||||
NSString* deviceType = nil;
|
||||
|
||||
//save monitor into iVar
|
||||
self.avMonitor = monitor;
|
||||
|
||||
//grab process id
|
||||
processID = notification.userInfo[EVENT_PROCESS_ID];
|
||||
|
||||
//grab process name
|
||||
processName = notification.userInfo[EVENT_PROCESS_NAME];
|
||||
|
||||
//grab process path
|
||||
// ->saved into iVar for whitelisting
|
||||
self.processPath = notification.userInfo[EVENT_PROCESS_PATH];
|
||||
|
||||
//set device type for audio
|
||||
if(SOURCE_AUDIO.intValue == [notification.userInfo[EVENT_DEVICE] intValue])
|
||||
{
|
||||
|
@ -87,19 +102,65 @@
|
|||
return;
|
||||
}
|
||||
|
||||
//automatically invoked when user clicks button 'yes' / 'no'
|
||||
//automatically invoked when user clicks button 'Allow'
|
||||
-(IBAction)buttonHandler:(id)sender
|
||||
{
|
||||
//xpc connection
|
||||
__block NSXPCConnection* xpcConnection = nil;
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"handling user response for 'allow' popup: %ld", (long)((NSButton*)sender).tag]);
|
||||
|
||||
//handle 'always allow' (whitelist) button
|
||||
if(BUTTON_ALWAYS_ALLOW == ((NSButton*)sender).tag)
|
||||
{
|
||||
//TODO: whitelist
|
||||
//init XPC
|
||||
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
|
||||
|
||||
//set remote object interface
|
||||
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
|
||||
|
||||
//resume
|
||||
[xpcConnection resume];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"sending XPC message to whitelist");
|
||||
|
||||
//invoke XPC method 'whitelistProcess' to add process to white list
|
||||
[[xpcConnection remoteObjectProxy] whitelistProcess:self.processPath reply:^(BOOL wasWhitelisted)
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"got XPC response: %d", wasWhitelisted]);
|
||||
|
||||
//reload whitelist on success
|
||||
if(YES == wasWhitelisted)
|
||||
{
|
||||
//reload AVMonitor's whitelist
|
||||
[self.avMonitor loadWhitelist];
|
||||
}
|
||||
//err
|
||||
// ->log msg
|
||||
else
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to whitelist: %@", self.processPath]);
|
||||
}
|
||||
|
||||
//close connection
|
||||
[xpcConnection invalidate];
|
||||
|
||||
//nil out
|
||||
xpcConnection = nil;
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
//always close
|
||||
[self.window close];
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
@end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11542"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -40,19 +40,8 @@
|
|||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" tag="101" translatesAutoresizingMaskIntoConstraints="NO" id="HZZ-Es-mpy">
|
||||
<rect key="frame" x="352" y="11" width="56" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="no" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="J9x-sM-h9S">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="buttonHandler:" target="-2" id="yBL-R5-DDG"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" tag="100" translatesAutoresizingMaskIntoConstraints="NO" id="RKg-ba-EQ4">
|
||||
<rect key="frame" x="202" y="11" width="127" height="32"/>
|
||||
<rect key="frame" x="177" y="11" width="127" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="yes, always" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="0OU-M1-ErX">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -62,6 +51,17 @@
|
|||
<action selector="buttonHandler:" target="-2" id="LNd-1i-TBg"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" tag="101" translatesAutoresizingMaskIntoConstraints="NO" id="HZZ-Es-mpy">
|
||||
<rect key="frame" x="310" y="11" width="98" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="just once" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="J9x-sM-h9S">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="buttonHandler:" target="-2" id="yBL-R5-DDG"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</view>
|
||||
<connections>
|
||||
|
|
|
@ -15,4 +15,7 @@
|
|||
|
||||
/* FUNCTION DEFINITIONS */
|
||||
|
||||
//send XPC message to remove process from whitelist file
|
||||
void unWhiteList(NSString* process);
|
||||
|
||||
#endif /* main_h */
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#import "main.h"
|
||||
#import "Logging.h"
|
||||
#import "Utilities.h"
|
||||
#import "../Shared/XPCProtocol.h"
|
||||
|
||||
//go go go
|
||||
// ->either install/uninstall, or just launch normally
|
||||
|
@ -20,11 +21,11 @@ int main(int argc, const char * argv[])
|
|||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"starting login item (args: %@/user: %@)", [[NSProcessInfo processInfo] arguments], NSUserName()]);
|
||||
|
||||
//check for uninstall/install flags
|
||||
//check for uninstall/install flags, and process to remove from whitelist
|
||||
if(2 == argc)
|
||||
{
|
||||
//install
|
||||
if(0 == strcmp(argv[1], ACTION_INSTALL.UTF8String))
|
||||
if(0 == strcmp(argv[1], CMD_INSTALL))
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"running install logic");
|
||||
|
@ -48,7 +49,7 @@ int main(int argc, const char * argv[])
|
|||
goto bail;
|
||||
}
|
||||
//uninstall
|
||||
else if(0 == strcmp(argv[1], ACTION_UNINSTALL.UTF8String))
|
||||
else if(0 == strcmp(argv[1], CMD_UNINSTALL))
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"running uninstall logic");
|
||||
|
@ -68,10 +69,22 @@ int main(int argc, const char * argv[])
|
|||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"removed preferences from: %@", [APP_PREFERENCES stringByExpandingTildeInPath]]);
|
||||
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//assume its a path to a process to remove from whitelist
|
||||
else
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"running 'un-whitelist me' logic");
|
||||
|
||||
//remove from whitelist file
|
||||
unWhiteList([NSString stringWithUTF8String:argv[1]]);
|
||||
|
||||
//don't bail
|
||||
// ->let it start (as it was killed)
|
||||
}
|
||||
}
|
||||
|
||||
//launch app normally
|
||||
|
@ -82,3 +95,45 @@ bail:
|
|||
|
||||
return iReturn;
|
||||
}
|
||||
|
||||
//send XPC message to remove process from whitelist file
|
||||
void unWhiteList(NSString* process)
|
||||
{
|
||||
//xpc connection
|
||||
__block NSXPCConnection* xpcConnection = nil;
|
||||
|
||||
//init XPC
|
||||
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
|
||||
|
||||
//set remote object interface
|
||||
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
|
||||
|
||||
//resume
|
||||
[xpcConnection resume];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"sending XPC message to remove %@ from whitelist file", process]);
|
||||
|
||||
//invoke XPC method 'whitelistProcess' to add process to white list
|
||||
[[xpcConnection remoteObjectProxy] unWhitelistProcess:process reply:^(BOOL wasRemoved)
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"got XPC response: %d", wasRemoved]);
|
||||
|
||||
//err msg ono failure
|
||||
if(YES != wasRemoved)
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to remove %@ from whitelist", process]);
|
||||
}
|
||||
|
||||
//close connection
|
||||
[xpcConnection invalidate];
|
||||
|
||||
//nil out
|
||||
xpcConnection = nil;
|
||||
|
||||
}];
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
#import "InfoWindowController.h"
|
||||
#import "AboutWindowController.h"
|
||||
#import "RulesWindowController.h"
|
||||
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
|
||||
|
@ -47,6 +49,9 @@
|
|||
//about window controller
|
||||
@property(nonatomic, retain)AboutWindowController* aboutWindowController;
|
||||
|
||||
//rules
|
||||
@property(nonatomic, retain)RulesWindowController* rulesWindowController;
|
||||
|
||||
//overlay view
|
||||
@property (weak) IBOutlet NSView *overlay;
|
||||
|
||||
|
@ -80,8 +85,8 @@
|
|||
//'manage rules' button handler
|
||||
-(IBAction)manageRules:(id)sender;
|
||||
|
||||
//start the login item
|
||||
-(void)startLoginItem:(BOOL)shouldRestart;
|
||||
//(re)start the login item
|
||||
-(void)startLoginItem:(BOOL)shouldRestart args:(NSArray*)args;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#import "Utilities.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
|
||||
@interface AppDelegate ()
|
||||
|
||||
@property (weak) IBOutlet NSWindow *window;
|
||||
|
@ -21,6 +20,7 @@
|
|||
|
||||
@synthesize infoWindowController;
|
||||
@synthesize aboutWindowController;
|
||||
@synthesize rulesWindowController;
|
||||
|
||||
//center window
|
||||
// ->also make front, init title bar, etc
|
||||
|
@ -29,15 +29,15 @@
|
|||
//center
|
||||
[self.window center];
|
||||
|
||||
//set button states
|
||||
[self setButtonStates];
|
||||
|
||||
//make it key window
|
||||
[self.window makeKeyAndOrderFront:self];
|
||||
|
||||
//make window front
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
||||
//set button states
|
||||
[self setButtonStates];
|
||||
|
||||
//set title
|
||||
self.window.title = [NSString stringWithFormat:@"OverSight Preferences (v. %@)", getAppVersion()];
|
||||
|
||||
|
@ -48,6 +48,9 @@
|
|||
// ->init user interface
|
||||
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"OverSight Preferences App Launched");
|
||||
|
||||
//register for hotkey presses
|
||||
// ->for now, just cmd+q to quit app
|
||||
[self registerKeypressHandler];
|
||||
|
@ -69,7 +72,8 @@
|
|||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||
^{
|
||||
//start
|
||||
[self startLoginItem:NO];
|
||||
// -> 'NO' means don't start if already running
|
||||
[self startLoginItem:NO args:nil];
|
||||
});
|
||||
|
||||
return;
|
||||
|
@ -109,6 +113,7 @@
|
|||
}
|
||||
|
||||
//register handler for hot keys
|
||||
// ->for now, it just handles cmd+q to quit
|
||||
-(void)registerKeypressHandler
|
||||
{
|
||||
//event handler
|
||||
|
@ -226,12 +231,15 @@ bail:
|
|||
//set
|
||||
preferences[PREF_RUN_HEADLESS] = [NSNumber numberWithBool:[sender state]];
|
||||
|
||||
//save em now so new instance of login item can read them
|
||||
[preferences writeToFile:[APP_PREFERENCES stringByExpandingTildeInPath] atomically:YES];
|
||||
|
||||
//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];
|
||||
[self startLoginItem:YES args:nil];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -305,8 +313,11 @@ bail:
|
|||
//alloc string
|
||||
versionString = [NSMutableString string];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"checking for new version");
|
||||
|
||||
//check if available version is newer
|
||||
// ->show update window
|
||||
// ->show update popup/window
|
||||
if(YES == isNewVersion(versionString))
|
||||
{
|
||||
//dbg msg
|
||||
|
@ -343,7 +354,7 @@ bail:
|
|||
}
|
||||
|
||||
//no new version
|
||||
// ->stop animations/just (debug) log msg
|
||||
// ->stop animations, etc
|
||||
else
|
||||
{
|
||||
//dbg msg
|
||||
|
@ -359,18 +370,17 @@ bail:
|
|||
self.versionLabel.hidden = NO;
|
||||
|
||||
//set message
|
||||
self.versionLabel.stringValue = @"No new versions";
|
||||
self.versionLabel.stringValue = @"no new versions";
|
||||
|
||||
//re-draw
|
||||
[self.versionLabel displayIfNeeded];
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//start the login item
|
||||
-(void)startLoginItem:(BOOL)shouldRestart
|
||||
//(re)start the login item
|
||||
-(void)startLoginItem:(BOOL)shouldRestart args:(NSArray*)args
|
||||
{
|
||||
//path to login item
|
||||
NSString* loginItem = nil;
|
||||
|
@ -378,50 +388,122 @@ bail:
|
|||
//login item's pid
|
||||
pid_t loginItemPID = -1;
|
||||
|
||||
//get pid of login item
|
||||
//error
|
||||
NSError* error = nil;
|
||||
|
||||
//config (args, etc)
|
||||
// ->can't be nil, so init to blank here
|
||||
NSDictionary* configuration = @{};
|
||||
|
||||
//get pid of login item for user
|
||||
loginItemPID = getProcessID(@"OverSight Helper", getuid());
|
||||
|
||||
//already running?
|
||||
// ->kill the login item
|
||||
if( (YES == shouldRestart) &&
|
||||
(-1 != loginItemPID) )
|
||||
//no need to start if already running
|
||||
// ->well, and if 'shouldRestart' is not set
|
||||
if( (-1 != loginItemPID) &&
|
||||
(YES != shouldRestart) )
|
||||
{
|
||||
//kill
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"login item already running and 'shouldRestart' not set, so no need to start it!");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//running?
|
||||
// ->kill
|
||||
else if(-1 != loginItemPID)
|
||||
{
|
||||
//kill it
|
||||
kill(loginItemPID, SIGTERM);
|
||||
|
||||
//sleep
|
||||
sleep(2);
|
||||
[NSThread sleepForTimeInterval:1.0f];
|
||||
|
||||
//really kill
|
||||
kill(loginItemPID, SIGKILL);
|
||||
|
||||
//reset pid
|
||||
loginItemPID = -1;
|
||||
}
|
||||
|
||||
//start not already running
|
||||
if(-1 == loginItemPID)
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"starting login item");
|
||||
|
||||
//add overlay
|
||||
[self addOverlay:shouldRestart];
|
||||
|
||||
//init path to login item
|
||||
loginItem = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"/Contents/Library/LoginItems/OverSight Helper.app"];
|
||||
|
||||
//any args?
|
||||
// ->init config with them args
|
||||
if(nil != args)
|
||||
{
|
||||
//add args
|
||||
configuration = @{NSWorkspaceLaunchConfigurationArguments:args};
|
||||
}
|
||||
|
||||
//launch it
|
||||
[[NSWorkspace sharedWorkspace] launchApplicationAtURL:[NSURL fileURLWithPath:loginItem] options:NSWorkspaceLaunchWithoutActivation configuration:configuration error:&error];
|
||||
|
||||
//remove overlay
|
||||
[self removeOverlay];
|
||||
|
||||
//check if login launch was ok
|
||||
// ->do down here, since always want to remove overlay
|
||||
if(nil != error)
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to start login item, %@/%@", loginItem, error]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//add overlay to main window
|
||||
-(void)addOverlay:(BOOL)restarting
|
||||
{
|
||||
//show overlay view on main thread
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
//frame
|
||||
NSRect frame = {0};
|
||||
|
||||
//pre-req
|
||||
[self.overlay setWantsLayer:YES];
|
||||
|
||||
//round edges
|
||||
[self.overlay.layer setCornerRadius: 10];
|
||||
//get main window's frame
|
||||
frame = self.window.frame;
|
||||
|
||||
//set origin to 0/0
|
||||
frame.origin = CGPointZero;
|
||||
|
||||
//update overlay to take up entire window
|
||||
self.overlay.frame = frame;
|
||||
|
||||
//set overlay's view color to white
|
||||
self.overlay.layer.backgroundColor = [NSColor grayColor].CGColor;
|
||||
self.overlay.layer.backgroundColor = [NSColor whiteColor].CGColor;
|
||||
|
||||
//make it semi-transparent
|
||||
self.overlay.alphaValue = 0.85;
|
||||
|
||||
//show it
|
||||
self.overlay.hidden = NO;
|
||||
//set start message
|
||||
if(YES != restarting)
|
||||
{
|
||||
//set
|
||||
self.statusMessage.stringValue = @"starting monitor...";
|
||||
}
|
||||
//set restart message
|
||||
else
|
||||
{
|
||||
//set
|
||||
self.statusMessage.stringValue = @"(re)starting monitor...";
|
||||
}
|
||||
|
||||
//show message
|
||||
self.statusMessage.hidden = NO;
|
||||
|
@ -432,50 +514,24 @@ bail:
|
|||
//animate it
|
||||
[self.progressIndicator startAnimation:nil];
|
||||
|
||||
});
|
||||
}
|
||||
//add to main window
|
||||
[self.window.contentView addSubview:self.overlay];
|
||||
|
||||
//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];
|
||||
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
//hide overlay?
|
||||
if(-1 == loginItemPID)
|
||||
{
|
||||
//sleep to give message some more time
|
||||
[NSThread sleepForTimeInterval:1];
|
||||
|
||||
//update message
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
//stop spinner
|
||||
[self.progressIndicator stopAnimation:nil];
|
||||
|
||||
//update
|
||||
self.statusMessage.stringValue = @"started!";
|
||||
//show
|
||||
self.overlay.hidden = NO;
|
||||
|
||||
});
|
||||
|
||||
//sleep to give message some more time
|
||||
[NSThread sleepForTimeInterval:1];
|
||||
return;
|
||||
}
|
||||
|
||||
//hide overlay view on main thread
|
||||
//remove overlay from main window
|
||||
-(void)removeOverlay
|
||||
{
|
||||
//sleep to give message more viewing time
|
||||
[NSThread sleepForTimeInterval:2];
|
||||
|
||||
//remove overlay view on main thread
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
//hide spinner
|
||||
|
@ -487,17 +543,27 @@ bail:
|
|||
//hide message
|
||||
self.statusMessage.hidden = YES;
|
||||
|
||||
});
|
||||
//remove
|
||||
[self.overlay removeFromSuperview];
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//manage rules
|
||||
//button handle when user clicks 'Manage Rules'
|
||||
// ->just shwo the rules window
|
||||
-(IBAction)manageRules:(id)sender
|
||||
{
|
||||
//alloc
|
||||
rulesWindowController = [[RulesWindowController alloc] initWithWindowNibName:@"Rules"];
|
||||
|
||||
//center window
|
||||
[[self.rulesWindowController window] center];
|
||||
|
||||
//show it
|
||||
[self.rulesWindowController showWindow:self];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11542"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -19,7 +19,7 @@
|
|||
<outlet property="check4UpdatesNow" destination="M9W-tZ-0Z8" id="PKA-ah-dde"/>
|
||||
<outlet property="disableInactive" destination="Nf6-uL-uwr" id="7js-Vr-2nI"/>
|
||||
<outlet property="logActivity" destination="rVS-uv-DNf" id="5XJ-Ec-mNt"/>
|
||||
<outlet property="overlay" destination="bHg-M3-YdC" id="dTJ-FV-0Bt"/>
|
||||
<outlet property="overlay" destination="ewJ-2y-7Qh" id="tzX-7g-UCn"/>
|
||||
<outlet property="progressIndicator" destination="K08-2Z-AIq" id="0K0-Ao-9Ad"/>
|
||||
<outlet property="runHeadless" destination="7bS-hN-Wc7" id="dbA-Sv-fUR"/>
|
||||
<outlet property="spinner" destination="sph-QU-dL4" id="uwr-2X-sg8"/>
|
||||
|
@ -159,25 +159,6 @@
|
|||
<action selector="manageRules:" target="Voe-Tx-rLC" id="UuK-ti-hYN"/>
|
||||
</connections>
|
||||
</button>
|
||||
<customView hidden="YES" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bHg-M3-YdC">
|
||||
<rect key="frame" x="27" y="116" width="426" height="155"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="u4r-or-Aue">
|
||||
<rect key="frame" x="-5" y="68" width="437" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="starting monitor...." id="vap-C0-2c8">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" maxValue="100" bezeled="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="K08-2Z-AIq">
|
||||
<rect key="frame" x="197" y="96" width="32" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</progressIndicator>
|
||||
</subviews>
|
||||
</customView>
|
||||
</subviews>
|
||||
</view>
|
||||
</window>
|
||||
|
@ -205,6 +186,26 @@
|
|||
</items>
|
||||
<point key="canvasLocation" x="-49" y="-382"/>
|
||||
</menu>
|
||||
<customView id="ewJ-2y-7Qh" userLabel="Overlay">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="u4r-or-Aue">
|
||||
<rect key="frame" x="18" y="164" width="444" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="message..." id="vap-C0-2c8">
|
||||
<font key="font" size="17" name="Menlo-Bold"/>
|
||||
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" maxValue="100" bezeled="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="K08-2Z-AIq">
|
||||
<rect key="frame" x="224" y="197" width="32" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</progressIndicator>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="765" y="150"/>
|
||||
</customView>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="icon" width="512" height="512"/>
|
||||
|
|
|
@ -15,17 +15,17 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
<string>1.1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<string>1.1.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>LSUIElement</key>
|
||||
<false/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright (c) 2016 Objective-See. All rights reserved.</string>
|
||||
<string>Copyright (c) 2017 Objective-See. All rights reserved.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// RuleRow.h
|
||||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 4/4/15.
|
||||
// Copyright (c) 2017 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface RuleRow : NSTableRowView
|
||||
|
||||
@end
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// RuleRow.m
|
||||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 4/4/15.
|
||||
// Copyright (c) 2017 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RuleRow.h"
|
||||
|
||||
@implementation RuleRow
|
||||
|
||||
//custom row selection
|
||||
-(void)drawSelectionInRect:(NSRect)dirtyRect
|
||||
{
|
||||
//selection rect
|
||||
NSRect selectionRect = {0};
|
||||
|
||||
//selection path
|
||||
NSBezierPath *selectionPath = nil;
|
||||
|
||||
//highlight selected rows
|
||||
if(self.selectionHighlightStyle != NSTableViewSelectionHighlightStyleNone)
|
||||
{
|
||||
//make selection rect
|
||||
selectionRect = NSInsetRect(self.bounds, 2.5, 2.5);
|
||||
|
||||
//set stroke
|
||||
[[NSColor colorWithCalibratedWhite:.65 alpha:1.0] setStroke];
|
||||
|
||||
//set fill
|
||||
[[NSColor colorWithCalibratedWhite:.82 alpha:1.0] setFill];
|
||||
|
||||
//create selection path
|
||||
// ->with rounded corners
|
||||
selectionPath = [NSBezierPath bezierPathWithRoundedRect:selectionRect xRadius:5 yRadius:5];
|
||||
|
||||
//fill
|
||||
[selectionPath fill];
|
||||
|
||||
//stroke
|
||||
[selectionPath stroke];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// RuleRowCell.h
|
||||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 4/6/15.
|
||||
// Copyright (c) 2017 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface RuleRowCell : NSTableCellView
|
||||
|
||||
@end
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// kkRowCell.m
|
||||
// KnockKnock
|
||||
//
|
||||
// Created by Patrick Wardle on 4/6/15.
|
||||
// Copyright (c) 2015 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RuleRowCell.h"
|
||||
|
||||
@implementation RuleRowCell
|
||||
|
||||
-(void)drawRect:(NSRect)dirtyRect{
|
||||
[super drawRect:dirtyRect];
|
||||
|
||||
// Drawing code here.
|
||||
}
|
||||
|
||||
-(void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle
|
||||
{
|
||||
[super setBackgroundStyle: NSBackgroundStyleLight];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,161 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="RulesWindowController">
|
||||
<connections>
|
||||
<outlet property="tableView" destination="rpa-sZ-jQp" id="SvF-Yi-OKB"/>
|
||||
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="White Listed Applications" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="611" height="270"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1058"/>
|
||||
<value key="minSize" type="size" width="500" height="100"/>
|
||||
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="611" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="56" horizontalPageScroll="10" verticalLineScroll="56" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gth-To-Lf2">
|
||||
<rect key="frame" x="-1" y="-1" width="613" height="272"/>
|
||||
<clipView key="contentView" id="ubO-Ur-NA2">
|
||||
<rect key="frame" x="1" y="0.0" width="611" height="271"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="54" rowSizeStyle="automatic" headerView="MjO-gV-r1W" viewBased="YES" id="rpa-sZ-jQp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="611" height="248"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn width="608" minWidth="40" maxWidth="1000" id="ocl-vV-SZu">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="pc6-VQ-D0H">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="itemCell" id="moN-VP-hzI" customClass="RuleRowCell">
|
||||
<rect key="frame" x="1" y="1" width="608" height="54"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView translatesAutoresizingMaskIntoConstraints="NO" id="JiF-Nx-dl5">
|
||||
<rect key="frame" x="3" y="7" width="40" height="40"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSColorPanel" id="v3A-16-OyI"/>
|
||||
</imageView>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" tag="100" translatesAutoresizingMaskIntoConstraints="NO" id="JMN-i9-vxR">
|
||||
<rect key="frame" x="54" y="21" width="523" height="26"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Item Category" id="2GD-5k-sEf">
|
||||
<font key="font" size="17" name="Menlo-Bold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" tag="101" translatesAutoresizingMaskIntoConstraints="NO" id="w9P-yZ-pho">
|
||||
<rect key="frame" x="54" y="8" width="523" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="519" id="kmq-rD-zZS"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Item Category Description" id="EZN-NC-GXx">
|
||||
<font key="font" size="11" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aWW-9r-3lC">
|
||||
<rect key="frame" x="583" y="19" width="14" height="15"/>
|
||||
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSStopProgressFreestandingTemplate" imagePosition="overlaps" alignment="center" imageScaling="proportionallyDown" inset="2" id="wdw-rZ-IR3">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="deleteRule:" target="-2" id="vM0-zk-yrN"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="JiF-Nx-dl5" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="7" id="0og-eM-P5A"/>
|
||||
<constraint firstItem="aWW-9r-3lC" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="20" id="2Hq-gF-RRx"/>
|
||||
<constraint firstAttribute="bottom" secondItem="JMN-i9-vxR" secondAttribute="bottom" constant="21" id="2lm-V9-MgG"/>
|
||||
<constraint firstItem="w9P-yZ-pho" firstAttribute="trailing" secondItem="JMN-i9-vxR" secondAttribute="trailing" id="3zL-0B-Yt0"/>
|
||||
<constraint firstItem="JMN-i9-vxR" firstAttribute="top" secondItem="JiF-Nx-dl5" secondAttribute="top" id="4ZQ-hO-YeB"/>
|
||||
<constraint firstAttribute="bottom" secondItem="aWW-9r-3lC" secondAttribute="bottom" constant="19" id="JXs-JW-Xg3"/>
|
||||
<constraint firstItem="w9P-yZ-pho" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="29" id="Rbh-tU-0B0"/>
|
||||
<constraint firstItem="JiF-Nx-dl5" firstAttribute="leading" secondItem="moN-VP-hzI" secondAttribute="leading" constant="3" id="UxO-Jr-Zzk"/>
|
||||
<constraint firstItem="w9P-yZ-pho" firstAttribute="leading" secondItem="JMN-i9-vxR" secondAttribute="leading" id="d1t-pb-hcW"/>
|
||||
<constraint firstItem="w9P-yZ-pho" firstAttribute="leading" secondItem="JiF-Nx-dl5" secondAttribute="trailing" constant="13" id="dD6-u5-DZw"/>
|
||||
<constraint firstItem="w9P-yZ-pho" firstAttribute="leading" secondItem="moN-VP-hzI" secondAttribute="leading" constant="56" id="mQY-GF-Ndc"/>
|
||||
<constraint firstAttribute="trailing" secondItem="aWW-9r-3lC" secondAttribute="trailing" constant="11" id="n4e-65-kcp"/>
|
||||
<constraint firstAttribute="bottom" secondItem="JiF-Nx-dl5" secondAttribute="bottom" constant="7" id="na5-b4-JAs"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="imageView" destination="JiF-Nx-dl5" id="pVf-M3-mAH"/>
|
||||
<outlet property="textField" destination="JMN-i9-vxR" id="sds-eR-7bO"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="-2" id="cSg-At-Ff8"/>
|
||||
<outlet property="delegate" destination="-2" id="gZb-ih-AfV"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="VKw-SH-33M">
|
||||
<rect key="frame" x="1" y="255" width="480" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="ecy-l3-ARc">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<tableHeaderView key="headerView" id="MjO-gV-r1W">
|
||||
<rect key="frame" x="0.0" y="0.0" width="611" height="23"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableHeaderView>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="gth-To-Lf2" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="-1" id="0Jc-HW-sRY"/>
|
||||
<constraint firstAttribute="trailing" secondItem="gth-To-Lf2" secondAttribute="trailing" constant="-1" id="GjA-Ta-LSr"/>
|
||||
<constraint firstAttribute="bottom" secondItem="gth-To-Lf2" secondAttribute="bottom" constant="-1" id="gFc-Mm-n7w"/>
|
||||
<constraint firstItem="gth-To-Lf2" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="-1" id="jrL-0c-zSr"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="205.5" y="162"/>
|
||||
</window>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" id="8rY-Kl-bJs">
|
||||
<rect key="frame" x="0.0" y="0.0" width="38" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="AAC-Kf-Jrf">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<point key="canvasLocation" x="145" y="385.5"/>
|
||||
</textField>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="NSColorPanel" width="32" height="32"/>
|
||||
<image name="NSStopProgressFreestandingTemplate" width="14" height="14"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// RulesWindowController.h
|
||||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 7/7/16.
|
||||
// Copyright (c) 2016 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface RulesWindowController : NSWindowController <NSWindowDelegate, NSTableViewDataSource, NSTableViewDelegate>
|
||||
|
||||
/* PROPERTIES */
|
||||
@property(nonatomic, retain)NSMutableArray* items;
|
||||
|
||||
//table view
|
||||
@property (weak) IBOutlet NSTableView *tableView;
|
||||
|
||||
/* METHODS */
|
||||
|
||||
//delete a rule
|
||||
- (IBAction)deleteRule:(id)sender;
|
||||
|
||||
@end
|
|
@ -0,0 +1,195 @@
|
|||
//
|
||||
// RulesWindowController.m
|
||||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 7/7/16.
|
||||
// Copyright (c) 2016 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#import "Consts.h"
|
||||
#import "RuleRow.h"
|
||||
#import "Utilities.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "../Shared/Logging.h"
|
||||
#import "RulesWindowController.h"
|
||||
#import "../Shared/XPCProtocol.h"
|
||||
|
||||
@interface RulesWindowController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RulesWindowController
|
||||
|
||||
@synthesize items;
|
||||
|
||||
//automatically called when nib is loaded
|
||||
// ->just center window
|
||||
-(void)awakeFromNib
|
||||
{
|
||||
//load whitelisted items
|
||||
self.items = [NSMutableArray arrayWithContentsOfFile:[[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] stringByAppendingPathComponent:FILE_WHITELIST]];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
-(void)windowDidLoad
|
||||
{
|
||||
//super
|
||||
[super windowDidLoad];
|
||||
|
||||
//load whitelisted items
|
||||
self.items = [NSMutableArray arrayWithContentsOfFile:[[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] stringByAppendingPathComponent:FILE_WHITELIST]];
|
||||
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
//table delegate
|
||||
// ->return number of rows
|
||||
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
||||
{
|
||||
//count
|
||||
return self.items.count;
|
||||
}
|
||||
|
||||
//table delegate method
|
||||
// ->return cell for row
|
||||
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
||||
{
|
||||
//cell
|
||||
NSTableCellView *tableCell = nil;
|
||||
|
||||
//process name
|
||||
NSString* processName = nil;
|
||||
|
||||
//process path
|
||||
NSString* processPath = nil;
|
||||
|
||||
//process icon
|
||||
NSImage* processIcon = nil;
|
||||
|
||||
//app bundle
|
||||
NSBundle* appBundle = nil;
|
||||
|
||||
//sanity check
|
||||
if(row >= self.items.count)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//grab process path
|
||||
processPath = [self.items objectAtIndex:row];
|
||||
|
||||
//try find an app bundle
|
||||
appBundle = findAppBundle(processPath);
|
||||
if(nil != appBundle)
|
||||
{
|
||||
//grab name from app's bundle
|
||||
processName = [appBundle infoDictionary][@"CFBundleName"];
|
||||
}
|
||||
|
||||
//still nil?
|
||||
// ->just grab from path
|
||||
if(nil == processName)
|
||||
{
|
||||
//from path
|
||||
processName = [processPath lastPathComponent];
|
||||
}
|
||||
|
||||
//grab icon
|
||||
processIcon = getIconForProcess(processPath);
|
||||
|
||||
//init table cell
|
||||
tableCell = [tableView makeViewWithIdentifier:@"itemCell" owner:self];
|
||||
if(nil == tableCell)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//set icon
|
||||
tableCell.imageView.image = processIcon;
|
||||
|
||||
//set (main) text
|
||||
tableCell.textField.stringValue = processName;
|
||||
|
||||
//set sub text
|
||||
[[tableCell viewWithTag:TABLE_ROW_SUB_TEXT_TAG] setStringValue:processPath];
|
||||
|
||||
//set detailed text color to gray
|
||||
((NSTextField*)[tableCell viewWithTag:TABLE_ROW_SUB_TEXT_TAG]).textColor = [NSColor grayColor];
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
// Return the result
|
||||
return tableCell;
|
||||
|
||||
}
|
||||
|
||||
//automatically invoked
|
||||
// ->create custom (sub-classed) NSTableRowView
|
||||
-(NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
|
||||
{
|
||||
//row view
|
||||
RuleRow* rowView = nil;
|
||||
|
||||
//row ID
|
||||
static NSString* const kRowIdentifier = @"RowView";
|
||||
|
||||
//try grab existing row view
|
||||
rowView = [tableView makeViewWithIdentifier:kRowIdentifier owner:self];
|
||||
|
||||
//make new if needed
|
||||
if(nil == rowView)
|
||||
{
|
||||
//create new
|
||||
// ->size doesn't matter
|
||||
rowView = [[RuleRow alloc] initWithFrame:NSZeroRect];
|
||||
|
||||
//set row ID
|
||||
rowView.identifier = kRowIdentifier;
|
||||
}
|
||||
|
||||
return rowView;
|
||||
}
|
||||
|
||||
//delete a whitelist item
|
||||
// ->gotta invoke the login item, as it can do that via XPC
|
||||
-(IBAction)deleteRule:(id)sender
|
||||
{
|
||||
//index of selected row
|
||||
NSInteger selectedRow = 0;
|
||||
|
||||
//rule
|
||||
NSString* processPath = nil;
|
||||
|
||||
//grab selected row
|
||||
selectedRow = [self.tableView rowForView:sender];
|
||||
|
||||
//extract selected item
|
||||
processPath = self.items[selectedRow];
|
||||
|
||||
//remove from items
|
||||
[self.items removeObject:processPath];
|
||||
|
||||
//reload table
|
||||
[self.tableView reloadData];
|
||||
|
||||
//restart login item in background
|
||||
// ->pass in process name so it can un-whitelist via XPC
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||
^{
|
||||
//restart
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) startLoginItem:YES args:@[processPath]];
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@end
|
|
@ -37,6 +37,10 @@
|
|||
7D9A7DEC1D8BE1E00091C1AF /* Exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C5311D659E580066232A /* Exception.m */; };
|
||||
7D9A7DF21D8F2C900091C1AF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7D6245841D87C43900870565 /* Images.xcassets */; };
|
||||
7DAF4B7F1D657192000DA31A /* StatusBarMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DAF4B7D1D656FD3000DA31A /* StatusBarMenu.m */; };
|
||||
7DC038021E87025100349474 /* RulesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DC038001E87025100349474 /* RulesWindowController.m */; };
|
||||
7DC038031E87025100349474 /* Rules.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7DC038011E87025100349474 /* Rules.xib */; };
|
||||
7DC038061E8716F700349474 /* RuleRowCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DC038051E8716F700349474 /* RuleRowCell.m */; };
|
||||
7DC038091E8717B200349474 /* RuleRow.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DC038081E8717B200349474 /* RuleRow.m */; };
|
||||
7DC9C8171D641A350017D143 /* OverSightXPC.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DC9C8161D641A350017D143 /* OverSightXPC.m */; };
|
||||
7DC9C8191D641A350017D143 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DC9C8181D641A350017D143 /* main.m */; };
|
||||
8B5755A119DA3E9500799E6B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5755A019DA3E9500799E6B /* main.m */; };
|
||||
|
@ -125,6 +129,13 @@
|
|||
7D9A7DEF1D8CADD10091C1AF /* main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = main.h; path = OverSightXPC/main.h; sourceTree = "<group>"; };
|
||||
7DAF4B7C1D656FD3000DA31A /* StatusBarMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StatusBarMenu.h; sourceTree = "<group>"; };
|
||||
7DAF4B7D1D656FD3000DA31A /* StatusBarMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StatusBarMenu.m; sourceTree = "<group>"; };
|
||||
7DC037FF1E87025100349474 /* RulesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RulesWindowController.h; sourceTree = "<group>"; };
|
||||
7DC038001E87025100349474 /* RulesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RulesWindowController.m; sourceTree = "<group>"; };
|
||||
7DC038011E87025100349474 /* Rules.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = Rules.xib; sourceTree = "<group>"; };
|
||||
7DC038041E8716F700349474 /* RuleRowCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RuleRowCell.h; sourceTree = "<group>"; };
|
||||
7DC038051E8716F700349474 /* RuleRowCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuleRowCell.m; sourceTree = "<group>"; };
|
||||
7DC038071E8717B200349474 /* RuleRow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RuleRow.h; sourceTree = "<group>"; };
|
||||
7DC038081E8717B200349474 /* RuleRow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuleRow.m; sourceTree = "<group>"; };
|
||||
7DC9C8121D641A350017D143 /* OverSightXPC.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = OverSightXPC.xpc; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7DC9C8151D641A350017D143 /* OverSightXPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OverSightXPC.h; sourceTree = "<group>"; };
|
||||
7DC9C8161D641A350017D143 /* OverSightXPC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OverSightXPC.m; sourceTree = "<group>"; };
|
||||
|
@ -262,9 +273,16 @@
|
|||
8B57559D19DA3E9500799E6B /* MainApp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7DC038071E8717B200349474 /* RuleRow.h */,
|
||||
7DC038081E8717B200349474 /* RuleRow.m */,
|
||||
7DC038041E8716F700349474 /* RuleRowCell.h */,
|
||||
7DC038051E8716F700349474 /* RuleRowCell.m */,
|
||||
8B5755A219DA3E9500799E6B /* AppDelegate.h */,
|
||||
8B5755A319DA3E9500799E6B /* AppDelegate.m */,
|
||||
8B5755A719DA3E9500799E6B /* MainMenu.xib */,
|
||||
7DC037FF1E87025100349474 /* RulesWindowController.h */,
|
||||
7DC038001E87025100349474 /* RulesWindowController.m */,
|
||||
7DC038011E87025100349474 /* Rules.xib */,
|
||||
8B57559E19DA3E9500799E6B /* Supporting Files */,
|
||||
);
|
||||
path = MainApp;
|
||||
|
@ -371,7 +389,7 @@
|
|||
8B57559319DA3E9500799E6B /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0810;
|
||||
LastUpgradeCheck = 0820;
|
||||
ORGANIZATIONNAME = "Cory Bohon";
|
||||
TargetAttributes = {
|
||||
7DC9C8111D641A350017D143 = {
|
||||
|
@ -431,6 +449,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7D9A7DF21D8F2C900091C1AF /* Images.xcassets in Resources */,
|
||||
7DC038031E87025100349474 /* Rules.xib in Resources */,
|
||||
7D6245921D87D46800870565 /* InfoWindow.xib in Resources */,
|
||||
7D17C53C1D659E580066232A /* icon.png in Resources */,
|
||||
8B5755A919DA3E9500799E6B /* MainMenu.xib in Resources */,
|
||||
|
@ -474,12 +493,15 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7DC038061E8716F700349474 /* RuleRowCell.m in Sources */,
|
||||
7DC038021E87025100349474 /* RulesWindowController.m in Sources */,
|
||||
7D6245911D87D39E00870565 /* InfoWindowController.m in Sources */,
|
||||
7D62458B1D87CE0900870565 /* AboutWindowController.m in Sources */,
|
||||
7D6245891D87C58400870565 /* Logging.m in Sources */,
|
||||
8B5755A419DA3E9500799E6B /* AppDelegate.m in Sources */,
|
||||
8B5755A119DA3E9500799E6B /* main.m in Sources */,
|
||||
7D17C53B1D659E580066232A /* Exception.m in Sources */,
|
||||
7DC038091E8717B200349474 /* RuleRow.m in Sources */,
|
||||
7D17C53F1D659E580066232A /* Utilities.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -586,6 +608,7 @@
|
|||
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
|
|
@ -385,6 +385,15 @@ bail:
|
|||
//assign
|
||||
candidateAudioProcs = [[intersection allObjects] mutableCopy];
|
||||
|
||||
//if there aren't any new i/o registy clients and only one new mach sender
|
||||
// ->use that! (e.g. Siri, reactivated)
|
||||
if( (0 == candidateAudioProcs.count) &&
|
||||
(1 == newSenders.count) )
|
||||
{
|
||||
//assign as candidate
|
||||
[candidateAudioProcs addObject:newSenders.firstObject];
|
||||
}
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu candidate audio procs: %@", (unsigned long)candidateAudioProcs.count, candidateAudioProcs]);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (c) 2016 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Consts.h"
|
||||
#import "Logging.h"
|
||||
#import "Utilities.h"
|
||||
#import "Enumerator.h"
|
||||
|
@ -75,6 +76,129 @@
|
|||
return;
|
||||
}
|
||||
|
||||
//whitelist a process
|
||||
-(void)whitelistProcess:(NSString*)processPath reply:(void (^)(BOOL))reply
|
||||
{
|
||||
//flag
|
||||
BOOL wasAdded = NO;
|
||||
|
||||
//path to whitelist
|
||||
NSString* path = nil;
|
||||
|
||||
//whitelist
|
||||
NSMutableArray* whiteList = nil;
|
||||
|
||||
//error
|
||||
NSError* error = nil;
|
||||
|
||||
//init path to whitelist
|
||||
path = [[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] stringByAppendingPathComponent:FILE_WHITELIST];
|
||||
|
||||
//load whitelist
|
||||
whiteList = [NSMutableArray arrayWithContentsOfFile:path];
|
||||
|
||||
//failed to load
|
||||
// ->might not exist yet, so alloc
|
||||
if(nil == whiteList)
|
||||
{
|
||||
//alloc
|
||||
whiteList = [NSMutableArray array];
|
||||
}
|
||||
|
||||
//add
|
||||
[whiteList addObject:processPath];
|
||||
|
||||
//check if intermediate dirs exist
|
||||
// ->create them if they aren't there yet
|
||||
if(YES != [[NSFileManager defaultManager] fileExistsAtPath:[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath]])
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"XPC: creating app support directory: %@", [APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath]]);
|
||||
|
||||
//create
|
||||
if(YES != [[NSFileManager defaultManager] createDirectoryAtPath: [APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] withIntermediateDirectories:YES attributes:nil error:&error])
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"XPC: failed to create directory: %@", error]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
//save to disk
|
||||
if(YES != [whiteList writeToFile:path atomically:YES])
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"XPC: failed to save %@ -> %@", processPath, path]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//happy
|
||||
wasAdded = YES;
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
//reply
|
||||
reply(wasAdded);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//remove a process from the whitelist file
|
||||
-(void)unWhitelistProcess:(NSString*)processPath reply:(void (^)(BOOL))reply
|
||||
{
|
||||
//flag
|
||||
BOOL wasRemoved = NO;
|
||||
|
||||
//path to whitelist
|
||||
NSString* path = nil;
|
||||
|
||||
//whitelist
|
||||
NSMutableArray* whiteList = nil;
|
||||
|
||||
//init path to whitelist
|
||||
path = [[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] stringByAppendingPathComponent:FILE_WHITELIST];
|
||||
|
||||
//load whitelist
|
||||
whiteList = [NSMutableArray arrayWithContentsOfFile:path];
|
||||
if(nil == whiteList)
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"XPC: failed to load whitelist from %@", path]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//remove item
|
||||
[whiteList removeObject:processPath];
|
||||
|
||||
//save to disk
|
||||
if(YES != [whiteList writeToFile:path atomically:YES])
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"XPC: failed to save updated whitelist to %@", path]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//happy
|
||||
wasRemoved = YES;
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
//reply
|
||||
reply(wasRemoved);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//kill a process
|
||||
-(void)killProcess:(NSNumber*)processID reply:(void (^)(BOOL))reply
|
||||
{
|
||||
|
@ -85,7 +209,7 @@
|
|||
if(-1 == kill(processID.intValue, SIGKILL))
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to kill %@, with %d", processID, errno]);
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"XPC: failed to kill %@, with %d", processID, errno]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 9/16/16.
|
||||
// Copyright © 2016 Cory Bohon. All rights reserved.
|
||||
// Copyright (c) 2016 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef main_h
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#define PRODUCT_URL @"https://objective-see.com/products/oversight.html"
|
||||
|
||||
//product version url
|
||||
#define PRODUCT_VERSION_URL @"https://objective-see.com/products/versions/oversight.json"
|
||||
#define PRODUCT_VERSION_URL @"https://objective-see.com/products.json"
|
||||
|
||||
//OS version x
|
||||
#define OS_MAJOR_VERSION_X 10
|
||||
|
@ -50,12 +50,22 @@
|
|||
// ->also button title
|
||||
#define ACTION_UNINSTALL @"Uninstall"
|
||||
|
||||
//button title
|
||||
// ->Close
|
||||
#define ACTION_CLOSE @"Close"
|
||||
|
||||
//flag to uninstall
|
||||
#define ACTION_UNINSTALL_FLAG 0
|
||||
|
||||
//flag to install
|
||||
#define ACTION_INSTALL_FLAG 1
|
||||
|
||||
//flag for partial uninstall (leave whitelist)
|
||||
#define UNINSTALL_PARIAL 0
|
||||
|
||||
//flag for full uninstall
|
||||
#define UNINSTALL_FULL 1
|
||||
|
||||
//error msg
|
||||
#define KEY_ERROR_MSG @"errorMsg"
|
||||
|
||||
|
@ -107,14 +117,22 @@
|
|||
//path to facetime
|
||||
#define FACE_TIME @"/Applications/FaceTime.app/Contents/MacOS/FaceTime"
|
||||
|
||||
//app support directory
|
||||
#define APP_SUPPORT_DIRECTORY @"~/Library/Application Support/Objective-See/OverSight"
|
||||
|
||||
//whitelist
|
||||
#define FILE_WHITELIST @"whitelist.plist"
|
||||
|
||||
//event keys
|
||||
//#define EVENT_SOURCE @"source"
|
||||
#define EVENT_DEVICE @"device"
|
||||
#define EVENT_TIMESTAMP @"timestamp"
|
||||
#define EVENT_TIMESTAMP @"timeStamp"
|
||||
#define EVENT_DEVICE_STATUS @"status"
|
||||
#define EVENT_PROCESS_ID @"processID"
|
||||
#define EVENT_PROCESS_NAME @"processName"
|
||||
#define EVENT_PROCESS_PATH @"processPath"
|
||||
|
||||
//unknown process
|
||||
#define PROCESS_UNKNOWN @"<unknown>"
|
||||
|
||||
//source audio
|
||||
#define SOURCE_AUDIO @0x1
|
||||
|
@ -128,6 +146,8 @@
|
|||
//no/close button
|
||||
#define BUTTON_NO 101
|
||||
|
||||
//id (tag) for detailed text in rules table
|
||||
#define TABLE_ROW_SUB_TEXT_TAG 101
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
// Copyright (c) 2016 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
//TODO: add os_log_with_type for 10.12+
|
||||
|
||||
#import "Consts.h"
|
||||
#import "Logging.h"
|
||||
#import "Utilities.h"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Utilities.h
|
||||
// WhatsYourSign
|
||||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 7/7/16.
|
||||
// Copyright (c) 2016 Objective-See. All rights reserved.
|
||||
|
@ -59,6 +59,10 @@ NSString* getProcessName(pid_t pid);
|
|||
// ->get the (first) instance of that process
|
||||
pid_t getProcessID(NSString* processName, uid_t userID);
|
||||
|
||||
//get an icon for a process
|
||||
// ->for apps, this will be app's icon, otherwise just a standard system one
|
||||
NSImage* getIconForProcess(NSString* path);
|
||||
|
||||
//wait until a window is non nil
|
||||
// ->then make it modal
|
||||
void makeModal(NSWindowController* windowController);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Utilities.m
|
||||
// WhatsYourSign
|
||||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 7/7/16.
|
||||
// Copyright (c) 2016 Objective-See. All rights reserved.
|
||||
|
@ -611,6 +611,89 @@ bail:
|
|||
return processID;
|
||||
}
|
||||
|
||||
|
||||
//get an icon for a process
|
||||
// ->for apps, this will be app's icon, otherwise just a standard system one
|
||||
NSImage* getIconForProcess(NSString* path)
|
||||
{
|
||||
//icon's file name
|
||||
NSString* iconFile = nil;
|
||||
|
||||
//icon's path
|
||||
NSString* iconPath = nil;
|
||||
|
||||
//icon's path extension
|
||||
NSString* iconExtension = nil;
|
||||
|
||||
//icon
|
||||
NSImage* icon = nil;
|
||||
|
||||
//system's document icon
|
||||
static NSData* documentIcon = nil;
|
||||
|
||||
//bundle
|
||||
NSBundle* appBundle = nil;
|
||||
|
||||
//first try grab bundle
|
||||
// ->then extact icon from this
|
||||
appBundle = findAppBundle(path);
|
||||
if(nil != appBundle)
|
||||
{
|
||||
//get file
|
||||
iconFile = appBundle.infoDictionary[@"CFBundleIconFile"];
|
||||
|
||||
//get path extension
|
||||
iconExtension = [iconFile pathExtension];
|
||||
|
||||
//if its blank (i.e. not specified)
|
||||
// ->go with 'icns'
|
||||
if(YES == [iconExtension isEqualTo:@""])
|
||||
{
|
||||
//set type
|
||||
iconExtension = @"icns";
|
||||
}
|
||||
|
||||
//set full path
|
||||
iconPath = [appBundle pathForResource:[iconFile stringByDeletingPathExtension] ofType:iconExtension];
|
||||
|
||||
//load it
|
||||
icon = [[NSImage alloc] initWithContentsOfFile:iconPath];
|
||||
}
|
||||
|
||||
//process is not an app or couldn't get icon
|
||||
// ->try to get it via shared workspace
|
||||
if( (nil == appBundle) ||
|
||||
(nil == icon) )
|
||||
{
|
||||
//extract icon
|
||||
icon = [[NSWorkspace sharedWorkspace] iconForFile:path];
|
||||
|
||||
//load system document icon
|
||||
// ->static var, so only load once
|
||||
if(nil == documentIcon)
|
||||
{
|
||||
//load
|
||||
documentIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:
|
||||
NSFileTypeForHFSTypeCode(kGenericDocumentIcon)] TIFFRepresentation];
|
||||
}
|
||||
|
||||
//if 'iconForFile' method doesn't find and icon, it returns the system 'document' icon
|
||||
// ->the system 'applicatoon' icon seems more applicable, so use that here...
|
||||
if(YES == [[icon TIFFRepresentation] isEqual:documentIcon])
|
||||
{
|
||||
//set icon to system 'applicaiton' icon
|
||||
icon = [[NSWorkspace sharedWorkspace]
|
||||
iconForFileType: NSFileTypeForHFSTypeCode(kGenericApplicationIcon)];
|
||||
}
|
||||
|
||||
//'iconForFileType' returns small icons
|
||||
// ->so set size to 128
|
||||
[icon setSize:NSMakeSize(128, 128)];
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
//determine if there is a new version
|
||||
// -1, YES or NO
|
||||
NSInteger isNewVersion(NSMutableString* versionString)
|
||||
|
@ -628,7 +711,7 @@ NSInteger isNewVersion(NSMutableString* versionString)
|
|||
installedVersion = getAppVersion();
|
||||
|
||||
//get latest version
|
||||
// ->will query internet (bb's website)
|
||||
// ->will query internet (obj-see website)
|
||||
latestVersion = getLatestVersion();
|
||||
if(nil == latestVersion)
|
||||
{
|
||||
|
@ -656,7 +739,7 @@ bail:
|
|||
NSString* getLatestVersion()
|
||||
{
|
||||
//version data
|
||||
NSData* versionData = nil;
|
||||
__block NSData* versionData = nil;
|
||||
|
||||
//version dictionary
|
||||
NSDictionary* versionDictionary = nil;
|
||||
|
@ -664,8 +747,22 @@ NSString* getLatestVersion()
|
|||
//latest version
|
||||
NSString* latestVersion = nil;
|
||||
|
||||
//run in background if main thread
|
||||
if(YES == [NSThread isMainThread])
|
||||
{
|
||||
//run in background
|
||||
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||
^{
|
||||
//get version data
|
||||
versionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSION_URL]];
|
||||
});
|
||||
}
|
||||
//no need to background
|
||||
else
|
||||
{
|
||||
//get version from remote URL
|
||||
versionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSION_URL]];
|
||||
}
|
||||
|
||||
//sanity check
|
||||
if(nil == versionData)
|
||||
|
@ -958,5 +1055,3 @@ bail:
|
|||
|
||||
return processID;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,6 +28,12 @@
|
|||
// ->allows enumerator to stop baselining (when active), etc
|
||||
-(void)updateAudioStatus:(unsigned int)status reply:(void (^)(void))reply;
|
||||
|
||||
//whitelist a process
|
||||
-(void)whitelistProcess:(NSString*)processPath reply:(void (^)(BOOL))reply;
|
||||
|
||||
//remove a process from the whitelist file
|
||||
-(void)unWhitelistProcess:(NSString*)processPath reply:(void (^)(BOOL))reply;
|
||||
|
||||
//kill a process
|
||||
-(void)killProcess:(NSNumber*)processID reply:(void (^)(BOOL))reply;
|
||||
|
||||
|
|
Loading…
Reference in New Issue