-improved cmdline processing
-ignore 'duplicate' events -added code/UI for whitelist popup
This commit is contained in:
parent
2c1fcc4a72
commit
eb635e54d9
|
@ -21,7 +21,7 @@ int main(int argc, const char * argv[])
|
|||
//handle '-install' / '-uninstall'
|
||||
// ->this performs non-UI logic for easier automated deployment
|
||||
if( (argc >= 2) &&
|
||||
(YES != [[NSString stringWithUTF8String:argv[1]] hasPrefix:@"-psn_"]) )
|
||||
( (0 == strcmp(argv[1], CMD_INSTALL)) || (0 == strcmp(argv[1], CMD_UNINSTALL)) ) )
|
||||
{
|
||||
//first check rooot
|
||||
if(0 != geteuid())
|
||||
|
@ -69,56 +69,44 @@ int main(int argc, const char * argv[])
|
|||
//happy
|
||||
retVar = 0;
|
||||
}
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
|
||||
//invalid arg
|
||||
else
|
||||
}//args
|
||||
|
||||
//check for r00t
|
||||
// ->then spawn self via auth exec
|
||||
if(0 != geteuid())
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"non-root installer instance");
|
||||
|
||||
//spawn as root
|
||||
if(YES != spawnAsRoot(argv[0]))
|
||||
{
|
||||
//err msg
|
||||
printf("\nERROR: '%s', is an invalid option\n\n", argv[1]);
|
||||
logMsg(LOG_ERR, @"failed to spawn self as r00t");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
}//args
|
||||
//happy
|
||||
retVar = 0;
|
||||
}
|
||||
|
||||
//no args
|
||||
//otherwise
|
||||
// ->just kick off app, as we're root now
|
||||
else
|
||||
{
|
||||
//check for r00t
|
||||
// ->then spawn self via auth exec
|
||||
if(0 != geteuid())
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"non-root installer instance");
|
||||
|
||||
//spawn as root
|
||||
if(YES != spawnAsRoot(argv[0]))
|
||||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, @"failed to spawn self as r00t");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//happy
|
||||
retVar = 0;
|
||||
}
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"root installer instance");
|
||||
|
||||
//otherwise
|
||||
// ->just kick off app, as we're root now
|
||||
else
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"root installer instance");
|
||||
|
||||
//app away
|
||||
retVar = NSApplicationMain(argc, (const char **)argv);
|
||||
}
|
||||
|
||||
}//no args
|
||||
|
||||
//app away
|
||||
retVar = NSApplicationMain(argc, (const char **)argv);
|
||||
}
|
||||
|
||||
}//pool
|
||||
|
||||
//bail
|
||||
|
|
|
@ -13,20 +13,13 @@
|
|||
#import <CoreMediaIO/CMIOHardware.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#import "RemeberWindowController.h"
|
||||
|
||||
/* DEFINES */
|
||||
|
||||
#define VIDEO_DISABLED 0x0
|
||||
#define VIDEO_ENABLED 0x1
|
||||
|
||||
#define EVENT_SOURCE @"source"
|
||||
#define EVENT_DEVICE @"device"
|
||||
#define EVENT_DEVICE_STATUS @"status"
|
||||
#define EVENT_PROCESS_ID @"processID"
|
||||
|
||||
#define SOURCE_AUDIO @0x1
|
||||
#define SOURCE_VIDEO @0x2
|
||||
|
||||
#define DEVICE_INACTIVE @0x0
|
||||
#define DEVICE_ACTIVE @0x1
|
||||
|
||||
|
@ -53,6 +46,17 @@
|
|||
//monitor thread
|
||||
@property(nonatomic, retain)NSThread* videoMonitorThread;
|
||||
|
||||
//remember popup/window controller
|
||||
@property(nonatomic, retain)RememberWindowController* rememberWindowController;
|
||||
|
||||
//last event
|
||||
@property(nonatomic, retain)NSDictionary* lastEvent;
|
||||
|
||||
//last notification
|
||||
@property(nonatomic, retain)NSString* lastNotification;
|
||||
|
||||
|
||||
|
||||
/* METHODS */
|
||||
|
||||
//kicks off thread to monitor
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
|
||||
@synthesize mic;
|
||||
@synthesize camera;
|
||||
@synthesize lastEvent;
|
||||
@synthesize audioActive;
|
||||
@synthesize videoActive;
|
||||
@synthesize lastNotification;
|
||||
@synthesize videoMonitorThread;
|
||||
@synthesize rememberWindowController;
|
||||
|
||||
//init
|
||||
-(id)init
|
||||
|
@ -443,6 +446,9 @@ bail:
|
|||
// ->update menu to show (all) devices & their status
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:devices];
|
||||
|
||||
//add timestamp
|
||||
event[EVENT_TIMESTAMP] = [NSDate date];
|
||||
|
||||
//add device
|
||||
event[EVENT_DEVICE] = self.camera;
|
||||
|
||||
|
@ -695,6 +701,9 @@ bail:
|
|||
// ->update menu to show (all) devices & their status
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:@[@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)},@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:@(self.videoActive)}]];
|
||||
|
||||
//add timestamp
|
||||
event[EVENT_TIMESTAMP] = [NSDate date];
|
||||
|
||||
//add device
|
||||
event[EVENT_DEVICE] = self.mic;
|
||||
|
||||
|
@ -860,6 +869,10 @@ bail:
|
|||
//notification
|
||||
NSUserNotification* notification = nil;
|
||||
|
||||
//device
|
||||
// ->audio or video
|
||||
NSNumber* deviceType = nil;
|
||||
|
||||
//title
|
||||
NSMutableString* title = nil;
|
||||
|
||||
|
@ -885,6 +898,32 @@ 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]];
|
||||
|
||||
|
@ -899,19 +938,24 @@ bail:
|
|||
goto bail;
|
||||
}
|
||||
|
||||
//set title
|
||||
// ->audio device
|
||||
//set device and title for audio
|
||||
if(YES == [event[EVENT_DEVICE] isKindOfClass:NSClassFromString(@"AVCaptureHALDevice")])
|
||||
{
|
||||
//add
|
||||
[title appendString:@"Audio Device"];
|
||||
|
||||
//set device
|
||||
deviceType = SOURCE_AUDIO;
|
||||
|
||||
}
|
||||
//add source
|
||||
// ->video device
|
||||
//set device and title for video
|
||||
else
|
||||
{
|
||||
//add
|
||||
[title appendString:@"Video Device"];
|
||||
|
||||
//set device
|
||||
deviceType = SOURCE_VIDEO;
|
||||
}
|
||||
|
||||
//add action
|
||||
|
@ -953,14 +997,14 @@ bail:
|
|||
processName = getProcessName([event[EVENT_PROCESS_ID] intValue]);
|
||||
|
||||
//set other button title
|
||||
notification.otherButtonTitle = @"allowz";
|
||||
notification.otherButtonTitle = @"allow";
|
||||
|
||||
//set action title
|
||||
notification.actionButtonTitle = @"block";
|
||||
|
||||
//set pid in user info
|
||||
// ->allows code to try kill proc (later) if user clicks 'block'
|
||||
notification.userInfo = @{EVENT_PROCESS_ID:event[EVENT_PROCESS_ID]};
|
||||
//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};
|
||||
|
||||
//set details
|
||||
// ->name of process using it / icon too?
|
||||
|
@ -999,6 +1043,9 @@ bail:
|
|||
//set subtitle
|
||||
[notification setSubtitle:details];
|
||||
|
||||
//set id
|
||||
notification.identifier = [[NSUUID UUID] UUIDString];
|
||||
|
||||
//set notification
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
|
||||
|
||||
|
@ -1058,6 +1105,24 @@ bail:
|
|||
//dbg msg
|
||||
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"user responded to notification: %@", notification]);
|
||||
|
||||
//ignore if this notification was already seen
|
||||
// ->need this logic, since have to determine if 'allow' was invoke indirectly
|
||||
if(nil != self.lastNotification)
|
||||
{
|
||||
//same?
|
||||
if(YES == [self.lastNotification isEqualToString:notification.identifier])
|
||||
{
|
||||
//update
|
||||
self.lastNotification = notification.identifier;
|
||||
|
||||
//ignore
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
//update
|
||||
self.lastNotification = notification.identifier;
|
||||
|
||||
//for alerts without an action
|
||||
// ->don't need to do anything!
|
||||
if(YES != notification.hasActionButton)
|
||||
|
@ -1092,11 +1157,33 @@ bail:
|
|||
syslog(LOG_ERR, "%s\n", sysLogMsg.UTF8String);
|
||||
}
|
||||
|
||||
//when user clicks 'allow'
|
||||
// ->show popup w/ option to whitelist
|
||||
if(notification.activationType == NSUserNotificationActivationTypeAdditionalActionClicked)
|
||||
//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
|
||||
if( (nil != notification.userInfo) &&
|
||||
(NSUserNotificationActivationTypeAdditionalActionClicked == [notification.userInfo[@"activationType"] integerValue]) )
|
||||
{
|
||||
//TODO: show popup
|
||||
//alloc/init settings window
|
||||
if(nil == self.rememberWindowController)
|
||||
{
|
||||
//alloc/init
|
||||
rememberWindowController = [[RememberWindowController alloc] initWithWindowNibName:@"RememberPopup"];
|
||||
}
|
||||
|
||||
//center window
|
||||
[[self.rememberWindowController window] center];
|
||||
|
||||
//show it
|
||||
[self.rememberWindowController showWindow:self];
|
||||
|
||||
//manually configure
|
||||
// ->invoke here as the outlets will be set
|
||||
[self.rememberWindowController configure:notification];
|
||||
|
||||
//make it key window
|
||||
[self.rememberWindowController.window makeKeyAndOrderFront:self];
|
||||
|
||||
//make window front
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"user clicked 'allow'");
|
||||
|
@ -1104,7 +1191,7 @@ bail:
|
|||
|
||||
//when user clicks 'block'
|
||||
// ->kill the process to block it
|
||||
else if(notification.activationType == NSUserNotificationActivationTypeActionButtonClicked)
|
||||
else if(NSUserNotificationActivationTypeActionButtonClicked == notification.activationType)
|
||||
{
|
||||
//dbg msg
|
||||
logMsg(LOG_DEBUG, @"user clicked 'block'");
|
||||
|
@ -1140,7 +1227,6 @@ bail:
|
|||
{
|
||||
//err msg
|
||||
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to kill/block: %@", processID]);
|
||||
|
||||
}
|
||||
|
||||
//close connection
|
||||
|
@ -1158,6 +1244,75 @@ bail:
|
|||
return;
|
||||
}
|
||||
|
||||
//manually monitor delivered notifications to see if user closes alert
|
||||
// ->can't detect 'allow' otherwise :/ (see: http://stackoverflow.com/questions/21110714/mac-os-x-nsusernotificationcenter-notification-get-dismiss-event-callback)
|
||||
-(void)userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification
|
||||
{
|
||||
//flag
|
||||
__block BOOL notificationStillPresent;
|
||||
|
||||
//user dictionary
|
||||
__block NSMutableDictionary* userInfo = nil;
|
||||
|
||||
//only process notifications have 'allow' / 'block'
|
||||
if(YES == notification.hasActionButton)
|
||||
{
|
||||
//monitor in background to see if alert was dismissed
|
||||
// ->invokes normal 'didActivateNotification' callback when alert is dimsissed
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||
^{
|
||||
//monitor all delivered notifications until it goes away
|
||||
do {
|
||||
|
||||
//reset
|
||||
notificationStillPresent = NO;
|
||||
|
||||
//check all delivered notifications
|
||||
for (NSUserNotification *nox in [[NSUserNotificationCenter defaultUserNotificationCenter] deliveredNotifications])
|
||||
{
|
||||
//check
|
||||
if(YES == [nox.identifier isEqualToString:notification.identifier])
|
||||
{
|
||||
//found!
|
||||
notificationStillPresent = YES;
|
||||
|
||||
//exit loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//nap if notification is still there
|
||||
if(YES == notificationStillPresent)
|
||||
{
|
||||
//nap
|
||||
[NSThread sleepForTimeInterval:0.25f];
|
||||
}
|
||||
|
||||
//keep monitoring until its gone
|
||||
} while(YES == notificationStillPresent);
|
||||
|
||||
//alert was dismissed
|
||||
// ->invoke 'didActivateNotification' to process if it was an 'allow/block' alert
|
||||
dispatch_async(dispatch_get_main_queue(),
|
||||
^{
|
||||
//grab user info dictionary
|
||||
userInfo = [notification.userInfo mutableCopy];
|
||||
|
||||
//add activation type
|
||||
userInfo[@"activationType"] = [NSNumber numberWithInteger:NSUserNotificationActivationTypeAdditionalActionClicked];
|
||||
|
||||
//update
|
||||
notification.userInfo = userInfo;
|
||||
|
||||
//deliver
|
||||
[self userNotificationCenter:center didActivateNotification:notification];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//monitor for new procs (video only at the moment)
|
||||
// ->runs until video is no longer in use (set elsewhere)
|
||||
-(void)monitor4Procs
|
||||
|
@ -1212,7 +1367,7 @@ bail:
|
|||
}
|
||||
|
||||
//generate notification
|
||||
[self generateNotification:@{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}];
|
||||
}
|
||||
|
||||
//signal sema
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// AboutWindowController.h
|
||||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 7/15/16.
|
||||
// Copyright (c) 2016 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface RememberWindowController : NSWindowController <NSWindowDelegate>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* PROPERTIES */
|
||||
|
||||
//version label/string
|
||||
@property (weak) IBOutlet NSTextField *windowText;
|
||||
|
||||
/* METHODS */
|
||||
|
||||
//configure window w/ dynamic text
|
||||
-(void)configure:(NSUserNotification*)notification;
|
||||
|
||||
@end
|
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// RememberWindowController.m
|
||||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 7/15/16.
|
||||
// Copyright (c) 2016 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Consts.h"
|
||||
#import "Utilities.h"
|
||||
#import "RemeberWindowController.h"
|
||||
|
||||
@implementation RememberWindowController
|
||||
|
||||
//@synthesize versionLabel;
|
||||
|
||||
//automatically called when nib is loaded
|
||||
// ->center window
|
||||
-(void)awakeFromNib
|
||||
{
|
||||
//center
|
||||
[self.window center];
|
||||
}
|
||||
|
||||
//automatically invoked when window is loaded
|
||||
// ->set to white
|
||||
-(void)windowDidLoad
|
||||
{
|
||||
//super
|
||||
[super windowDidLoad];
|
||||
|
||||
//make white
|
||||
[self.window setBackgroundColor: NSColor.whiteColor];
|
||||
|
||||
//set version sting
|
||||
//[self.versionLabel setStringValue:[NSString stringWithFormat:@"version: %@", getAppVersion()]];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
//automatically invoked when window is closing
|
||||
// ->make ourselves unmodal
|
||||
-(void)windowWillClose:(NSNotification *)notification
|
||||
{
|
||||
//make un-modal
|
||||
[[NSApplication sharedApplication] stopModal];
|
||||
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
//configure window w/ dynamic text
|
||||
-(void)configure:(NSUserNotification*)notification
|
||||
{
|
||||
//process ID
|
||||
NSNumber* processID = nil;
|
||||
|
||||
//process name
|
||||
NSString* processName = nil;
|
||||
|
||||
//device type
|
||||
NSString* deviceType = nil;
|
||||
|
||||
//grab process id
|
||||
processID = notification.userInfo[EVENT_PROCESS_ID];
|
||||
|
||||
//grab process name
|
||||
processName = notification.userInfo[EVENT_PROCESS_NAME];
|
||||
|
||||
//set device type for audio
|
||||
if(SOURCE_AUDIO.intValue == [notification.userInfo[EVENT_DEVICE] intValue])
|
||||
{
|
||||
//set
|
||||
deviceType = @"mic";
|
||||
}
|
||||
//set device type for mic
|
||||
else if(SOURCE_VIDEO.intValue == [notification.userInfo[EVENT_DEVICE] intValue])
|
||||
{
|
||||
//set
|
||||
deviceType = @"camera";
|
||||
}
|
||||
|
||||
//set text
|
||||
[self.windowText setStringValue:[NSString stringWithFormat:@"always allow %@ (%@) to use the %@?", processName, processID, deviceType]];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//automatically invoked when user clicks button 'yes' / 'no'
|
||||
-(IBAction)buttonHandler:(id)sender
|
||||
{
|
||||
//handle 'always allow' (whitelist) button
|
||||
if(BUTTON_ALWAYS_ALLOW == ((NSButton*)sender).tag)
|
||||
{
|
||||
//TODO: whitelist
|
||||
}
|
||||
|
||||
//always close
|
||||
[self.window close];
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
@end
|
|
@ -0,0 +1,77 @@
|
|||
<?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">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11542"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="RememberWindowController">
|
||||
<connections>
|
||||
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
|
||||
<outlet property="windowText" destination="OSm-xS-Dmd" id="Hi5-F0-gUp"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="422" height="123"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1058"/>
|
||||
<view key="contentView" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="422" height="123"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lEv-Wj-6S5">
|
||||
<rect key="frame" x="10" y="36" width="104" height="82"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon" id="xKf-GK-m0k"/>
|
||||
</imageView>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Eaf-yA-bbe">
|
||||
<rect key="frame" x="20" y="6" width="94" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="overSight" id="Ws8-bD-j2R"/>
|
||||
</imageView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OSm-xS-Dmd">
|
||||
<rect key="frame" x="120" y="47" width="290" height="56"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" truncatesLastVisibleLine="YES" sendsActionOnEndEditing="YES" id="bBK-v0-ypq">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="buttonHandler:" target="-2" id="LNd-1i-TBg"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="318" y="246.5"/>
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="icon" width="512" height="512"/>
|
||||
<image name="overSight" width="1396" height="286"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -18,6 +18,8 @@
|
|||
7D17CFE51D8133840017B475 /* AVMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17CFE11D81121E0017B475 /* AVMonitor.m */; };
|
||||
7D17D0101D8136C60017B475 /* OverSightXPC.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 7DC9C8121D641A350017D143 /* OverSightXPC.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
7D3B524C1D9B3A74006568D9 /* libbsm.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D9A7DED1D8CACE30091C1AF /* libbsm.tbd */; };
|
||||
7D6216731E078D65002C1774 /* RemeberWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D6216701E078C09002C1774 /* RemeberWindowController.m */; };
|
||||
7D6216741E07BB27002C1774 /* RememberPopup.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D62166E1E078BDD002C1774 /* RememberPopup.xib */; };
|
||||
7D62457C1D84FB8900870565 /* Enumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D62457B1D84FB8900870565 /* Enumerator.m */; };
|
||||
7D6245801D85348E00870565 /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D62457F1D85348E00870565 /* Utilities.m */; };
|
||||
7D6245851D87C43900870565 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7D6245841D87C43900870565 /* Images.xcassets */; };
|
||||
|
@ -108,6 +110,9 @@
|
|||
7D17CFE11D81121E0017B475 /* AVMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVMonitor.m; sourceTree = "<group>"; };
|
||||
7D17CFE21D81121E0017B475 /* AVMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVMonitor.h; sourceTree = "<group>"; };
|
||||
7D32457A1DA59A3700AE0711 /* main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = "<group>"; };
|
||||
7D62166E1E078BDD002C1774 /* RememberPopup.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RememberPopup.xib; sourceTree = "<group>"; };
|
||||
7D6216701E078C09002C1774 /* RemeberWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RemeberWindowController.m; sourceTree = "<group>"; };
|
||||
7D6216721E078C13002C1774 /* RemeberWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemeberWindowController.h; sourceTree = "<group>"; };
|
||||
7D62457A1D84FB8900870565 /* Enumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Enumerator.h; sourceTree = "<group>"; };
|
||||
7D62457B1D84FB8900870565 /* Enumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Enumerator.m; sourceTree = "<group>"; };
|
||||
7D62457E1D85348E00870565 /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
|
||||
|
@ -277,6 +282,9 @@
|
|||
8B5755C319DA3F9300799E6B /* LoginItem */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7D6216721E078C13002C1774 /* RemeberWindowController.h */,
|
||||
7D6216701E078C09002C1774 /* RemeberWindowController.m */,
|
||||
7D62166E1E078BDD002C1774 /* RememberPopup.xib */,
|
||||
7D17C5131D658FE20066232A /* Images */,
|
||||
8B5755C919DA3F9300799E6B /* AppDelegate.m */,
|
||||
7D17CFE21D81121E0017B475 /* AVMonitor.h */,
|
||||
|
@ -363,7 +371,7 @@
|
|||
8B57559319DA3E9500799E6B /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0800;
|
||||
LastUpgradeCheck = 0810;
|
||||
ORGANIZATIONNAME = "Cory Bohon";
|
||||
TargetAttributes = {
|
||||
7DC9C8111D641A350017D143 = {
|
||||
|
@ -435,6 +443,7 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7D6216741E07BB27002C1774 /* RememberPopup.xib in Resources */,
|
||||
7D9A7DE81D893E4F0091C1AF /* InfoWindow.xib in Resources */,
|
||||
7D6245951D87E14800870565 /* MainMenu.xib in Resources */,
|
||||
7D6245931D87E13200870565 /* icon.png in Resources */,
|
||||
|
@ -479,6 +488,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7D6216731E078D65002C1774 /* RemeberWindowController.m in Sources */,
|
||||
7D6245881D87C57600870565 /* Logging.m in Sources */,
|
||||
7D6245801D85348E00870565 /* Utilities.m in Sources */,
|
||||
7D17CFE51D8133840017B475 /* AVMonitor.m in Sources */,
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
/* METHODS */
|
||||
|
||||
//invoked when user clicks 'more info' button
|
||||
// ->open KK's webpage
|
||||
// ->open OverSights webpage
|
||||
- (IBAction)moreInfo:(id)sender;
|
||||
|
||||
@end
|
||||
|
|
|
@ -107,6 +107,26 @@
|
|||
//path to facetime
|
||||
#define FACE_TIME @"/Applications/FaceTime.app/Contents/MacOS/FaceTime"
|
||||
|
||||
//event keys
|
||||
//#define EVENT_SOURCE @"source"
|
||||
#define EVENT_DEVICE @"device"
|
||||
#define EVENT_TIMESTAMP @"timestamp"
|
||||
#define EVENT_DEVICE_STATUS @"status"
|
||||
#define EVENT_PROCESS_ID @"processID"
|
||||
#define EVENT_PROCESS_NAME @"processName"
|
||||
|
||||
|
||||
//source audio
|
||||
#define SOURCE_AUDIO @0x1
|
||||
|
||||
//source video
|
||||
#define SOURCE_VIDEO @0x2
|
||||
|
||||
//always allow button
|
||||
#define BUTTON_ALWAYS_ALLOW 100
|
||||
|
||||
//no/close button
|
||||
#define BUTTON_NO 101
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// PrefsWindowController.m
|
||||
// KnockKnock
|
||||
// OverSight
|
||||
//
|
||||
// Created by Patrick Wardle on 2/6/15.
|
||||
// Copyright (c) 2015 Objective-See, LLC. All rights reserved.
|
||||
|
|
Loading…
Reference in New Issue