'support us' window added to installer/about

wrapped debug statements with #ifdef DEBUG
improved NSTask to (hopefully) avoid hangs
added dictionary of activation alerts to help with deactivation alert logic
This commit is contained in:
Patrick Wardle 2017-04-02 13:12:42 -10:00
parent c6eaba30f2
commit a837ffdcf6
22 changed files with 723 additions and 278 deletions

View File

@ -23,6 +23,7 @@
7D17C5011D658DB90066232A /* ErrorWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4F81D658DB90066232A /* ErrorWindowController.xib */; };
7D17C5041D658DEC0066232A /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C5021D658DEC0066232A /* MainMenu.xib */; };
7D6245831D87C3D700870565 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7D6245821D87C3D700870565 /* Images.xcassets */; };
7DEABC481E907DF10024C6AB /* objectiveSee.png in Resources */ = {isa = PBXBuildFile; fileRef = 7DEABC471E907DF10024C6AB /* objectiveSee.png */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -54,6 +55,7 @@
7D24C8651D2CDEA7009932EE /* OverSight_Installer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OverSight_Installer.app; sourceTree = BUILT_PRODUCTS_DIR; };
7D6245821D87C3D700870565 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Images/Images.xcassets; sourceTree = "<group>"; };
7D9A7DE91D8A8BDA0091C1AF /* main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = main.h; path = Installer/main.h; sourceTree = SOURCE_ROOT; };
7DEABC471E907DF10024C6AB /* objectiveSee.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = objectiveSee.png; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -90,6 +92,7 @@
isa = PBXGroup;
children = (
7D17C4DF1D658D9F0066232A /* icon.png */,
7DEABC471E907DF10024C6AB /* objectiveSee.png */,
7D17C4E01D658D9F0066232A /* overSight.png */,
);
path = Images;
@ -118,11 +121,11 @@
7D17C5021D658DEC0066232A /* MainMenu.xib */,
7D17C4F61D658DB90066232A /* AppDelegate.h */,
7D17C4ED1D658DB90066232A /* AppDelegate.m */,
7D17C4F11D658DB90066232A /* ConfigureWindowController.h */,
7D17C4EE1D658DB90066232A /* ConfigureWindowController.m */,
7D17C4EF1D658DB90066232A /* ConfigureWindowController.xib */,
7D17C4F01D658DB90066232A /* Configure.h */,
7D17C4EC1D658DB90066232A /* Configure.m */,
7D17C4F11D658DB90066232A /* ConfigureWindowController.h */,
7D17C4F21D658DB90066232A /* ErrorWindowController.h */,
7D17C4F31D658DB90066232A /* ErrorWindowController.m */,
7D17C4F81D658DB90066232A /* ErrorWindowController.xib */,
@ -204,6 +207,7 @@
7D6245831D87C3D700870565 /* Images.xcassets in Resources */,
7D17C4E91D658D9F0066232A /* overSight.png in Resources */,
7D17C5011D658DB90066232A /* ErrorWindowController.xib in Resources */,
7DEABC481E907DF10024C6AB /* objectiveSee.png in Resources */,
7D17C4E81D658D9F0066232A /* icon.png in Resources */,
7D17C4FC1D658DB90066232A /* ConfigureWindowController.xib in Resources */,
7D17C4E51D658D9F0066232A /* AboutWindow.xib in Resources */,

View File

@ -27,14 +27,18 @@
if(ACTION_INSTALL_FLAG == parameter)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"installing...");
#endif
//if already installed though
// ->uninstall everything first
if(YES == [self isInstalled])
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"already installed, so stopping/uninstalling...");
#endif
//stop
// ->kill login item/XPC service
@ -49,7 +53,9 @@
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"uninstalled");
#endif
}
//install
@ -63,7 +69,9 @@
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"installed, now will start");
#endif
//start login item
if(YES != [self start])
@ -80,14 +88,18 @@
else if(ACTION_UNINSTALL_FLAG == parameter)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"stopping login item");
#endif
//stop
// ->kill login item/XPC service
[self stop];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"uninstalling...");
#endif
//uninstall
if(YES != [self uninstall:UNINSTALL_FULL])
@ -97,7 +109,9 @@
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"uninstalled!");
#endif
}
//no errors
@ -161,14 +175,18 @@ bail:
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"copied %@ -> %@", appPathSrc, appPathDest]);
#endif
//remove xattrs
// ->otherwise app translocation causes issues
execTask(XATTR, @[@"-cr", appPathDest]);
//dbg msg
logMsg(LOG_DEBUG, @"removed xattrz");
#ifdef DEBUG
logMsg(LOG_DEBUG, @"removed xattr");
#endif
//init path to login item
loginItem = [appPathDest stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"];
@ -189,7 +207,9 @@ bail:
execTask(SUDO, @[@"-u", user, loginItem, [NSString stringWithUTF8String:CMD_INSTALL]]);
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"persisted %@", loginItem]);
#endif
//init path to XPC service
xpcServicePath = [appPathDest stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/XPCServices/OverSightXPC.xpc"];
@ -306,7 +326,9 @@ bail:
installedAppPath = [APPS_FOLDER stringByAppendingPathComponent:APP_NAME];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"uninstalling login item");
#endif
//get user
user = loggedinUser();
@ -325,18 +347,24 @@ bail:
else
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"telling login item %@, to uninstall itself", loginItem]);
#endif
//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, [NSString stringWithUTF8String:CMD_UNINSTALL]]);
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"unpersisted %@", loginItem]);
#endif
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"deleting app");
#endif
//delete folder
if(YES != [[NSFileManager defaultManager] removeItemAtPath:installedAppPath error:&error])
@ -355,7 +383,9 @@ bail:
if(UNINSTALL_FULL == type)
{
//dbg msg
logMsg(LOG_DEBUG, @"full, so also deleting app support directory");
#ifdef DEBUG
logMsg(LOG_DEBUG, @"full uninstall, so also deleting app support directory");
#endif
//delete app support folder
if(YES == [[NSFileManager defaultManager] fileExistsAtPath:[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath]])

View File

@ -20,6 +20,11 @@
@property (weak) IBOutlet NSButton *installButton;
@property (weak) IBOutlet NSButton *uninstallButton;
@property (weak) IBOutlet NSButton *moreInfoButton;
@property (weak) IBOutlet NSButton *supportButton;
@property (strong) IBOutlet NSView *supportView;
/* METHODS */

View File

@ -12,6 +12,8 @@
#import "Utilities.h"
#import "ConfigureWindowController.h"
#import <Quartz/Quartz.h>
@implementation ConfigureWindowController
@synthesize statusMsg;
@ -110,20 +112,87 @@
NSUInteger action = 0;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"handling action click: %@", buttonTitle]);
#endif
//close?
//close/no?
// ->just exit
if(YES == [buttonTitle isEqualToString:ACTION_CLOSE])
if( (YES == [buttonTitle isEqualToString:ACTION_CLOSE]) ||
(YES == [buttonTitle isEqualToString:ACTION_NO]) )
{
//close
[self.window close];
//bail
goto bail;
}
//next >>?
// ->show 'support' us view
if(YES == [buttonTitle isEqualToString:ACTION_NEXT])
{
//frame
NSRect frame = {0};
//unset window title
self.window.title = @"";
//get main window's frame
frame = self.window.contentView.frame;
//set origin to 0/0
frame.origin = CGPointZero;
//increase y offset
frame.origin.y += 5;
//reduce height
frame.size.height -= 5;
//pre-req
[self.supportView setWantsLayer:YES];
//update overlay to take up entire window
self.supportView.frame = frame;
//set overlay's view color to white
self.supportView.layer.backgroundColor = [NSColor whiteColor].CGColor;
//nap for UI purposes
[NSThread sleepForTimeInterval:0.10f];
//add to main window
[self.window.contentView addSubview:self.supportView];
//show
self.supportView.hidden = NO;
//make 'yes!' button active
[self.window makeFirstResponder:self.supportButton];
//bail
goto bail;
}
//'yes' for support
// ->load supprt in URL
if(YES == [buttonTitle isEqualToString:ACTION_YES])
{
//open URL
// ->invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:PATREON_URL]];
//close
[self.window close];
//bail
goto bail;
}
//install/uninstall logic handlers
else
{
//hide 'get more info' button
self.moreInfoButton.hidden = YES;
@ -159,10 +228,12 @@
^{
//install/uninstall
[self lifeCycleEvent:action];
});
}
//bail
bail:
return;
}
@ -193,9 +264,6 @@
//configure object
Configure* configureObj = nil;
//dbg msg
//logMsg(LOG_DEBUG, [NSString stringWithFormat:@"handling life cycle event, %lu", (unsigned long)event]);
//alloc control object
configureObj = [[Configure alloc] init];
@ -351,11 +419,11 @@
[self.statusMsg setStringValue:resultMsg];
//update button
// ->after install change butter to 'close'
// ->after install change butter to 'next'
if(ACTION_INSTALL_FLAG == event)
{
//set button title to 'close'
self.installButton.title = ACTION_CLOSE;
self.installButton.title = ACTION_NEXT;
//enable
self.installButton.enabled = YES;

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16A323" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
<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="ConfigureWindowController">
@ -10,6 +11,8 @@
<outlet property="installButton" destination="553-2y-kvm" id="nxf-wO-EI3"/>
<outlet property="moreInfoButton" destination="QWu-qZ-Za2" id="8kC-Ga-5oJ"/>
<outlet property="statusMsg" destination="SpB-Xc-WlB" id="jlm-ke-XfM"/>
<outlet property="supportButton" destination="P9c-Mi-M5f" id="Lxc-8L-j9l"/>
<outlet property="supportView" destination="vCr-3l-dVw" id="gO5-OQ-h7d"/>
<outlet property="uninstallButton" destination="XrB-7g-xya" id="0Hc-Xn-STu"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
@ -20,16 +23,18 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="460" height="176"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<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="460" height="176"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" maxValue="100" bezeled="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="b1w-5W-ayX">
<rect key="frame" x="144" y="31" width="32" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</progressIndicator>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XrB-7g-xya">
<rect key="frame" x="138" y="76" width="115" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Uninstall" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="G7H-Q1-04P">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Menlo-Regular"/>
@ -40,10 +45,12 @@
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fgM-lv-GeJ">
<rect key="frame" x="13" y="43" width="114" height="123"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon" id="bCU-0f-ff8"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" preferredMaxLayoutWidth="293" translatesAutoresizingMaskIntoConstraints="NO" id="SpB-Xc-WlB">
<rect key="frame" x="144" y="11" width="293" height="47"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" truncatesLastVisibleLine="YES" enabled="NO" sendsActionOnEndEditing="YES" alignment="center" id="Rib-WU-Syl">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -52,6 +59,7 @@
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="553-2y-kvm">
<rect key="frame" x="331" y="76" width="115" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Install" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="1hy-8F-qxt">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Menlo-Regular"/>
@ -62,10 +70,12 @@
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="K52-9g-sjn">
<rect key="frame" x="138" y="109" width="243" height="67"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="overSight" id="WRg-I2-vkK"/>
</imageView>
<button hidden="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QWu-qZ-Za2">
<rect key="frame" x="414" y="33" width="25" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="v1Z-aX-Wlm">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -82,9 +92,53 @@
</connections>
<point key="canvasLocation" x="185" y="-256"/>
</window>
<customView id="vCr-3l-dVw" userLabel="Support">
<rect key="frame" x="0.0" y="0.0" width="460" height="176"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PrA-cn-uEb">
<rect key="frame" x="0.0" y="73" width="460" height="95"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="objectiveSee" id="R16-ui-bGJ"/>
</imageView>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nMV-BX-h3q" userLabel="No">
<rect key="frame" x="216" y="13" width="115" 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="mxu-rh-2Cj">
<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="AbE-9d-JcC"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="P9c-Mi-M5f" userLabel="Yes">
<rect key="frame" x="331" y="13" width="115" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="yes!" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="TKT-GO-QZ5">
<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="M6h-5G-Cp2"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tlG-7V-dhI">
<rect key="frame" x="144" y="60" width="306" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="support our free tools?" id="ojw-4b-aZW">
<font key="font" size="17" name="Menlo-Bold"/>
<color key="textColor" white="0.25" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<point key="canvasLocation" x="785" y="-256"/>
</customView>
</objects>
<resources>
<image name="icon" width="512" height="512"/>
<image name="objectiveSee" width="943" height="236"/>
<image name="overSight" width="1396" height="286"/>
</resources>
</document>

View File

@ -7,6 +7,7 @@
//
#import "Consts.h"
#import "Logging.h"
#import "ErrorWindowController.h"
@interface ErrorWindowController ()
@ -138,7 +139,9 @@
if(YES == self.shouldExit)
{
//dbg msg
//logMsg(LOG_DEBUG, @"exiting application");
#ifdef DEBUG
logMsg(LOG_DEBUG, @"exiting application");
#endif
//exit
[NSApp terminate:self];

View File

@ -82,7 +82,9 @@ int main(int argc, const char * argv[])
if(0 != geteuid())
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"non-root installer instance");
#endif
//spawn as root
if(YES != spawnAsRoot(argv[0]))
@ -103,7 +105,9 @@ int main(int argc, const char * argv[])
else
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"root installer instance");
#endif
//app away
retVar = NSApplicationMain(argc, (const char **)argv);

View File

@ -43,12 +43,6 @@
//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;
@ -64,6 +58,9 @@
//whitelisted procs
@property(nonatomic, retain)NSMutableArray* whiteList;
//activation alerts that were displayed
@property(nonatomic, retain)NSMutableDictionary* activationAlerts;
/* METHODS */

View File

@ -21,14 +21,11 @@
@synthesize lastEvent;
@synthesize whiteList;
@synthesize audioActive;
@synthesize activationAlerts;
@synthesize lastNotification;
@synthesize videoMonitorThread;
@synthesize showAudioDeactivation;
@synthesize showVideoDeactivation;
@synthesize rememberWindowController;
//TODO: fix hang!!
//init
-(id)init
{
@ -36,6 +33,9 @@
self = [super init];
if(nil != self)
{
//alloc
activationAlerts = [NSMutableDictionary dictionary];
//load whitelist
[self loadWhitelist];
}
@ -53,7 +53,9 @@
path = [[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] stringByAppendingPathComponent:FILE_WHITELIST];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"loading whitelist %@", path]);
#endif
//since file is created by priv'd XPC, it shouldn't be writeable
// ...unless somebody maliciously creates it, so we check if that here
@ -70,7 +72,9 @@
self.whiteList = [NSMutableArray arrayWithContentsOfFile:path];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"whitelist: %@", self.whiteList]);
#endif
//bail
bail:
@ -89,7 +93,9 @@ bail:
cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"cameras: %@", cameras]);
#endif
//look for camera that belongs to apple
for(AVCaptureDevice* currentCamera in cameras)
@ -113,7 +119,9 @@ bail:
self.camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"didn't find apple camera, grabbed default: %@", self.camera]);
#endif
}
return;
@ -130,7 +138,9 @@ bail:
mics = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"mics: %@", mics]);
#endif
//look for mic that belongs to apple
for(AVCaptureDevice* currentMic in mics)
@ -156,7 +166,9 @@ bail:
self.mic = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"didn't find apple 'imic', grabbed default: %@", self.mic]);
#endif
}
return;
@ -199,7 +211,9 @@ bail:
[xpcConnection resume];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"telling XPC service to begin base-lining mach messages");
#endif
//init wait semaphore
waitSema = dispatch_semaphore_create(0);
@ -253,14 +267,15 @@ bail:
if(YES == self.videoActive)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"video already active, so will start polling for new video procs");
#endif
//tell XPC video is active
[[xpcConnection remoteObjectProxy] updateVideoStatus:self.videoActive reply:^{
//signal sema
dispatch_semaphore_signal(waitSema);
}];
//wait until XPC is done
@ -281,7 +296,9 @@ bail:
if(YES != [self watchVideo:connectionID])
{
//err msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"failed to watch for video events");
#endif
//set err
wasErrors = YES;
@ -322,11 +339,13 @@ bail:
//if audio is already active
// ->tell XPC that it's active
// TODO: monitor for hijacking?
// TODO: monitor for piggybacking?
if(YES == self.audioActive)
{
//dbg msg
logMsg(LOG_DEBUG, @"audio already active");//so will start polling for new video procs");
#ifdef DEBUG
logMsg(LOG_DEBUG, @"audio already active");
#endif
//tell XPC audio is active
[[xpcConnection remoteObjectProxy] updateAudioStatus:self.audioActive reply:^{
@ -348,7 +367,9 @@ bail:
if(YES != [self watchAudio:connectionID])
{
//err msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"failed to watch for audio events");
#endif
//set err
wasErrors = YES;
@ -495,7 +516,9 @@ bail:
event[EVENT_DEVICE_STATUS] = [NSNumber numberWithInt:self.videoActive];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"got video change notification; is running? %x", self.videoActive]);
#endif
//alloc XPC connection
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
@ -518,7 +541,6 @@ bail:
}];
//TODO: maybe add timeout here?
//wait until XPC is done
// ->XPC reply block will signal semaphore
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
@ -528,7 +550,9 @@ bail:
if(YES == self.videoActive)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"video is active, so querying XPC to get video process(s)");
#endif
//set allowed classes
[xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil]
@ -544,7 +568,9 @@ bail:
xpcConnection = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"video procs from XPC: %@", videoProcesses]);
#endif
//generate notification for each process
for(NSNumber* processID in videoProcesses)
@ -599,7 +625,9 @@ bail:
if(YES != videoMonitorThread.isExecuting)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"(re)Starting polling/monitor thread");
#endif
//alloc
videoMonitorThread = [[NSThread alloc] initWithTarget:self selector:@selector(monitor4Procs) object:nil];
@ -611,7 +639,9 @@ bail:
else
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"polling/monitor thread still running");
#endif
}
}
@ -651,7 +681,6 @@ bail:
{
//invoke helper function
[self handleVideoNotification:deviceID addresses:addresses];
};
//register (add) property block listener
@ -751,7 +780,9 @@ bail:
event[EVENT_DEVICE_STATUS] = [NSNumber numberWithInt:self.audioActive];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"got audio change notification; is running? %x", self.audioActive]);
#endif
//alloc XPC connection
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
@ -783,7 +814,9 @@ bail:
if(YES == self.audioActive)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"audio is active, so querying XPC to get audio process(s)");
#endif
//set allowed classes
[xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil]
@ -799,7 +832,9 @@ bail:
xpcConnection = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"audio procs from XPC: %@", audioProcesses]);
#endif
//generate notification for each process
for(NSNumber* processID in audioProcesses)
@ -933,6 +968,9 @@ bail:
//preferences
NSDictionary* preferences = nil;
//flag
BOOL matchingActivationAlert = NO;
//alloc notificaiton
notification = [[NSUserNotification alloc] init];
@ -943,7 +981,9 @@ bail:
sysLogMsg = [NSMutableString string];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"generating notification for %@", event]);
#endif
//get process name
processName = getProcessName([event[EVENT_PROCESS_ID] intValue]);
@ -982,7 +1022,9 @@ bail:
(YES == [self.whiteList containsObject:processPath]) )
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"activation alert for process %@ is whitelisted, so ignoring", processPath]);
#endif
//bail
goto bail;
@ -991,30 +1033,33 @@ bail:
// ->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) )
//any active alerts?
// ->note: we make to check if the type matches
for(NSUUID* key in self.activationAlerts.allKeys)
{
//same type?
if(event[EVENT_DEVICE] == self.activationAlerts[key][EVENT_DEVICE])
{
//got match
matchingActivationAlert = YES;
//no need to check more
break;
}
}
//no match?
// ->bail, as means process was whitelisted so no activation was shown
if(YES != matchingActivationAlert)
{
//dbg msg
logMsg(LOG_DEBUG, @"deactivation audio alert doesn't have an activation alert (whitelisted?), so ignoring");
#ifdef DEBUG
logMsg(LOG_DEBUG, @"deactivation alert doesn't have an activation alert (whitelisted?), so ignoring");
#endif
//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)
@ -1029,7 +1074,9 @@ bail:
(YES == [self.lastEvent[EVENT_DEVICE_STATUS] isEqual:event[EVENT_DEVICE_STATUS]]) )
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"alert for %@ would be same as previous (%@), so ignoring", event, self.lastEvent]);
#endif
//update
self.lastEvent = event;
@ -1052,7 +1099,9 @@ bail:
(YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) )
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"user has decided to ingore 'inactive' events, so ingoring A/V going to disable state");
#endif
//bail
goto bail;
@ -1077,21 +1126,11 @@ bail:
// ->name of device
details = ((AVCaptureDevice*)event[EVENT_DEVICE]).localizedName;
//customize buttons
// ->inactive events, or when consumer proc couldn't be ID'd, just say 'ok'
if( (YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) ||
(0 == [event[EVENT_PROCESS_ID] intValue]) )
{
//set other button title
notification.otherButtonTitle = @"ok";
//ALERT 1:
// ->activate alert with lots of info
if( (YES == [DEVICE_ACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) &&
(0 != [event[EVENT_PROCESS_ID] intValue]) )
//remove action button
notification.hasActionButton = NO;
}
//customize buttons
// ->for activated audio/video; allow/block
else
{
//set other button title
notification.otherButtonTitle = @"allow";
@ -1099,15 +1138,47 @@ bail:
//set action title
notification.actionButtonTitle = @"block";
//remove action button
notification.hasActionButton = YES;
//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_PATH:processPath, 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, EVENT_ALERT_TYPE:ALERT_ACTIVATE};
//set details
// ->name of process using it / icon too?
[notification setInformativeText:[NSString stringWithFormat:@"process: %@ (%@)", processName, event[EVENT_PROCESS_ID]]];
}
//ALERT 2:
// ->activate alert, with no process info
else if( (YES == [DEVICE_ACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) &&
(0 == [event[EVENT_PROCESS_ID] intValue]) )
{
//set other button title
notification.otherButtonTitle = @"ok";
//remove action button
notification.hasActionButton = NO;
//set type
notification.userInfo = @{EVENT_DEVICE:deviceType, EVENT_ALERT_TYPE:ALERT_ACTIVATE};
}
//ALERT 3:
// ->inactive alert, with no process info
else
{
//set other button title
notification.otherButtonTitle = @"ok";
//remove action button
notification.hasActionButton = NO;
//set type
notification.userInfo = @{EVENT_DEVICE:deviceType, EVENT_ALERT_TYPE:ALERT_INACTIVE};
}
//log event?
if(YES == [preferences[PREF_LOG_ACTIVITY] boolValue])
{
@ -1150,56 +1221,50 @@ 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
//save displayed activation alerts
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;
}
//save
self.activationAlerts[notification.identifier] = event;
}
//for 'went inactive' notification
// ->automatically close after some time
//for 'went inactive' notification automatically close
// ->unless there still is an active alert on the screen
if(YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]])
{
//dbg msg
logMsg(LOG_DEBUG, @"event is 'went inactive', so will automatically close");
//any active alerts still visible?
// ->note: we make to check if the type matches
for(NSUUID* key in self.activationAlerts.allKeys)
{
//same type?
if(event[EVENT_DEVICE] == self.activationAlerts[key][EVENT_DEVICE])
{
//got match
matchingActivationAlert = YES;
//close after 2 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
//no need to check more
break;
}
}
//go match
// ->close if not still visible
if(YES != matchingActivationAlert)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"automatically closing deactivation alert");
#endif
//delay, then close
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
//close
[NSUserNotificationCenter.defaultUserNotificationCenter removeDeliveredNotification:notification];
});
}
}
//bail
bail:
@ -1223,6 +1288,9 @@ bail:
//process id
NSNumber* processID = nil;
//device type
NSNumber* deviceType = nil;
//preferences
NSDictionary* preferences = nil;
@ -1236,7 +1304,9 @@ bail:
sysLogMsg = [NSMutableString string];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"user responded to notification: %@", notification]);
#endif
//ignore if this notification was already seen
// ->need this logic, since have to determine if 'allow' was invoke indirectly
@ -1253,6 +1323,38 @@ bail:
}
}
//when it's a deactivation alert
// ->remove all activation alerts that match the same type
if(ALERT_INACTIVE.intValue == [notification.userInfo[EVENT_ALERT_TYPE] intValue])
{
//remove any active alerts
// ->note: we make to check if the type matches
for(NSUUID* key in self.activationAlerts.allKeys)
{
//check stored activation alert type
// ->audio
if(YES == [self.activationAlerts[key][EVENT_DEVICE] isKindOfClass:NSClassFromString(@"AVCaptureHALDevice")])
{
//set device
deviceType = SOURCE_AUDIO;
}
//check stored activation alert type
// ->video
else
{
//set device
deviceType = SOURCE_VIDEO;
}
//same type?
if( [notification.userInfo[EVENT_DEVICE] intValue] == deviceType.intValue)
{
//remove
[self.activationAlerts removeObjectForKey:key];
}
}
}
//update
self.lastNotification = notification.identifier;
@ -1261,7 +1363,9 @@ bail:
if(YES != notification.hasActionButton)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"popup without an action, no need to do anything");
#endif
//bail
goto bail;
@ -1291,19 +1395,22 @@ 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]) )
// ->show a popup w/ option to remember ('whitelist') the application
// don't do this for 'block' since that kills the app, so obv, that'd be bad to *always* do!
if(NSUserNotificationActivationTypeAdditionalActionClicked == [notification.userInfo[@"activationType"] integerValue])
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"user clicked 'allow'");
#endif
//can't remember process that we didn't find the path for
if(YES == [notification.userInfo[EVENT_PROCESS_PATH] isEqualToString:PROCESS_UNKNOWN])
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"don't have a process path, so not displaying whitelisting popup");
#endif
//bail
goto bail;
@ -1338,7 +1445,9 @@ bail:
else if(NSUserNotificationActivationTypeActionButtonClicked == notification.activationType)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"user clicked 'block'");
#endif
//extract process id
processID = notification.userInfo[EVENT_PROCESS_ID];
@ -1361,7 +1470,9 @@ bail:
[xpcConnection resume];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"invoking XPC method to kill: %@", processID]);
#endif
//invoke XPC method 'killProcess' to terminate
[[xpcConnection remoteObjectProxy] killProcess:processID reply:^(BOOL wasKilled)
@ -1398,9 +1509,6 @@ bail:
//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),
@ -1439,6 +1547,10 @@ bail:
// ->invoke 'didActivateNotification' to process if it was an 'allow/block' alert
dispatch_async(dispatch_get_main_queue(),
^{
//add extra info for activation alerts
// ->will allow callback to identify we delivered via this mechanism
if(YES == notification.hasActionButton)
{
//grab user info dictionary
userInfo = [notification.userInfo mutableCopy];
@ -1447,12 +1559,12 @@ bail:
//update
notification.userInfo = userInfo;
}
//deliver
[self userNotificationCenter:center didActivateNotification:notification];
});
});
}
return;
}
@ -1468,7 +1580,9 @@ bail:
dispatch_semaphore_t waitSema = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"[MONITOR THREAD] video is active, so polling for new procs");
#endif
//alloc XPC connection
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
@ -1490,14 +1604,18 @@ bail:
waitSema = dispatch_semaphore_create(0);
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"[MONITOR THREAD] (re)Asking XPC for (new) video procs");
#endif
//invoke XPC service to get (new) video procs
// ->will generate user notifications for any new processes
[[xpcConnection remoteObjectProxy] getVideoProcs:^(NSMutableArray* videoProcesses)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"[MONITOR THREAD] found %lu new video procs: %@", (unsigned long)videoProcesses.count, videoProcesses]);
#endif
//generate a notification for each process
// ->double check video is still active though...
@ -1538,7 +1656,9 @@ bail:
xpcConnection = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"[MONITOR THREAD] exiting polling/monitor thread since camera is off");
#endif
return;
}

View File

@ -1,6 +1,6 @@
//
// AppDelegate.m
// Test Application Helper
// Login Item, (app helper)
//
// Created by Patrick Wardle on 9/10/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
@ -11,8 +11,6 @@
#import "Utilities.h"
#import "AppDelegate.h"
//TODO: "I have a dual monitor setup and just noticed that the alert came up on the other monitor, not the one that Skype was launched on."
@interface AppDelegate ()
@ -33,7 +31,9 @@
NSDictionary* preferences = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"starting login item app logic");
#endif
//drop group privs
setgid(getgid());
@ -52,14 +52,18 @@
[self loadStatusBar];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"initialized/loaded status bar (icon/menu)");
#endif
}
#ifdef DEBUG
//dbg msg
else
{
//dbg msg
logMsg(LOG_DEBUG, @"running in headless mode");
}
#endif
//check for updates
// ->but only when user has not disabled that feature
@ -70,11 +74,12 @@
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"checking for update");
#endif
//check
[self isThereAnUpdate];
});
}
@ -82,14 +87,18 @@
avMonitor = [[AVMonitor alloc] init];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"alloc/init'd AV monitor");
#endif
//start monitoring
// ->sets up audio/video callbacks
[avMonitor monitor];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"AV monitor off and running");
#endif
//bail
bail:
@ -127,7 +136,9 @@ bail:
dispatch_sync(dispatch_get_main_queue(), ^{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"a new version (%@) is available", versionString]);
#endif
//alloc/init about window
infoWindowController = [[InfoWindowController alloc] initWithWindowNibName:@"InfoWindow"];
@ -154,11 +165,13 @@ bail:
//no new version
// ->just (debug) log msg
#ifdef DEBUG
else
{
//dbg msg
logMsg(LOG_DEBUG, @"no updates available");
}
#endif
return;
}

View File

@ -109,7 +109,9 @@
__block NSXPCConnection* xpcConnection = nil;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"handling user response for 'allow' popup: %ld", (long)((NSButton*)sender).tag]);
#endif
//handle 'always allow' (whitelist) button
if(BUTTON_ALWAYS_ALLOW == ((NSButton*)sender).tag)
@ -124,13 +126,17 @@
[xpcConnection resume];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"sending XPC message to whitelist");
#endif
//invoke XPC method 'whitelistProcess' to add process to white list
[[xpcConnection remoteObjectProxy] whitelistProcess:self.processPath reply:^(BOOL wasWhitelisted)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"got XPC response: %d", wasWhitelisted]);
#endif
//reload whitelist on success
if(YES == wasWhitelisted)

View File

@ -221,7 +221,9 @@
-(void)quit:(id)sender
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"user clicked 'exit', so goodbye!");
#endif
//exit
[[NSApplication sharedApplication] terminate:nil];
@ -234,7 +236,9 @@
-(void)preferences:(id)sender
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"launching main app (from /Applications)");
#endif
//launch main app
[[NSWorkspace sharedWorkspace] launchApplication:[APPS_FOLDER stringByAppendingPathComponent:APP_NAME]];

View File

@ -19,7 +19,9 @@ int main(int argc, const char * argv[])
int iReturn = 0;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"starting login item (args: %@/user: %@)", [[NSProcessInfo processInfo] arguments], NSUserName()]);
#endif
//check for uninstall/install flags, and process to remove from whitelist
if(2 == argc)
@ -28,7 +30,9 @@ int main(int argc, const char * argv[])
if(0 == strcmp(argv[1], CMD_INSTALL))
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"running install logic");
#endif
//drop user privs
setuid(getuid());
@ -37,13 +41,17 @@ int main(int argc, const char * argv[])
toggleLoginItem([NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]], ACTION_INSTALL_FLAG);
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"installed login item");
#endif
//create default prefs
[@{PREF_LOG_ACTIVITY:@YES, PREF_START_AT_LOGIN:@YES, PREF_RUN_HEADLESS:@NO, PREF_CHECK_4_UPDATES:@YES} writeToFile:[APP_PREFERENCES stringByExpandingTildeInPath] atomically:NO];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"created preferences at: %@", [APP_PREFERENCES stringByExpandingTildeInPath]]);
#endif
//bail
goto bail;
@ -52,7 +60,9 @@ int main(int argc, const char * argv[])
else if(0 == strcmp(argv[1], CMD_UNINSTALL))
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"running uninstall logic");
#endif
//drop user privs
setuid(getuid());
@ -61,13 +71,17 @@ int main(int argc, const char * argv[])
toggleLoginItem([NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]], ACTION_UNINSTALL_FLAG);
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"removed login item");
#endif
//delete prefs
[[NSFileManager defaultManager] removeItemAtPath:[APP_PREFERENCES stringByExpandingTildeInPath] error:nil];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"removed preferences from: %@", [APP_PREFERENCES stringByExpandingTildeInPath]]);
#endif
//bail
goto bail;
@ -77,7 +91,9 @@ int main(int argc, const char * argv[])
else
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"running 'un-whitelist me' logic");
#endif
//remove from whitelist file
unWhiteList([NSString stringWithUTF8String:argv[1]]);
@ -112,15 +128,19 @@ void unWhiteList(NSString* process)
[xpcConnection resume];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"sending XPC message to remove %@ from whitelist file", process]);
#endif
//invoke XPC method 'whitelistProcess' to add process to white list
[[xpcConnection remoteObjectProxy] unWhitelistProcess:process reply:^(BOOL wasRemoved)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"got XPC response: %d", wasRemoved]);
#endif
//err msg ono failure
//err msg on failure
if(YES != wasRemoved)
{
//err msg

View File

@ -49,7 +49,9 @@
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"OverSight Preferences App Launched");
#endif
//register for hotkey presses
// ->for now, just cmd+q to quit app
@ -60,7 +62,9 @@
if(YES != [[NSFileManager defaultManager] fileExistsAtPath:[APP_PREFERENCES stringByExpandingTildeInPath]])
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"preference file not found; manually creating");
#endif
//write em out
// ->note; set 'start at login' to false, since no prefs here, mean installer wasn't run (user can later toggle)
@ -314,14 +318,18 @@ bail:
versionString = [NSMutableString string];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"checking for new version");
#endif
//check if available version is newer
// ->show update popup/window
if(YES == isNewVersion(versionString))
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"a new version (%@) is available", versionString]);
#endif
//hide version message
self.versionLabel.hidden = YES;
@ -358,7 +366,9 @@ bail:
else
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"no updates available");
#endif
//stop/hide spinner
[self.spinner stopAnimation:self];
@ -404,7 +414,9 @@ bail:
(YES != shouldRestart) )
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"login item already running and 'shouldRestart' not set, so no need to start it!");
#endif
//bail
goto bail;
@ -425,7 +437,9 @@ bail:
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"starting login item");
#endif
//add overlay
[self addOverlay:shouldRestart];
@ -478,11 +492,17 @@ bail:
[self.overlay setWantsLayer:YES];
//get main window's frame
frame = self.window.frame;
frame = self.window.contentView.frame;
//set origin to 0/0
frame.origin = CGPointZero;
//tweak since window is rounded
// ->and adding this view doesn't get rounded?
frame.origin.y += 1;
frame.origin.x += 1;
frame.size.width -= 2;
//update overlay to take up entire window
self.overlay.frame = frame;

View File

@ -16,7 +16,6 @@
#import <sys/sysctl.h>
//ignored mach sender procs
// TODO: maybe just ignore apple signed daemons/bg procs!?
static NSArray* ignoredProcs = nil;
@implementation Enumerator
@ -93,7 +92,9 @@ static NSArray* ignoredProcs = nil;
if(YES != self.videoActive)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"baselining mach senders for video...");
#endif
//find camera assistant
// ->first look for 'VDCAssistant'
@ -118,29 +119,37 @@ static NSArray* ignoredProcs = nil;
self.machSendersVideo = [self enumMachSenders:cameraAssistant];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSendersVideo.count, self.machSendersVideo]);
#endif
}
//only baseline if video isn't active
//only baseline if audio isn't active
if(YES != self.audioActive)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"baselining mach senders for audio...");
#endif
//enumerate procs that have send mach messages
self.machSendersAudio = [self enumMachSenders:findProcess(CORE_AUDIO)];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSendersAudio.count, self.machSendersVideo]);
//dbg msg
logMsg(LOG_DEBUG, @"baselining i/o registry entries for audio...");
#endif
//enumerate procs that have i/o registry entries
self.userClients = [self enumDomainUserClients];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined i/or registry senders: %@", (unsigned long)self.userClients.count, self.userClients]);
#endif
}
}
@ -198,7 +207,9 @@ static NSArray* ignoredProcs = nil;
currentSenders = [self enumMachSenders:cameraAssistant];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu current mach senders: %@", (unsigned long)currentSenders.count, currentSenders]);
#endif
//remove any known/existing senders
for(NSNumber* processID in currentSenders.allKeys)
@ -220,7 +231,9 @@ static NSArray* ignoredProcs = nil;
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu candidate video procs: %@", (unsigned long)candidateVideoProcs.count, candidateVideoProcs]);
#endif
//update
self.machSendersVideo = currentSenders;
@ -294,7 +307,9 @@ bail:
currentSenders = [self enumMachSenders:coreAudio];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu current mach senders: %@", (unsigned long)currentSenders.count, currentSenders]);
#endif
//add new senders or those w/ new mach msgs
for(NSNumber* processID in currentSenders.allKeys)
@ -319,7 +334,9 @@ bail:
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"new mach senders: %@", newSenders]);
#endif
//update iVar
self.machSendersAudio = currentSenders;
@ -328,7 +345,9 @@ bail:
currentUserClients = [self enumDomainUserClients];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu current i/o registry user clients: %@", (unsigned long)currentUserClients.count, currentUserClients]);
#endif
//add new user clients
for(NSNumber* processID in currentUserClients.allKeys)
@ -353,29 +372,13 @@ bail:
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"new user clients: %@", newUserClients]);
#endif
//update iVar
self.userClients = currentUserClients;
/*
//parse/check
// ->starts at end to find most recent IOUserClientCreator
for(NSNumber* domainUserClient in [domainUserClients reverseObjectEnumerator])
{
//no match?
// ->remove from candidate
if(YES != [candidateAudioProcs containsObject:domainUserClient])
{
//remove
}
}
*/
//init set for intersection
intersection = [NSMutableSet setWithArray:newSenders];
@ -395,15 +398,25 @@ bail:
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu candidate audio procs: %@", (unsigned long)candidateAudioProcs.count, candidateAudioProcs]);
#endif
//TODO: sample?
//invoke 'sample' to confirm that candidates are using CMIO/video inputs
// ->note, will skip FaceTime.app on macOS Sierra, as it doesn't do CMIO stuff directly
//audioProcs = [self sampleCandidates:candidateAudioProcs];
//only once candidate?
// ->all set, so assign, then bail here
if(candidateAudioProcs.count <= 1)
{
//assign
audioProcs = candidateAudioProcs;
//bail
goto bail;
}
// ->invoke 'sample' to determine which candidate is using CMIO/video inputs
// note: will skip FaceTime.app on macOS Sierra, as it doesn't do CMIO stuff directly
audioProcs = [self sampleCandidates:candidateAudioProcs];
}//sync
//bail
@ -615,11 +628,12 @@ bail:
}
//invoke 'sample' to confirm candidates are using CMIO/video inputs
//invoke 'sample' to confirm candidates are using CMIO/video/av inputs
// note: path audio/vide invoke 'CMIOGraph::DoWork'
-(NSMutableArray*)sampleCandidates:(NSArray*)currentSenders
{
//current procs
NSMutableArray* videoProcs = nil;
//av procs
NSMutableArray* avProcs = nil;
//results from 'sample' cmd
NSString* results = nil;
@ -628,14 +642,16 @@ bail:
NSString* processPath = nil;
//alloc
videoProcs = [NSMutableArray array];
avProcs = [NSMutableArray array];
//invoke 'sample' on each
// ->skips FaceTime.app though on macOS Sierra
for(NSNumber* processID in currentSenders)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"processing %d for sampling", processID.intValue]);
#endif
//get process path
// ->skip ones that fail
@ -653,10 +669,12 @@ bail:
([getOSVersion() [@"minorVersion"] intValue] >= 12) )
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"not sampling as candidate app is FaceTime on macOS Sierra");
#endif
//add
[videoProcs addObject:processID];
[avProcs addObject:processID];
//next
continue;
@ -664,7 +682,9 @@ bail:
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"sampling %d", processID.intValue]);
#endif
//exec 'sample' to get threads/dylibs
// ->uses 1.0 seconds for sampling time
@ -681,18 +701,23 @@ bail:
[self deleteSampleFile:processPath];
//for now, just check for 'CMIOGraph::DoWork'
// ->TODO: could look for dylibs, other calls, etc
// ->note: both audio/video invoke this, so this method works for both!
if(YES != [results containsString:@"CMIOGraph::DoWork"])
{
//skip
continue;
}
//looks like a video proc!
[videoProcs addObject:processID];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"processing %d for has 'CMIOGraph::DoWork', as adding to list of candidates", processID.intValue]);
#endif
//looks like a av proc!
[avProcs addObject:processID];
}
return videoProcs;
return avProcs;
}
//'sample' binary creates a file
@ -734,7 +759,9 @@ bail:
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"deleting sample file: %@", file]);
#endif
//delete
if(YES != [[NSFileManager defaultManager] removeItemAtPath:[@"/tmp" stringByAppendingPathComponent:file] error:&error])

View File

@ -113,7 +113,9 @@
if(YES != [[NSFileManager defaultManager] fileExistsAtPath:[APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath]])
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"XPC: creating app support directory: %@", [APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath]]);
#endif
//create
if(YES != [[NSFileManager defaultManager] createDirectoryAtPath: [APP_SUPPORT_DIRECTORY stringByExpandingTildeInPath] withIntermediateDirectories:YES attributes:nil error:&error])

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
<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="AboutWindowController">
@ -15,35 +16,50 @@
<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="1057"/>
<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="12" width="110" height="106"/>
<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="141" y="63" width="180" height="50"/>
<rect key="frame" x="138" y="63" width="180" height="50"/>
<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="141" y="51" width="182" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="version:" 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" translatesAutoresizingMaskIntoConstraints="NO" id="HZZ-Es-mpy">
<button verticalHuggingPriority="750" fixedFrame="YES" tag="101" translatesAutoresizingMaskIntoConstraints="NO" id="HZZ-Es-mpy">
<rect key="frame" x="280" y="11" width="128" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="more info" 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="moreInfo:" target="-2" id="YYW-1b-uJY"/>
<action selector="buttonHandler:" target="-2" id="Cgx-f4-Thr"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" tag="100" translatesAutoresizingMaskIntoConstraints="NO" id="xnd-Gw-0o8">
<rect key="frame" x="137" y="11" width="128" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="support us!" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6g3-Pg-x3P">
<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="gP0-s7-o6h"/>
</connections>
</button>
</subviews>

View File

@ -20,8 +20,5 @@
/* METHODS */
//invoked when user clicks 'more info' button
// ->open OverSights webpage
- (IBAction)moreInfo:(id)sender;
@end

View File

@ -48,13 +48,25 @@
return;
}
//automatically invoked when user clicks 'more info'
// ->load products webpage view their default browser
-(IBAction)moreInfo:(id)sender
//automatically invoked when user clicks any of the buttons
// ->load patreon or products webpage in user's default browser
-(IBAction)buttonHandler:(id)sender
{
//support us button
if(((NSButton*)sender).tag == BUTTON_SUPPORT_US)
{
//open URL
// ->invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:PATREON_URL]];
}
//more info button
else if(((NSButton*)sender).tag == BUTTON_MORE_INFO)
{
//open URL
// ->invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:PRODUCT_URL]];
}
return;
}

View File

@ -27,6 +27,9 @@
//product version url
#define PRODUCT_VERSION_URL @"https://objective-see.com/products.json"
//patreon url
#define PATREON_URL @"https://www.patreon.com/objective_see"
//OS version x
#define OS_MAJOR_VERSION_X 10
@ -52,7 +55,19 @@
//button title
// ->Close
#define ACTION_CLOSE @"Close"
#define ACTION_CLOSE @"close"
//button title
// ->next
#define ACTION_NEXT @"next »"
//button title
// ->no
#define ACTION_NO @"no"
//button title
// ->yes
#define ACTION_YES @"yes!"
//flag to uninstall
#define ACTION_UNINSTALL_FLAG 0
@ -128,12 +143,19 @@
#define EVENT_TIMESTAMP @"timeStamp"
#define EVENT_DEVICE_STATUS @"status"
#define EVENT_PROCESS_ID @"processID"
#define EVENT_ALERT_TYPE @"alertType"
#define EVENT_PROCESS_NAME @"processName"
#define EVENT_PROCESS_PATH @"processPath"
//unknown process
#define PROCESS_UNKNOWN @"<unknown>"
//went inactive
#define ALERT_INACTIVE @0x0
//went active
#define ALERT_ACTIVATE @0x1
//source audio
#define SOURCE_AUDIO @0x1
@ -149,5 +171,11 @@
//id (tag) for detailed text in rules table
#define TABLE_ROW_SUB_TEXT_TAG 101
//support us button tag
#define BUTTON_SUPPORT_US 100
//more info button tag
#define BUTTON_MORE_INFO 101
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -153,7 +153,9 @@ BOOL setFileOwner(NSString* path, NSNumber* groupID, NSNumber* ownerID, BOOL rec
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"set ownership for %@ (%@)", path, fileOwner]);
#endif
//do it recursively
if(YES == recursive)
@ -269,35 +271,28 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments)
//output pipe
NSPipe *outPipe = nil;
//read handle
NSFileHandle* readHandle = nil;
//output
NSMutableData *output = nil;
NSData *output = nil;
//init task
task = [NSTask new];
//init output pipe
//init pipe
outPipe = [NSPipe pipe];
//init read handle
readHandle = [outPipe fileHandleForReading];
//init output buffer
output = [NSMutableData data];
//set task's path
[task setLaunchPath:binaryPath];
task.launchPath = binaryPath;
//set task's args
[task setArguments:arguments];
task.arguments = arguments;
//set task's output
[task setStandardOutput:outPipe];
//set task's output to pipe
task.standardOutput = outPipe;
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"@exec'ing %@ (args: %@)", binaryPath, arguments]);
#endif
//wrap task launch
@try
@ -307,19 +302,33 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments)
}
@catch(NSException* exception)
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"task failed with %@", exception]);
//bail
goto bail;
}
//read in output
while(YES == [task isRunning])
{
//accumulate output
[output appendData:[readHandle readDataToEndOfFile]];
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"invoking 'readDataToEndOfFile' to get all data");
#endif
//grab any left over data
[output appendData:[readHandle readDataToEndOfFile]];
//read until file is closed
output = [outPipe.fileHandleForReading readDataToEndOfFile];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"now waiting for task to exit");
#endif
//wait till exit
[task waitUntilExit];
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"task to exited");
#endif
//bail
bail:
@ -845,7 +854,9 @@ BOOL toggleLoginItem(NSURL* loginItem, int toggleFlag)
if(ACTION_INSTALL_FLAG == toggleFlag)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"adding login item");
#endif
//add
LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsRef, kLSSharedFileListItemLast, NULL, NULL, (__bridge CFURLRef)(loginItem), NULL, NULL);
@ -854,7 +865,9 @@ BOOL toggleLoginItem(NSURL* loginItem, int toggleFlag)
if(NULL != itemRef)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"added %@/%@", loginItem, itemRef]);
#endif
//release
CFRelease(itemRef);
@ -879,7 +892,9 @@ BOOL toggleLoginItem(NSURL* loginItem, int toggleFlag)
else
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"removing login item");
#endif
//grab existing login items
loginItems = LSSharedFileListCopySnapshot(loginItemsRef, nil);