'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:
parent
c6eaba30f2
commit
a837ffdcf6
|
@ -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 */,
|
||||
|
|
|
@ -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]])
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -20,8 +20,5 @@
|
|||
|
||||
/* METHODS */
|
||||
|
||||
//invoked when user clicks 'more info' button
|
||||
// ->open OverSights webpage
|
||||
- (IBAction)moreInfo:(id)sender;
|
||||
|
||||
@end
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 |
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue