(full) rewrite leveraging logging subsystem
+ M1 native support
+ execute script on event (#2)
+ improved Big Sur compatibility/issues (#3, #4, #6, #7)
This commit is contained in:
Patrick Wardle 2021-05-02 22:26:55 -10:00
parent 596d47ce8f
commit 79067635d2
218 changed files with 27904 additions and 11855 deletions

22
.gitignore vendored Normal file
View File

@ -0,0 +1,22 @@
## macOS
.DS_Store
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
# Carthage
Carthage/Build/
Carthage/Checkouts

View File

@ -0,0 +1,515 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 48;
objects = {
/* Begin PBXBuildFile section */
7D16D6941F64E43300DB3161 /* Update.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D16D6901F64E43300DB3161 /* Update.m */; };
7D16D6951F64E43300DB3161 /* UpdateWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D16D6911F64E43300DB3161 /* UpdateWindow.xib */; };
7D16D6961F64E43300DB3161 /* UpdateWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D16D6931F64E43300DB3161 /* UpdateWindowController.m */; };
7D3B75141F13354900568828 /* StatusBarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D3B74FD1F13354900568828 /* StatusBarItem.m */; };
7D5C5F521F200B9200C0E94A /* utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D5C5F511F200B9200C0E94A /* utilities.m */; };
7D7755E81F02E05B00D0017D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7755E71F02E05B00D0017D /* AppDelegate.m */; };
7D7755EB1F02E05B00D0017D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D7755EA1F02E05B00D0017D /* main.m */; };
7D7755F01F02E05B00D0017D /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D7755EE1F02E05B00D0017D /* MainMenu.xib */; };
7DD25FF01F23B73C00277EC4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7DD25FEF1F23B73C00277EC4 /* Assets.xcassets */; };
CD22785E20434CAC00C72C76 /* StatusBarPopover.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD22785B20434CAB00C72C76 /* StatusBarPopover.xib */; };
CD22785F20434CAC00C72C76 /* StatusBarPopoverController.m in Sources */ = {isa = PBXBuildFile; fileRef = CD22785D20434CAB00C72C76 /* StatusBarPopoverController.m */; };
CD2F8008244551AC009C3D77 /* NSApplicationKeyEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = CD2F8007244551AC009C3D77 /* NSApplicationKeyEvents.m */; };
CD2F800C24455333009C3D77 /* AboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD2F800924455333009C3D77 /* AboutWindow.xib */; };
CD2F800D24455333009C3D77 /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CD2F800B24455333009C3D77 /* AboutWindowController.m */; };
CD2F801724468A8C009C3D77 /* patrons.txt in Resources */ = {isa = PBXBuildFile; fileRef = CD2F801624468A8C009C3D77 /* patrons.txt */; };
CD6836682391DB6F00CF19C1 /* security.plist in Resources */ = {isa = PBXBuildFile; fileRef = CD6836672391DB6F00CF19C1 /* security.plist */; };
CD8FD5D523BAE2D200EFE0FB /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD8FD5D323BAE2D100EFE0FB /* Preferences.xib */; };
CD8FD5D623BAE2D200EFE0FB /* PrefsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CD8FD5D423BAE2D200EFE0FB /* PrefsWindowController.m */; };
CD8FD5F623C05AD900EFE0FB /* RuleRow.m in Sources */ = {isa = PBXBuildFile; fileRef = CD8FD5F023C05AD800EFE0FB /* RuleRow.m */; };
CD8FD5F723C05AD900EFE0FB /* RuleRowCell.m in Sources */ = {isa = PBXBuildFile; fileRef = CD8FD5F223C05AD800EFE0FB /* RuleRowCell.m */; };
CD8FD5F823C05AD900EFE0FB /* RulesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CD8FD5F323C05AD800EFE0FB /* RulesWindowController.m */; };
CD8FD5FA23C05AD900EFE0FB /* Rules.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD8FD5F523C05AD900EFE0FB /* Rules.xib */; };
CDA88A792537CE2400C469BF /* Sentry.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CD21501B20AD2EE000CEF17B /* Sentry.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CDC6098E263CB7EB006D1332 /* LogMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = CDC6098D263CB7EB006D1332 /* LogMonitor.m */; };
CDC60991263CBD36006D1332 /* AVMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = CDC60990263CBD36006D1332 /* AVMonitor.m */; };
CDC60994263CBEE7006D1332 /* Client.m in Sources */ = {isa = PBXBuildFile; fileRef = CDC60993263CBEE7006D1332 /* Client.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
CDA88A782537CE1A00C469BF /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
CDA88A792537CE2400C469BF /* Sentry.framework in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
7D16D68F1F64E43300DB3161 /* Update.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Update.h; sourceTree = "<group>"; };
7D16D6901F64E43300DB3161 /* Update.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Update.m; sourceTree = "<group>"; };
7D16D6911F64E43300DB3161 /* UpdateWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UpdateWindow.xib; sourceTree = "<group>"; };
7D16D6921F64E43300DB3161 /* UpdateWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UpdateWindowController.h; sourceTree = "<group>"; };
7D16D6931F64E43300DB3161 /* UpdateWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UpdateWindowController.m; sourceTree = "<group>"; };
7D3B74FD1F13354900568828 /* StatusBarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StatusBarItem.m; sourceTree = "<group>"; };
7D3B74FF1F13354900568828 /* StatusBarItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StatusBarItem.h; sourceTree = "<group>"; };
7D3B75021F13354900568828 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = MainMenu.xib; sourceTree = "<group>"; };
7D3B75221F13357D00568828 /* consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = consts.h; path = ../Shared/consts.h; sourceTree = "<group>"; };
7D5C5F501F200B9200C0E94A /* utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = utilities.h; path = ../Shared/utilities.h; sourceTree = "<group>"; };
7D5C5F511F200B9200C0E94A /* utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = utilities.m; path = ../Shared/utilities.m; sourceTree = "<group>"; };
7D7755E31F02E05B00D0017D /* OverSight.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OverSight.app; sourceTree = BUILT_PRODUCTS_DIR; };
7D7755E61F02E05B00D0017D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7D7755E71F02E05B00D0017D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
7D7755EA1F02E05B00D0017D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
7D7755EF1F02E05B00D0017D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
7D7755F11F02E05B00D0017D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7DD25FEF1F23B73C00277EC4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
CD21501B20AD2EE000CEF17B /* Sentry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sentry.framework; path = ../Carthage/Build/Mac/Sentry.framework; sourceTree = "<group>"; };
CD22785B20434CAB00C72C76 /* StatusBarPopover.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StatusBarPopover.xib; sourceTree = "<group>"; };
CD22785C20434CAB00C72C76 /* StatusBarPopoverController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StatusBarPopoverController.h; sourceTree = "<group>"; };
CD22785D20434CAB00C72C76 /* StatusBarPopoverController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StatusBarPopoverController.m; sourceTree = "<group>"; };
CD2F8006244551AB009C3D77 /* NSApplicationKeyEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSApplicationKeyEvents.h; sourceTree = "<group>"; };
CD2F8007244551AC009C3D77 /* NSApplicationKeyEvents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSApplicationKeyEvents.m; sourceTree = "<group>"; };
CD2F800924455333009C3D77 /* AboutWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutWindow.xib; sourceTree = "<group>"; };
CD2F800A24455333009C3D77 /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutWindowController.h; sourceTree = "<group>"; };
CD2F800B24455333009C3D77 /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutWindowController.m; sourceTree = "<group>"; };
CD2F801624468A8C009C3D77 /* patrons.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = patrons.txt; path = ../Shared/patrons.txt; sourceTree = "<group>"; };
CD6836672391DB6F00CF19C1 /* security.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = security.plist; sourceTree = "<group>"; };
CD8FD5D223BAE2D100EFE0FB /* PrefsWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrefsWindowController.h; sourceTree = "<group>"; };
CD8FD5D323BAE2D100EFE0FB /* Preferences.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
CD8FD5D423BAE2D200EFE0FB /* PrefsWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PrefsWindowController.m; sourceTree = "<group>"; };
CD8FD5ED23C05AD800EFE0FB /* RuleRowCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RuleRowCell.h; sourceTree = "<group>"; };
CD8FD5EE23C05AD800EFE0FB /* RulesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RulesWindowController.h; sourceTree = "<group>"; };
CD8FD5F023C05AD800EFE0FB /* RuleRow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuleRow.m; sourceTree = "<group>"; };
CD8FD5F123C05AD800EFE0FB /* RuleRow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RuleRow.h; sourceTree = "<group>"; };
CD8FD5F223C05AD800EFE0FB /* RuleRowCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuleRowCell.m; sourceTree = "<group>"; };
CD8FD5F323C05AD800EFE0FB /* RulesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RulesWindowController.m; sourceTree = "<group>"; };
CD8FD5F523C05AD900EFE0FB /* Rules.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = Rules.xib; sourceTree = "<group>"; };
CDC6098C263CB7EB006D1332 /* LogMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogMonitor.h; sourceTree = "<group>"; };
CDC6098D263CB7EB006D1332 /* LogMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LogMonitor.m; sourceTree = "<group>"; };
CDC6098F263CBD36006D1332 /* AVMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AVMonitor.h; sourceTree = "<group>"; };
CDC60990263CBD36006D1332 /* AVMonitor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AVMonitor.m; sourceTree = "<group>"; };
CDC60992263CBEE7006D1332 /* Client.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Client.h; sourceTree = "<group>"; };
CDC60993263CBEE7006D1332 /* Client.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Client.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
7D7755E01F02E05B00D0017D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
7D3B75001F13354900568828 /* Base.lproj */ = {
isa = PBXGroup;
children = (
7D3B75011F13354900568828 /* MainMenu.xib */,
);
path = Base.lproj;
sourceTree = "<group>";
};
7D3B75201F13356900568828 /* Shared */ = {
isa = PBXGroup;
children = (
CD2F801624468A8C009C3D77 /* patrons.txt */,
7D3B75221F13357D00568828 /* consts.h */,
7D5C5F501F200B9200C0E94A /* utilities.h */,
7D5C5F511F200B9200C0E94A /* utilities.m */,
);
name = Shared;
sourceTree = "<group>";
};
7D7755DA1F02E05B00D0017D = {
isa = PBXGroup;
children = (
7D3B75201F13356900568828 /* Shared */,
7D7755E51F02E05B00D0017D /* Source */,
CDAFF434203E55F300F27635 /* Frameworks */,
7D7755E41F02E05B00D0017D /* Products */,
);
sourceTree = "<group>";
};
7D7755E41F02E05B00D0017D /* Products */ = {
isa = PBXGroup;
children = (
7D7755E31F02E05B00D0017D /* OverSight.app */,
);
name = Products;
sourceTree = "<group>";
};
7D7755E51F02E05B00D0017D /* Source */ = {
isa = PBXGroup;
children = (
CD2F800924455333009C3D77 /* AboutWindow.xib */,
CD2F800A24455333009C3D77 /* AboutWindowController.h */,
CD2F800B24455333009C3D77 /* AboutWindowController.m */,
7D7755E61F02E05B00D0017D /* AppDelegate.h */,
7D7755E71F02E05B00D0017D /* AppDelegate.m */,
7DD25FEF1F23B73C00277EC4 /* Assets.xcassets */,
CDC6098F263CBD36006D1332 /* AVMonitor.h */,
CDC60990263CBD36006D1332 /* AVMonitor.m */,
7D3B75001F13354900568828 /* Base.lproj */,
CDC60992263CBEE7006D1332 /* Client.h */,
CDC60993263CBEE7006D1332 /* Client.m */,
7D7755F11F02E05B00D0017D /* Info.plist */,
CDC6098C263CB7EB006D1332 /* LogMonitor.h */,
CDC6098D263CB7EB006D1332 /* LogMonitor.m */,
7D7755EE1F02E05B00D0017D /* MainMenu.xib */,
CD2F8006244551AB009C3D77 /* NSApplicationKeyEvents.h */,
CD2F8007244551AC009C3D77 /* NSApplicationKeyEvents.m */,
CD8FD5D323BAE2D100EFE0FB /* Preferences.xib */,
CD8FD5D223BAE2D100EFE0FB /* PrefsWindowController.h */,
CD8FD5D423BAE2D200EFE0FB /* PrefsWindowController.m */,
CD8FD5F123C05AD800EFE0FB /* RuleRow.h */,
CD8FD5F023C05AD800EFE0FB /* RuleRow.m */,
CD8FD5ED23C05AD800EFE0FB /* RuleRowCell.h */,
CD8FD5F223C05AD800EFE0FB /* RuleRowCell.m */,
CD8FD5F523C05AD900EFE0FB /* Rules.xib */,
CD8FD5EE23C05AD800EFE0FB /* RulesWindowController.h */,
CD8FD5F323C05AD800EFE0FB /* RulesWindowController.m */,
7D3B74FF1F13354900568828 /* StatusBarItem.h */,
7D3B74FD1F13354900568828 /* StatusBarItem.m */,
CD22785B20434CAB00C72C76 /* StatusBarPopover.xib */,
CD22785C20434CAB00C72C76 /* StatusBarPopoverController.h */,
CD22785D20434CAB00C72C76 /* StatusBarPopoverController.m */,
7D7755E91F02E05B00D0017D /* Supporting Files */,
7D16D68F1F64E43300DB3161 /* Update.h */,
7D16D6901F64E43300DB3161 /* Update.m */,
7D16D6911F64E43300DB3161 /* UpdateWindow.xib */,
7D16D6921F64E43300DB3161 /* UpdateWindowController.h */,
7D16D6931F64E43300DB3161 /* UpdateWindowController.m */,
);
name = Source;
path = Application;
sourceTree = "<group>";
};
7D7755E91F02E05B00D0017D /* Supporting Files */ = {
isa = PBXGroup;
children = (
CD6836672391DB6F00CF19C1 /* security.plist */,
7D7755EA1F02E05B00D0017D /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
CDAFF434203E55F300F27635 /* Frameworks */ = {
isa = PBXGroup;
children = (
CD21501B20AD2EE000CEF17B /* Sentry.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
7D7755E21F02E05B00D0017D /* Application */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7D7755F41F02E05B00D0017D /* Build configuration list for PBXNativeTarget "Application" */;
buildPhases = (
7D7755DF1F02E05B00D0017D /* Sources */,
7D7755E01F02E05B00D0017D /* Frameworks */,
7D7755E11F02E05B00D0017D /* Resources */,
CDA88A782537CE1A00C469BF /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = Application;
productName = loginItem;
productReference = 7D7755E31F02E05B00D0017D /* OverSight.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
7D7755DB1F02E05B00D0017D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1000;
ORGANIZATIONNAME = "Objective-See";
TargetAttributes = {
7D7755E21F02E05B00D0017D = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = VBG97UB4TA;
ProvisioningStyle = Manual;
SystemCapabilities = {
com.apple.ApplicationGroups.Mac = {
enabled = 0;
};
com.apple.HardenedRuntime = {
enabled = 1;
};
com.apple.Sandbox = {
enabled = 0;
};
};
};
};
};
buildConfigurationList = 7D7755DE1F02E05B00D0017D /* Build configuration list for PBXProject "Application" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
mainGroup = 7D7755DA1F02E05B00D0017D;
productRefGroup = 7D7755E41F02E05B00D0017D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
7D7755E21F02E05B00D0017D /* Application */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
7D7755E11F02E05B00D0017D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CD8FD5D523BAE2D200EFE0FB /* Preferences.xib in Resources */,
CD6836682391DB6F00CF19C1 /* security.plist in Resources */,
CD2F800C24455333009C3D77 /* AboutWindow.xib in Resources */,
7D16D6951F64E43300DB3161 /* UpdateWindow.xib in Resources */,
CD22785E20434CAC00C72C76 /* StatusBarPopover.xib in Resources */,
CD2F801724468A8C009C3D77 /* patrons.txt in Resources */,
CD8FD5FA23C05AD900EFE0FB /* Rules.xib in Resources */,
7D7755F01F02E05B00D0017D /* MainMenu.xib in Resources */,
7DD25FF01F23B73C00277EC4 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
7D7755DF1F02E05B00D0017D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7D16D6961F64E43300DB3161 /* UpdateWindowController.m in Sources */,
7D3B75141F13354900568828 /* StatusBarItem.m in Sources */,
7D7755EB1F02E05B00D0017D /* main.m in Sources */,
CD2F8008244551AC009C3D77 /* NSApplicationKeyEvents.m in Sources */,
7D5C5F521F200B9200C0E94A /* utilities.m in Sources */,
CD8FD5D623BAE2D200EFE0FB /* PrefsWindowController.m in Sources */,
CDC6098E263CB7EB006D1332 /* LogMonitor.m in Sources */,
CD8FD5F823C05AD900EFE0FB /* RulesWindowController.m in Sources */,
7D7755E81F02E05B00D0017D /* AppDelegate.m in Sources */,
CDC60994263CBEE7006D1332 /* Client.m in Sources */,
CD8FD5F723C05AD900EFE0FB /* RuleRowCell.m in Sources */,
CD22785F20434CAC00C72C76 /* StatusBarPopoverController.m in Sources */,
CD8FD5F623C05AD900EFE0FB /* RuleRow.m in Sources */,
CDC60991263CBD36006D1332 /* AVMonitor.m in Sources */,
CD2F800D24455333009C3D77 /* AboutWindowController.m in Sources */,
7D16D6941F64E43300DB3161 /* Update.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
7D3B75011F13354900568828 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
7D3B75021F13354900568828 /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
7D7755EE1F02E05B00D0017D /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
7D7755EF1F02E05B00D0017D /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
7D7755F21F02E05B00D0017D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = NO;
SDKROOT = macosx;
};
name = Debug;
};
7D7755F31F02E05B00D0017D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
7D7755F51F02E05B00D0017D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2.0.0;
DEVELOPMENT_TEAM = VBG97UB4TA;
ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac";
INFOPLIST_FILE = Application/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(LD_RUNPATH_SEARCH_PATHS_$(IS_MACCATALYST)) @executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = "";
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 2.0.0;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.oversight";
PRODUCT_NAME = OverSight;
SKIP_INSTALL = YES;
};
name = Debug;
};
7D7755F61F02E05B00D0017D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2.0.0;
DEVELOPMENT_TEAM = VBG97UB4TA;
ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac";
INFOPLIST_FILE = Application/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(LD_RUNPATH_SEARCH_PATHS_$(IS_MACCATALYST)) @executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = "";
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 2.0.0;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.oversight";
PRODUCT_NAME = OverSight;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
7D7755DE1F02E05B00D0017D /* Build configuration list for PBXProject "Application" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7D7755F21F02E05B00D0017D /* Debug */,
7D7755F31F02E05B00D0017D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7D7755F41F02E05B00D0017D /* Build configuration list for PBXNativeTarget "Application" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7D7755F51F02E05B00D0017D /* Debug */,
7D7755F61F02E05B00D0017D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 7D7755DB1F02E05B00D0017D /* Project object */;
}

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7D7755E21F02E05B00D0017D"
BuildableName = "OverSight.app"
BlueprintName = "Application"
ReferencedContainer = "container:Application.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7D7755E21F02E05B00D0017D"
BuildableName = "OverSight.app"
BlueprintName = "Application"
ReferencedContainer = "container:Application.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7D7755E21F02E05B00D0017D"
BuildableName = "OverSight.app"
BlueprintName = "Application"
ReferencedContainer = "container:Application.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,38 @@
//
// AVMonitor.h
// Application
//
// Created by Patrick Wardle on 4/30/21.
// Copyright © 2021 Objective-See. All rights reserved.
//
@import Cocoa;
@import Foundation;
@import UserNotifications;
#import "LogMonitor.h"
@interface AVMonitor : NSObject <UNUserNotificationCenterDelegate>
//log monitor
@property(nonatomic, retain)LogMonitor* logMonitor;
//clients
@property(nonatomic, retain)NSMutableArray* clients;
//camera state
@property NSControlStateValue cameraState;
//microphone state
@property NSControlStateValue microphoneState;
/* METHODS */
//start
-(void)start;
//stop
-(void)stop;
@end

View File

@ -0,0 +1,887 @@
//
// AVMonitor.m
// Application
//
// Created by Patrick Wardle on 4/30/21.
// Copyright © 2021 Objective-See. All rights reserved.
//
@import OSLog;
@import AVFoundation;
#import "consts.h"
#import "Client.h"
#import "AVMonitor.h"
#import "utilities.h"
#import <CoreAudio/CoreAudio.h>
#import <CoreMedia/CoreMedia.h>
#import <Foundation/Foundation.h>
#import <CoreMediaIO/CMIOHardware.h>
/* GLOBALS */
//log handle
extern os_log_t logHandle;
@implementation AVMonitor
@synthesize clients;
//init
// create XPC connection & set remote obj interface
-(id)init
{
//action: allow
UNNotificationAction *allow = nil;
//action: allow
UNNotificationAction *allowAlways = nil;
//action: block
UNNotificationAction *block = nil;
//category
UNNotificationCategory* category = nil;
//super
self = [super init];
if(nil != self)
{
//init log monitor
self.logMonitor = [[LogMonitor alloc] init];
//init clients
self.clients = [NSMutableArray array];
//set up delegate
UNUserNotificationCenter.currentNotificationCenter.delegate = self;
//ask for notificaitons
[UNUserNotificationCenter.currentNotificationCenter requestAuthorizationWithOptions:(UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error)
{
//dbg msg
os_log_debug(logHandle, "permission to display notifications granted? %d (error: %@)", granted, error);
//not granted/error
if( (nil != error) &&
(YES != granted) )
{
//main thread?
if(YES == NSThread.isMainThread)
{
//show an error alert
showAlert(@"ERROR: permisssion not granted to display notifications", [NSString stringWithFormat:@"system error information: %@", error]);
}
//bg thread
// show alert on main thread
else
{
dispatch_async(dispatch_get_main_queue(),
^{
//show an error alert
showAlert(@"ERROR: permisssion not granted to display notifications", [NSString stringWithFormat:@"system error information: %@", error]);
});
}
}
}];
//init allow action
allow = [UNNotificationAction actionWithIdentifier:@"Allow" title:@"Allow (Once)" options:UNNotificationActionOptionNone];
//init allow action
allowAlways = [UNNotificationAction actionWithIdentifier:@"AllowAlways" title:@"Allow (Always)" options:UNNotificationActionOptionNone];
//init block action
block = [UNNotificationAction actionWithIdentifier:@"Block" title:@"Block" options:UNNotificationActionOptionNone];
//init category
category = [UNNotificationCategory categoryWithIdentifier:@BUNDLE_ID actions:@[allow, allowAlways, block] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
//set categories
[UNUserNotificationCenter.currentNotificationCenter setNotificationCategories:[NSSet setWithObject:category]];
//any active cameras
// only call on intel, since broken on M1
if(YES != AppleSilicon())
{
self.cameraState = [self isACameraOn];
}
}
return self;
}
//monitor AV
// also generate alerts as needed
-(void)start
{
//dbg msg
os_log_debug(logHandle, "starting AV monitoring");
//invoke appropriate architecute monitoring logic
(YES == AppleSilicon()) ? [self monitorM1] : [self monitorIntel];
return;
}
//on M1 systems
// monitor for video events via 'appleh13camerad'
-(void)monitorM1
{
//dbg msg
os_log_debug(logHandle, "CPU architecuture: M1, will leverage 'appleh13camerad'");
//start logging
[self.logMonitor start:[NSPredicate predicateWithFormat:@"process == 'appleh13camerad'"] callback:^(OSLogEvent* event) {
//new client
// add to list
if( (YES == [event.composedMessage hasPrefix:@"TCC access already allowed for pid"]) ||
(YES == [event.composedMessage hasPrefix:@"TCC preflight access returned allowed for pid"]) )
{
//client
Client* client = nil;
//pid
NSNumber* pid = nil;
//dbg msg
os_log_debug(logHandle, "new client msg: %{public}@", event.composedMessage);
//extract pid
pid = @([event.composedMessage componentsSeparatedByString:@" "].lastObject.intValue);
if(nil != pid)
{
//init client
client = [[Client alloc] init];
client.pid = pid;
client.path = getProcessPath(pid.intValue);
client.name = getProcessName(client.path);
//dbg msg
os_log_debug(logHandle, "new client: %{public}@", client);
//camera already on?
// show notifcation for new client
if(NSControlStateValueOn == self.cameraState)
{
//show notification
[self generateNotification:Device_Camera state:NSControlStateValueOn client:client];
//execute user-specified action?
if(YES == [NSUserDefaults.standardUserDefaults boolForKey:PREF_EXECUTE_ACTION])
{
//execute action
[self executeUserAction:Device_Camera state:NSControlStateValueOff client:nil];
}
}
//will handle when "on" camera msg is delivered
else
{
//add client
[self.clients addObject:client];
}
}
}
//camera on
// show alert!
else if(YES == [event.composedMessage isEqualToString:@"StartStream : StartStream: Powering ON camera"])
{
//client
Client* client = nil;
//dbg msg
os_log_debug(logHandle, "camera on msg: %{public}@", event.composedMessage);
//set state
self.cameraState = NSControlStateValueOn;
//last client should be responsible one
client = self.clients.lastObject;
//show notification
[self generateNotification:Device_Camera state:NSControlStateValueOn client:client];
//remove
[self.clients removeLastObject];
//execute user-specified action?
if(YES == [NSUserDefaults.standardUserDefaults boolForKey:PREF_EXECUTE_ACTION])
{
//execute action
[self executeUserAction:Device_Camera state:NSControlStateValueOn client:client];
}
}
//dead client
// remove from list
else if(YES == [event.composedMessage hasPrefix:@"Removing client: pid"])
{
//pid
NSNumber* pid = 0;
//dbg msg
os_log_debug(logHandle, "removed client msg: %{public}@", event.composedMessage);
//extract pid
pid = @([event.composedMessage componentsSeparatedByString:@" "].lastObject.intValue);
if(nil != pid)
{
//sync
@synchronized (self) {
//find and remove client
for (NSInteger i = self.clients.count - 1; i >= 0; i--)
{
if(pid != ((Client*)self.clients[i]).pid) continue;
//remove
[self.clients removeObjectAtIndex:i];
//dbg msg
os_log_debug(logHandle, "removed client at index %ld", (long)i);
}
}//sync
}
}
//camera off
// show inactive notification
else if(YES == [event.composedMessage hasPrefix:@"StopStream : Powering OFF camera"])
{
//dbg msg
os_log_debug(logHandle, "camera off msg: %{public}@", event.composedMessage);
//set state
self.cameraState = NSControlStateValueOff;
//show inactive notifcations?
if(YES != [NSUserDefaults.standardUserDefaults boolForKey:PREF_DISABLE_INACTIVE])
{
//show notification
[self generateNotification:Device_Camera state:NSControlStateValueOff client:nil];
//execute user-specified action?
if(YES == [NSUserDefaults.standardUserDefaults boolForKey:PREF_EXECUTE_ACTION])
{
//execute action
[self executeUserAction:Device_Camera state:NSControlStateValueOff client:nil];
}
}
else
{
//dbg msg
os_log_debug(logHandle, "user has set preference to ingore 'inactive' notifications");
}
}
}];
return;
}
//on Intel systems
// monitor for video events via 'VDCAssistant'
-(void)monitorIntel
{
//dbg msg
os_log_debug(logHandle, "CPU architecuture: Intel ...will leverage 'VDCAssistant'");
//msg count
// used to validate client pid to client id
__block unsigned long long msgCount = 0;
//start logging
[self.logMonitor start:[NSPredicate predicateWithFormat:@"process == 'VDCAssistant'"] callback:^(OSLogEvent* event) {
//inc
msgCount++;
//new client
// add to list
if(YES == [event.composedMessage hasPrefix:@"Client Connect for PID"])
{
//client
Client* client = nil;
//pid
NSNumber* pid = nil;
//dbg msg
os_log_debug(logHandle, "new client msg: %{public}@", event.composedMessage);
//extract pid
pid = @([event.composedMessage componentsSeparatedByString:@" "].lastObject.intValue);
if(nil != pid)
{
//init client
client = [[Client alloc] init];
client.msgCount = msgCount;
client.pid = pid;
client.path = getProcessPath(pid.intValue);
client.name = getProcessName(client.path);
//dbg msg
os_log_debug(logHandle, "new client: %{public}@", client);
//add client
[self.clients addObject:client];
}
}
//client w/ id msg
// update (last) client, with client id
else if(YES == [event.composedMessage containsString:@"GetDevicesState for client"])
{
//client
Client* client = nil;
//client id
NSNumber* clientID = nil;
//dbg msg
os_log_debug(logHandle, "new client id msg : %{public}@", event.composedMessage);
//extract client id
clientID = @([event.composedMessage componentsSeparatedByString:@" "].lastObject.intValue);
if(0 != clientID)
{
//get last client
// check that it the one in the *last* msg
client = self.clients.lastObject;
if(client.msgCount == msgCount-1)
{
//add id
client.clientID = clientID;
}
}
}
//camera on (for client)
// show notification
else if(YES == [event.composedMessage containsString:@"StartStream for client"])
{
//client
Client* client = nil;
//client id
NSNumber* clientID = nil;
//dbg msg
os_log_debug(logHandle, "camera on msg: %{public}@", event.composedMessage);
//set state
self.cameraState = NSControlStateValueOn;
//extract client id
clientID = @([event.composedMessage componentsSeparatedByString:@" "].lastObject.intValue);
if(0 != clientID)
{
//find client w/ matching id
for(Client* candidateClient in self.clients)
{
//match?
if(candidateClient.clientID == clientID)
{
//save
client = candidateClient;
//done
break;
}
}
//nil, but last client is FaceTime?
// use that, as FaceTime is "special"
if(nil == client)
{
//facetime check?
if( (YES == [((Client*)self.clients.lastObject).path isEqualToString:FACE_TIME]) &&
(YES == [NSWorkspace.sharedWorkspace.frontmostApplication.executableURL.path isEqualToString:FACE_TIME]) )
{
//set
client = self.clients.lastObject;
}
}
}
//show notification
// ok if client is (still) nil...
[self generateNotification:Device_Camera state:NSControlStateValueOn client:client];
//execute user-specified action?
if(YES == [NSUserDefaults.standardUserDefaults boolForKey:PREF_EXECUTE_ACTION])
{
//execute action
[self executeUserAction:Device_Camera state:NSControlStateValueOn client:client];
}
}
//dead client
// remove from list
// e.x. "ClientDied 1111 [PID: 2222]"
else if( (YES == [event.composedMessage hasPrefix:@"ClientDied "]) &&
(YES == [event.composedMessage hasSuffix:@"]"]) )
{
//message (trimmed)
NSString* message = nil;
//pid
NSNumber* pid = 0;
//dbg msg
os_log_debug(logHandle, "dead client msg: %{public}@", event.composedMessage);
//init message
// trim off last ']'
message = [event.composedMessage substringToIndex:event.composedMessage.length - 1];
//extract pid
pid = @([message componentsSeparatedByString:@" "].lastObject.intValue);
if(nil != pid)
{
//sync
@synchronized (self) {
for(NSInteger i = self.clients.count - 1; i >= 0; i--)
{
//no match?
if(pid != ((Client*)self.clients[i]).pid)
{
//skip
continue;
}
//remove
[self.clients removeObjectAtIndex:i];
//dbg msg
os_log_debug(logHandle, "removed client at index %ld", (long)i);
}
} //sync
}
}
//camera off
else if(YES == [event.composedMessage containsString:@"Post event kCameraStreamStop"])
{
//dbg msg
os_log_debug(logHandle, "camera off msg: %{public}@", event.composedMessage);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//all camera's off?
if(YES != [self isACameraOn])
{
//set state
self.cameraState = NSControlStateValueOff;
//show inactive notifcations?
if(YES != [NSUserDefaults.standardUserDefaults boolForKey:PREF_DISABLE_INACTIVE])
{
//show notification
[self generateNotification:Device_Camera state:NSControlStateValueOff client:nil];
//execute user-specified action?
if(YES == [NSUserDefaults.standardUserDefaults boolForKey:PREF_EXECUTE_ACTION])
{
//execute action
[self executeUserAction:Device_Camera state:NSControlStateValueOff client:nil];
}
}
else
{
//dbg msg
os_log_debug(logHandle, "user has set preference to ingore 'inactive' notifications");
}
}
});
}
}];
return;
}
//is (any) camera on?
-(BOOL)isACameraOn
{
//flag
BOOL cameraOn = NO;
//selector for getting device id
SEL methodSelector = nil;
//device's connection id
unsigned int connectionID = 0;
//dbg msg
os_log_debug(logHandle, "checking if any camera is active");
//init selector
methodSelector = NSSelectorFromString(@"connectionID");
//are any cameras currently on?
for(AVCaptureDevice* currentCamera in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo])
{
//dbg msg
os_log_debug(logHandle, "device: %@/%@", currentCamera.manufacturer, currentCamera.localizedName);
//sanity check
// make sure is has private 'connectionID' iVar
if(YES != [currentCamera respondsToSelector:methodSelector])
{
//skip
continue;
}
//ignore leak warning
// we know what we're doing via this 'performSelector'
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
//grab connection ID
connectionID = (unsigned int)[currentCamera performSelector:methodSelector withObject:nil];
//restore
#pragma clang diagnostic pop
//get state
// is (any) camera on?
if(NSControlStateValueOn == [self getCameraStatus:connectionID])
{
//dbg msg
os_log_debug(logHandle, "device: %@/%@, is on!", currentCamera.manufacturer, currentCamera.localizedName);
//set
cameraOn = YES;
//done
break;
}
}
bail:
return cameraOn;
}
//check if a specified video is active
// note: on M1 this always says 'on' (smh apple)
-(UInt32)getCameraStatus:(CMIODeviceID)deviceID
{
//status var
OSStatus status = -1;
//running flag
UInt32 isRunning = -1;
//size of query flag
UInt32 propertySize = 0;
//property address struct
CMIOObjectPropertyAddress propertyStruct = {0};
//init size
propertySize = sizeof(isRunning);
//init property struct's selector
propertyStruct.mSelector = kAudioDevicePropertyDeviceIsRunningSomewhere;
//init property struct's scope
propertyStruct.mScope = kCMIOObjectPropertyScopeGlobal;
//init property struct's element
propertyStruct.mElement = 0;
//query to get 'kAudioDevicePropertyDeviceIsRunningSomewhere' status
status = CMIOObjectGetPropertyData(deviceID, &propertyStruct, 0, NULL, sizeof(kAudioDevicePropertyDeviceIsRunningSomewhere), &propertySize, &isRunning);
if(noErr != status)
{
//err msg
os_log_error(logHandle, "ERROR: failed to get camera status (error: %#x)", status);
//set error
isRunning = -1;
//bail
goto bail;
}
//dbg msg
os_log_debug(logHandle, "isRunning: %d", isRunning);
bail:
return isRunning;
}
//build and display notification
-(void)generateNotification:(AVDevice)device state:(NSControlStateValue)state client:(Client*)client
{
//notification content
UNMutableNotificationContent* content = nil;
//notificaito0n request
UNNotificationRequest* request = nil;
//alloc content
content = [[UNMutableNotificationContent alloc] init];
//title
NSMutableString* title = nil;
//client?
// check if allowed
if(nil != client)
{
//match is simply: device and path
for(NSDictionary* allowedItem in [NSUserDefaults.standardUserDefaults objectForKey:PREFS_ALLOWED_ITEMS])
{
//match?
if( (allowedItem[EVENT_DEVICE] == (NSNumber*)@(device)) &&
(YES == [allowedItem[EVENT_PROCESS_PATH] isEqualToString:client.path]) )
{
//dbg msg
os_log_debug(logHandle, "%{public}@ is allowed to access %d, so no notification will be shown", client.path, device);
//done
goto bail;
}
}
}
//alloc title
title = [NSMutableString string];
//set device
(Device_Camera == device) ? [title appendString:@"Video Device"] : [title appendString:@"Audio Device"];
//set status
(NSControlStateValueOn == state) ? [title appendString:@" became active!"] : [title appendString:@" became inactive."];
//set title
content.title = title;
//have client?
// use as body
if(nil != client)
{
//set body
content.body = [NSString stringWithFormat:@"Process: %@ (%@)", getProcessName(client.path), client.pid];
//set category
content.categoryIdentifier = @BUNDLE_ID;
//set user info
content.userInfo = @{EVENT_DEVICE:@(Device_Camera), EVENT_PROCESS_ID:client.pid, EVENT_PROCESS_PATH:client.path};
}
//init request
request = [UNNotificationRequest requestWithIdentifier:NSUUID.UUID.UUIDString content:content trigger:NULL];
//send notification
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error)
{
//error?
if(nil != error)
{
//err msg
os_log_error(logHandle, "ERROR failed to deliver notification (error: %@)", error);
}
}];
bail:
return;
}
//execute user action
-(BOOL)executeUserAction:(AVDevice)device state:(NSControlStateValue)state client:(Client*)client
{
//flag
BOOL wasExecuted = NO;
//path to action
NSString* action = nil;
//args
NSMutableArray* args = nil;
//dbg msg
os_log_debug(logHandle, "executing user action");
//grab action
action = [NSUserDefaults.standardUserDefaults objectForKey:PREF_EXECUTE_PATH];
if(YES != [NSFileManager.defaultManager fileExistsAtPath:action])
{
//err msg
os_log_error(logHandle, "ERROR: %{public}@ is not a valid action", action);
//bail
goto bail;
}
//pass args?
if(YES == [NSUserDefaults.standardUserDefaults boolForKey:PREF_EXECUTE_ACTION_ARGS])
{
//alloc
args = [NSMutableArray array];
//add device
[args addObject:@"-device"];
(Device_Camera == device) ? [args addObject:@"camera"] : [args addObject:@"microphone"];
//add event
[args addObject:@"-event"];
(NSControlStateValueOn == state) ? [args addObject:@"on"] : [args addObject:@"off"];
//add process
if(nil != client)
{
//add
[args addObject:@"-process"];
[args addObject:client.pid.stringValue];
}
}
//exec user specified action
execTask(action, args, NO, NO);
bail:
return wasExecuted;
}
//stop monitor
-(void)stop
{
//stop logging
[self.logMonitor stop];
return;
}
# pragma mark UNNotificationCenter Delegate Methods
//handle user response to notification
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
//allowed items
NSMutableArray* allowedItems = nil;
//device
NSNumber* device = nil;
//process path
NSString* processPath = nil;
//process name
NSString* processName = nil;
//process id
NSNumber* processID = nil;
//error
int error = 0;
//dbg msg
os_log_debug(logHandle, "user response to notification: %{public}@", response);
//extract device
device = response.notification.request.content.userInfo[EVENT_DEVICE];
//extact process path
processPath = response.notification.request.content.userInfo[EVENT_PROCESS_PATH];
//extract process id
processID = response.notification.request.content.userInfo[EVENT_PROCESS_ID];
//get process name
processName = getProcessName(processPath);
//allow?
// really nothing to do
if(YES == [response.actionIdentifier isEqualToString:@"Allow"])
{
//dbg msg
os_log_debug(logHandle, "user clicked 'Allow'");
}
//always allow?
// added to 'allowed' items
else if(YES == [response.actionIdentifier isEqualToString:@"AllowAlways"])
{
//dbg msg
os_log_debug(logHandle, "user clicked 'Allow Always'");
//load allowed items
allowedItems = [[NSUserDefaults.standardUserDefaults objectForKey:PREFS_ALLOWED_ITEMS] mutableCopy];
if(nil == allowedItems)
{
//alloc
allowedItems = [NSMutableArray array];
}
//add item
[allowedItems addObject:@{EVENT_PROCESS_PATH:processPath, EVENT_DEVICE:device}];
//save & sync
[NSUserDefaults.standardUserDefaults setObject:allowedItems forKey:PREFS_ALLOWED_ITEMS];
[NSUserDefaults.standardUserDefaults synchronize];
//dbg msg
os_log_debug(logHandle, "added %{public}@ to list of allowed items", processPath);
//broadcast
[[NSNotificationCenter defaultCenter] postNotificationName:RULES_CHANGED object:nil userInfo:nil];
}
//block?
// kill process
else if(YES == [response.actionIdentifier isEqualToString:@"Block"])
{
//dbg msg
os_log_debug(logHandle, "user clicked 'Block'");
//kill
error = kill(processID.intValue, SIGKILL);
if(0 != error)
{
//err msg
os_log_error(logHandle, "ERROR: failed to kill %@ (%@)", processName, processID);
//show an alert
showAlert([NSString stringWithFormat:@"ERROR: failed to block %@ (%@)", processName, processID], [NSString stringWithFormat:@"system error code: %d", error]);
//bail
goto bail;
}
//dbg msg
os_log_debug(logHandle, "killed %@ (%@)", processName, processID);
}
bail:
//gotta call
completionHandler();
return;
}
@end

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -14,23 +15,18 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES"/>
<rect key="contentRect" x="196" y="240" width="486" height="301"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1418"/>
<rect key="screenRect" x="0.0" y="0.0" width="3440" height="1415"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="486" height="301"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lEv-Wj-6S5">
<rect key="frame" x="10" y="202" 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="153" y="231" width="180" height="50"/>
<rect key="frame" x="158" y="235" width="268" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="overSight" id="Ws8-bD-j2R"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="OverSight" id="Ws8-bD-j2R"/>
</imageView>
<button verticalHuggingPriority="750" fixedFrame="YES" tag="101" translatesAutoresizingMaskIntoConstraints="NO" id="HZZ-Es-mpy">
<rect key="frame" x="344" y="13" width="128" height="32"/>
@ -57,19 +53,21 @@
<scrollView fixedFrame="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5if-J7-OZf">
<rect key="frame" x="20" y="68" width="446" height="97"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" ambiguous="YES" id="WiG-CC-JV3">
<clipView key="contentView" drawsBackground="NO" id="WiG-CC-JV3">
<rect key="frame" x="1" y="1" width="444" height="95"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView ambiguous="YES" editable="NO" importsGraphics="NO" usesFontPanel="YES" findStyle="panel" continuousSpellChecking="YES" allowsUndo="YES" usesRuler="YES" allowsNonContiguousLayout="YES" quoteSubstitution="YES" dashSubstitution="YES" spellingCorrection="YES" smartInsertDelete="YES" id="j2X-Ub-niY">
<textView editable="NO" importsGraphics="NO" verticallyResizable="YES" usesFontPanel="YES" findStyle="panel" continuousSpellChecking="YES" allowsUndo="YES" usesRuler="YES" allowsNonContiguousLayout="YES" quoteSubstitution="YES" dashSubstitution="YES" spellingCorrection="YES" smartInsertDelete="YES" id="j2X-Ub-niY">
<rect key="frame" x="0.0" y="0.0" width="444" height="95"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="444" height="95"/>
<size key="maxSize" width="463" height="10000000"/>
<attributedString key="textStorage">
<fragment content="list here...">
<attributes>
<color key="NSColor" name="textColor" catalog="System" colorSpace="catalog"/>
<font key="NSFont" size="13" name="Menlo-Regular"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
@ -78,18 +76,17 @@
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="iNL-vM-cLm">
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="iNL-vM-cLm">
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="6bw-3O-hPc">
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="6bw-3O-hPc">
<rect key="frame" x="429" y="1" width="16" height="95"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fZJ-sG-ZuJ">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fZJ-sG-ZuJ">
<rect key="frame" x="20" y="166" width="153" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Supporters/Patrons:" id="fJg-qw-wDf">
@ -99,7 +96,7 @@
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OSm-xS-Dmd">
<rect key="frame" x="158" y="220" width="182" height="19"/>
<rect key="frame" x="158" y="217" 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"/>
@ -107,6 +104,11 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lEv-Wj-6S5">
<rect key="frame" x="22" y="193" width="96" height="92"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="Icon" id="xKf-GK-m0k"/>
</imageView>
</subviews>
</view>
<connections>
@ -116,7 +118,7 @@
</window>
</objects>
<resources>
<image name="icon" width="512" height="512"/>
<image name="overSight" width="1396" height="286"/>
<image name="Icon" width="512" height="512"/>
<image name="OverSight" width="1301" height="227"/>
</resources>
</document>

View File

@ -0,0 +1,31 @@
//
// file: AboutWindowController.h
// project: OverSight (login item)
// description: 'about' window controller (header)
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
@import Cocoa;
@interface AboutWindowController : NSWindowController <NSWindowDelegate>
{
}
/* PROPERTIES */
//version label/string
@property (weak) IBOutlet NSTextField *versionLabel;
//patrons
@property (unsafe_unretained) IBOutlet NSTextView *patrons;
/* METHODS */
//automatically invoked when user clicks any of the buttons
// ->perform actions, such as loading patreon or products URL
-(IBAction)buttonHandler:(id)sender;
@end

View File

@ -1,13 +1,14 @@
//
// AboutWindowController.m
// OverSight
// file: AboutWindowController.m
// project: OverSight (login item)
// description: 'about' window controller
//
// Created by Patrick Wardle on 7/15/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "Utilities.h"
#import "consts.h"
#import "utilities.h"
#import "AboutWindowController.h"
@implementation AboutWindowController
@ -24,26 +25,48 @@
}
//automatically invoked when window is loaded
// ->set to white
// set to window to white, set app version, patrons, etc
-(void)windowDidLoad
{
//version
NSString* version = nil;
//super
[super windowDidLoad];
//make white
[self.window setBackgroundColor: NSColor.whiteColor];
//not in dark mode?
// make window white
if(YES != isDarkMode())
{
//make white
self.window.backgroundColor = NSColor.whiteColor;
}
//grab app version
version = getAppVersion();
if(nil == version)
{
//default
version = @"unknown";
}
//set version sting
self.versionLabel.stringValue = [NSString stringWithFormat:@"Version: %@", getAppVersion()];
self.versionLabel.stringValue = version;
//load patrons
// <3 you guys & girls
self.patrons.string = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"patrons" ofType:@"txt"] encoding:NSUTF8StringEncoding error:NULL];
if(nil == self.patrons.string)
{
//default
self.patrons.string = @"error: failed to load patrons :/";
}
return;
}
//automatically invoked when window is closing
// ->make ourselves unmodal
// ->make window unmodal
-(void)windowWillClose:(NSNotification *)notification
{
//make un-modal
@ -53,7 +76,7 @@
}
//automatically invoked when user clicks any of the buttons
// ->load patreon or products webpage in user's default browser
// ->perform actions, such as loading patreon or products URL
-(IBAction)buttonHandler:(id)sender
{
//support us button

View File

@ -0,0 +1,67 @@
//
// file: AppDelegate.h
// project: OverSight (login item)
// description: app delegate for login item (header)
//
// created by Patrick Wardle
// copyright (c) 2020 Objective-See. All rights reserved.
//
@import Cocoa;
@import OSLog;
#import "AVMonitor.h"
#import "StatusBarItem.h"
#import "AboutWindowController.h"
#import "PrefsWindowController.h"
#import "RulesWindowController.h"
#import "UpdateWindowController.h"
@interface AppDelegate : NSObject <NSApplicationDelegate>
/* PROPERTIES */
//status bar menu
@property(strong) IBOutlet NSMenu* statusMenu;
//status bar menu controller
@property(nonatomic, retain)StatusBarItem* statusBarItemController;
//about window controller
@property(nonatomic, retain)AboutWindowController* aboutWindowController;
//preferences window controller
@property(nonatomic, retain)PrefsWindowController* prefsWindowController;
//rules window controller
@property(nonatomic, retain)RulesWindowController* rulesWindowController;
//update window controller
@property(nonatomic, retain)UpdateWindowController* updateWindowController;
//AV monitor
@property(nonatomic, retain)AVMonitor* avMonitor;
/* METHODS */
//set app foreground/background
// determined by the app's window count
-(void)setActivationPolicy;
//'rules' menu item handler
// alloc and show rules window
-(IBAction)showRules:(id)sender;
//'preferences' menu item handler
// alloc and show preferences window
-(IBAction)showPreferences:(id)sender;
//toggle (status) bar icon
-(void)toggleIcon:(BOOL)state;
@end

View File

@ -0,0 +1,420 @@
//
// file: AppDelegate.m
// project: OverSight (login item)
// description: app delegate for login item
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
@import UserNotifications;
#import "consts.h"
#import "Update.h"
#import "utilities.h"
#import "LogMonitor.h"
#import "AppDelegate.h"
/* GLOBALS */
//log handle
extern os_log_t logHandle;
@implementation AppDelegate
@synthesize avMonitor;
@synthesize aboutWindowController;
@synthesize prefsWindowController;
@synthesize rulesWindowController;
@synthesize updateWindowController;
@synthesize statusBarItemController;
//app's main interface
-(void)applicationDidFinishLaunching:(NSNotification *)notification
{
//parent
NSDictionary* parent = nil;
//flag
BOOL autoLaunched = NO;
//get real parent
parent = getRealParent(getpid());
//dbg msg(s)
os_log_debug(logHandle, "(real) parent: %@", parent);
//set auto launched flag (i.e. login item)
autoLaunched = [parent[@"CFBundleIdentifier"] isEqualToString:@"com.apple.loginwindow"];
//when user (manually) runs app
// show the app's preferences window
if( (YES != autoLaunched) &&
(YES != [NSProcessInfo.processInfo.arguments containsObject:INITIAL_LAUNCH]))
{
//show preferences
[self showPreferences:nil];
}
//show status bar item/icon?
if(YES != [NSUserDefaults.standardUserDefaults boolForKey:PREF_NO_ICON_MODE])
{
//alloc/load nib
statusBarItemController = [[StatusBarItem alloc] init:self.statusMenu];
}
//enabled?
// kick off device monitoring
if(YES != [NSUserDefaults.standardUserDefaults boolForKey:PREF_IS_DISABLED])
{
//init
self.avMonitor = [[AVMonitor alloc] init];
//start
[self.avMonitor start];
}
//disabled
else
{
//dbg msg
os_log_debug(logHandle, "is disabled, didn't start monitor");
}
//automatically check for updates?
if(YES != [NSUserDefaults.standardUserDefaults boolForKey:PREF_NO_UPDATE_MODE])
{
//after a 30 seconds
// check for updates in background
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
//dbg msg
os_log_debug(logHandle, "checking for update");
//check
[self check4Update];
});
}
bail:
return;
}
//handle user double-clicks
// app is (likely) already running as login item, so show (or) activate window
-(BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)hasVisibleWindows
{
//dbg msg
os_log_debug(logHandle, "method '%s' invoked from (hasVisibleWindows: %d)", __PRETTY_FUNCTION__, hasVisibleWindows);
//no visible window(s)
// default to show preferences
if(YES != hasVisibleWindows)
{
//show prefs
[self showPreferences:nil];
}
return NO;
}
//'rules' menu item handler
// alloc and show rules window
-(IBAction)showRules:(id)sender
{
//dbg msg
os_log_debug(logHandle, "method '%s' invoked", __PRETTY_FUNCTION__);
//alloc rules window controller
if(nil == self.rulesWindowController)
{
//dbg msg
os_log_debug(logHandle, "allocating rules window controller...");
//alloc
rulesWindowController = [[RulesWindowController alloc] initWithWindowNibName:@"Rules"];
}
//configure (UI)
[self.rulesWindowController configure];
//make active
[self makeActive:self.rulesWindowController];
return;
}
//'preferences' menu item handler
// alloc and show preferences window
-(IBAction)showPreferences:(id)sender
{
//alloc prefs window controller
if(nil == self.prefsWindowController)
{
//alloc
prefsWindowController = [[PrefsWindowController alloc] initWithWindowNibName:@"Preferences"];
}
//make active
[self makeActive:self.prefsWindowController];
return;
}
//'about' menu item handler
// alloc/show the about window
-(IBAction)showAbout:(id)sender
{
//alloc/init settings window
if(nil == self.aboutWindowController)
{
//alloc/init
aboutWindowController = [[AboutWindowController alloc] initWithWindowNibName:@"AboutWindow"];
}
//center window
[[self.aboutWindowController window] center];
//show it
[self.aboutWindowController showWindow:self];
return;
}
//close window handler
// close rules || pref window
-(IBAction)closeWindow:(id)sender {
//key window
NSWindow *keyWindow = nil;
//get key window
keyWindow = [[NSApplication sharedApplication] keyWindow];
//dbg msg
os_log_debug(logHandle, "close window request (key window: %@)", keyWindow);
//close
// but only for rules/pref/about window
if( (keyWindow != self.aboutWindowController.window) &&
(keyWindow != self.prefsWindowController.window) &&
(keyWindow != self.rulesWindowController.window) )
{
//dbg msg
os_log_debug(logHandle, "key window is not rules or pref window, so ignoring...");
//ignore
goto bail;
}
//close
[keyWindow close];
//set activation policy
[self setActivationPolicy];
bail:
return;
}
//make a window control/window front/active
-(void)makeActive:(NSWindowController*)windowController
{
//make foreground
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
//center
[windowController.window center];
//show it
[windowController showWindow:self];
//make it key window
[[windowController window] makeKeyAndOrderFront:self];
//make window front
[NSApp activateIgnoringOtherApps:YES];
return;
}
//toggle (status) bar icon
-(void)toggleIcon:(BOOL)state
{
//dbg msg
os_log_debug(logHandle, "toggling icon state");
//show?
// init and show status bar item
if(YES == state)
{
//already showing?
if(nil != self.statusBarItemController)
{
//bail
goto bail;
}
//alloc/load status bar icon/menu
// will configure, and show popup/menu
statusBarItemController = [[StatusBarItem alloc] init:self.statusMenu];
}
//hide?
else
{
//already removed?
if(nil == self.statusBarItemController)
{
//bail
goto bail;
}
//remove status item
[self.statusBarItemController removeStatusItem];
//unset
self.statusBarItemController = nil;
}
bail:
return;
}
//set app foreground/background
-(void)setActivationPolicy
{
//visible window
BOOL visibleWindow = NO;
//dbg msg(s)
os_log_debug(logHandle, "setting app's activation policy");
os_log_debug(logHandle, "windows: %@", NSApp.windows);
//find any visible windows
for(NSWindow* window in NSApp.windows)
{
//ignore status bar
if(YES == [window.className isEqualToString:@"NSStatusBarWindow"])
{
//skip
continue;
}
//visible?
if(YES == window.isVisible)
{
//set flag
visibleWindow = YES;
//done
break;
}
}
//any windows?
//bring app to foreground
if(YES == visibleWindow)
{
//dbg msg
os_log_debug(logHandle, "window(s) visible, setting policy: NSApplicationActivationPolicyRegular");
//foreground
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
}
//no more windows
// send app to background
else
{
//dbg msg
os_log_debug(logHandle, "window(s) not visible, setting policy: NSApplicationActivationPolicyAccessory");
//background
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
}
return;
}
//call into Update obj
// check to see if there an update?
-(void)check4Update
{
//update obj
Update* update = nil;
//init update obj
update = [[Update alloc] init];
//check for update
// ->'updateResponse newVersion:' method will be called when check is done
[update checkForUpdate:^(NSUInteger result, NSString* newVersion) {
//process response
[self updateResponse:result newVersion:newVersion];
}];
return;
}
//process update response
// error, no update, update/new version
-(void)updateResponse:(NSInteger)result newVersion:(NSString*)newVersion
{
//handle response
// new version, show popup
switch (result)
{
//error
case -1:
//err msg
os_log_error(logHandle, "ERROR: update check failed");
break;
//no updates
case 0:
//dbg msg
os_log_debug(logHandle, "no updates available");
break;
//new version
case 1:
//dbg msg
os_log_debug(logHandle, "a new version (%@) is available", newVersion);
//alloc update window
updateWindowController = [[UpdateWindowController alloc] initWithWindowNibName:@"UpdateWindow"];
//configure
[self.updateWindowController configure:[NSString stringWithFormat:@"a new version (%@) is available!", newVersion] buttonTitle:@"Update"];
//center window
[[self.updateWindowController window] center];
//show it
[self.updateWindowController showWindow:self];
//invoke function in background that will make window modal
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//make modal
makeModal(self.updateWindowController);
});
break;
}
return;
}
@end

View File

@ -1,68 +1,68 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16.png",
"scale" : "1x"
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16@2x.png",
"scale" : "2x"
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32.png",
"scale" : "1x"
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32@2x.png",
"scale" : "2x"
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128.png",
"scale" : "1x"
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128@2x.png",
"scale" : "2x"
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256.png",
"scale" : "1x"
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256@2x.png",
"scale" : "2x"
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512.png",
"scale" : "1x"
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512@2x.png",
"scale" : "2x"
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

View File

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,34 @@
{
"images" : [
{
"idiom" : "mac"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"filename" : "icon.png",
"idiom" : "mac"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "icon.png",
"idiom" : "mac"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "overSight.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,35 @@
{
"images" : [
{
"filename" : "action.png",
"idiom" : "mac"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"filename" : "action.png",
"idiom" : "mac"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "action.png",
"idiom" : "mac"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,35 @@
{
"images" : [
{
"idiom" : "mac",
"filename" : "prefsMode Any.pdf"
},
{
"idiom" : "mac",
"filename" : "prefsMode Light.pdf",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
]
},
{
"idiom" : "mac",
"filename" : "prefsMode Dark.pdf",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
]
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"preserves-vector-representation" : true
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,35 @@
{
"images" : [
{
"idiom" : "mac",
"filename" : "prefsUpdate Any.pdf"
},
{
"idiom" : "mac",
"filename" : "prefsUpdate Light.pdf",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
]
},
{
"idiom" : "mac",
"filename" : "prefsUpdate Dark.pdf",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
]
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"preserves-vector-representation" : true
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "active.png",
"idiom" : "mac"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "inactive.png",
"idiom" : "mac"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,35 @@
{
"images" : [
{
"idiom" : "mac",
"filename" : "rulesAllow Any.pdf"
},
{
"idiom" : "mac",
"filename" : "rulesAllow Light.pdf",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
]
},
{
"idiom" : "mac",
"filename" : "rulesAllow Dark.pdf",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
]
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"preserves-vector-representation" : true
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,35 @@
{
"images" : [
{
"idiom" : "mac",
"filename" : "block Any.pdf"
},
{
"idiom" : "mac",
"filename" : "block Light.pdf",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
]
},
{
"idiom" : "mac",
"filename" : "block Dark.pdf",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
]
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"preserves-vector-representation" : true
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
<connections>
<outlet property="statusMenu" destination="2P0-OI-hGc" id="9m4-cd-DGN"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="OverSight" id="1Xt-HY-uBw" userLabel="OverSight">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="OverSight" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About OverSight" id="RPu-ap-wNj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="showAbout:" target="Voe-Tx-rLC" id="oP6-qI-N7R"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="3Oo-ON-R9s"/>
<menuItem title="Preferences..." id="yfe-lu-f3I">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="showPreferences:" target="Voe-Tx-rLC" id="0UK-iW-u9N"/>
</connections>
</menuItem>
<menuItem title="Allowed Items..." id="l0V-ZX-X8S">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="showRules:" target="Voe-Tx-rLC" id="Xqt-cr-d9F"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="H15-vt-Gha"/>
<menuItem title="Close Window" keyEquivalent="w" id="rfE-3I-qPr" userLabel="Close Window">
<connections>
<action selector="closeWindow:" target="Voe-Tx-rLC" id="ZJI-gi-QEX"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
<point key="canvasLocation" x="67" y="135"/>
</menu>
<menu id="2P0-OI-hGc">
<items>
<menuItem title="OverSight: enabled" tag="100" id="fTQ-KS-1uk" userLabel="OverSight: enabled">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="WQ3-qz-mIN"/>
<menuItem title="Disable" tag="101" id="p8u-9L-hFY">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="Y5x-j1-ofU"/>
<menuItem title="Preferences..." tag="103" id="Zao-2v-WyJ">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Allowed Items..." tag="102" id="i2A-ax-9ff">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="xGL-in-U11"/>
<menuItem title="Quit OverSight" tag="104" id="FkZ-L3-H8o">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
<point key="canvasLocation" x="55" y="10"/>
</menu>
</objects>
</document>

View File

@ -0,0 +1,31 @@
//
// Client.h
// Application
//
// Created by Patrick Wardle on 4/30/21.
// Copyright © 2021 Objective-See. All rights reserved.
//
@import Foundation;
@interface Client : NSObject
/* PROPERTIES */
//pid
@property(nonatomic, retain)NSNumber* pid;
//message count
@property unsigned long long msgCount;
//client id
@property(nonatomic, retain)NSNumber* clientID;
//path
@property(nonatomic, retain)NSString* path;
//name
@property(nonatomic, retain)NSString* name;
@end

View File

@ -0,0 +1,20 @@
//
// Client.m
// Application
//
// Created by Patrick Wardle on 4/30/21.
// Copyright © 2021 Objective-See. All rights reserved.
//
#import "Client.h"
@implementation Client
//override description method
-(NSString*)description
{
//description
return [NSString stringWithFormat:@"CLIENT: pid: %@, path: %@, clientID: %@", self.pid, self.path, self.clientID];
}
@end

View File

@ -17,21 +17,21 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2.0</string>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.2.0</string>
<key>LSUIElement</key>
<true/>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2017 Objective-See. All rights reserved.</string>
<string>Copyright (c) 2021 Objective-See. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<string>NSApplicationKeyEvents</string>
<key>NSUserNotificationAlertStyle</key>
<string>alert</string>
</dict>

View File

@ -0,0 +1,79 @@
//
// logMonitor.h
// logAPI
//
// Created by Patrick Wardle on 4/30/21.
// Copyright © 2021 Objective-See. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#define LOGGING_SUPPORT @"/System/Library/PrivateFrameworks/LoggingSupport.framework"
// public OSLogEvent object
@interface OSLogEvent : NSObject
@property NSString *process;
@property NSNumber* processIdentifier;
@property NSString *processImagePath;
@property NSString *sender;
@property NSString *senderImagePath;
@property NSString *category;
@property NSString *subsystem;
@property NSDate *date;
@property NSString *composedMessage;
@end
//private log stream object
@interface OSLogEventLiveStream : NSObject
- (void)activate;
- (void)invalidate;
- (void)setFilterPredicate:(NSPredicate*)predicate;
- (void)setDroppedEventHandler:(void(^)(id))callback;
- (void)setInvalidationHandler:(void(^)(int, id))callback;
- (void)setEventHandler:(void(^)(id))callback;
@property(nonatomic) unsigned long long flags;
@end
//private log event object
//implementation in framework
@interface OSLogEventProxy : NSObject
@property(readonly, nonatomic) NSString *process;
@property(readonly, nonatomic) int processIdentifier;
@property(readonly, nonatomic) NSString *processImagePath;
@property(readonly, nonatomic) NSString *sender;
@property(readonly, nonatomic) NSString *senderImagePath;
@property(readonly, nonatomic) NSString *category;
@property(readonly, nonatomic) NSString *subsystem;
@property(readonly, nonatomic) NSDate *date;
@property(readonly, nonatomic) NSString *composedMessage;
@end
//(our) log monitor object
@interface LogMonitor : NSObject
//instance of live stream
@property(nonatomic, retain, nullable)OSLogEventLiveStream* liveStream;
/* METHODS */
//start
-(BOOL)start:(NSPredicate*)predicate callback:(void(^)(OSLogEvent*))callback;
//stop
-(void)stop;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,213 @@
//
// logMonitor.m
// logAPI
//
// Created by Patrick Wardle on 4/30/21.
// Copyright © 2021 Objective-See. All rights reserved.
//
@import OSLog;
#import "LogMonitor.h"
/* GLOBALS */
//log handle
extern os_log_t logHandle;
@implementation OSLogEvent
+ (instancetype) OSLogEvent {
return [[self alloc] init];
}
@end
//parse the private OSLogEventProxy event
// pull out items of interest, and save into our defined event
static OSLogEvent* parseEvent(OSLogEventProxy* event)
{
OSLogEvent *logEvent = [OSLogEvent OSLogEvent];
logEvent.process = event.process;
logEvent.processImagePath = event.processImagePath;
logEvent.processIdentifier = [NSNumber numberWithInt:event.processIdentifier];
logEvent.sender = event.sender;
logEvent.senderImagePath = event.senderImagePath;
logEvent.category = event.category;
logEvent.subsystem = event.subsystem;
logEvent.date = event.date;
logEvent.composedMessage = event.composedMessage;
return logEvent;
}
@implementation LogMonitor
//init
// load private framework, etc
-(id)init
{
//error
NSError* error = nil;
//super
self = [super init];
if(nil != self)
{
//ensure `LoggingSupport.framework` is loaded
if(YES != [[NSBundle bundleWithPath:LOGGING_SUPPORT] loadAndReturnError:&error])
{
//err msg
os_log_error(logHandle, "ERROR: failed to load logging framework %{public}@ (error: %{public}@)", LOGGING_SUPPORT, error);
//unset
self = nil;
//bail
goto bail;
}
}
bail:
return self;
}
//start logging
// pass in predicate to match, and callback
-(BOOL)start:(NSPredicate*)predicate callback:(void(^)(OSLogEvent*))callback
{
//flag
BOOL started = NO;
//live stream class
Class LiveStream = nil;
//event handler callback
void (^eventHandler)(OSLogEventProxy* event) = ^void(OSLogEventProxy* event) {
//Return the raw event
callback(parseEvent(event));
};
//get 'OSLogEventLiveStream' class
if(nil == (LiveStream = NSClassFromString(@"OSLogEventLiveStream")))
{
//bail
goto bail;
}
//init live stream
self.liveStream = [[LiveStream alloc] init];
if(nil == self.liveStream)
{
//bail
goto bail;
}
//sanity check
// obj responds to `setFilterPredicate:`?
if(YES != [self.liveStream respondsToSelector:NSSelectorFromString(@"setFilterPredicate:")])
{
//bail
goto bail;
}
//set predicate
[self.liveStream setFilterPredicate:predicate];
//sanity check
// obj responds to `setInvalidationHandler:`?
if(YES != [self.liveStream respondsToSelector:NSSelectorFromString(@"setInvalidationHandler:")])
{
//bail
goto bail;
}
//set invalidation handler
// note: need to have somethigng set as this get called (indirectly) when
// the 'invalidate' method is called ... but don't need to do anything
[self.liveStream setInvalidationHandler:^void (int reason, id streamPosition) {
//NSLog(@"invalidation handler called with %d!", reason);
;
}];
//sanity check
// obj responds to `setDroppedEventHandler:`?
if(YES != [self.liveStream respondsToSelector:NSSelectorFromString(@"setDroppedEventHandler:")])
{
//bail
goto bail;
}
//set dropped msg handler
// note: need to have somethigng set as this get called (indirectly)
[self.liveStream setDroppedEventHandler:^void (id droppedMessage)
{
//NSLog(@"invalidation handler called with %d!", reason);
;
}];
//sanity check
// obj responds to `setEventHandler:`?
if(YES != [self.liveStream respondsToSelector:NSSelectorFromString(@"setEventHandler:")])
{
//bail
goto bail;
}
//set event handler
[self.liveStream setEventHandler:eventHandler];
//sanity check
// obj responds to `activate:`?
if(YES != [self.liveStream respondsToSelector:NSSelectorFromString(@"activate")])
{
//bail
goto bail;
}
//
if(YES != [self.liveStream respondsToSelector:NSSelectorFromString(@"setFlags:")])
{
//bail
goto bail;
}
//set debug & info flags
[self.liveStream setFlags:0x1];
//activate
[self.liveStream activate];
//happy
started = YES;
bail:
return started;
}
//stop stream
// invalidates live stream
-(void)stop
{
//sanity check
// obj responds to `invalidate`?
if(YES != [self.liveStream respondsToSelector:NSSelectorFromString(@"invalidate")])
{
//bail
goto bail;
}
//not nil?
// invalidate
if(nil != self.liveStream)
{
//invalidate
[self.liveStream invalidate];
}
bail:
return;
}
@end

View File

@ -0,0 +1,14 @@
//
// file: NSApplicationKeyEvents.h
// project: OverSight (login item)
// description: adds support for keyboard shortcuts (header)
//
// created by Patrick Wardle
// copyright (c) 2018 Objective-See. All rights reserved.
//
@import Cocoa;
@interface NSApplicationKeyEvents : NSApplication
@end

View File

@ -0,0 +1,73 @@
//
// file: NSApplicationKeyEvents.h
// project: OverSight (login item)
// description: adds support for keyboard shortcuts (header)
//
// created by Patrick Wardle
// copyright (c) 2018 Objective-See. All rights reserved.
//
#import "NSApplicationKeyEvents.h"
@implementation NSApplicationKeyEvents
//to enable copy/paste etc even though we don't have an 'Edit' menu
// details: http://stackoverflow.com/questions/970707/cocoa-keyboard-shortcuts-in-dialog-without-an-edit-menu
-(void) sendEvent:(NSEvent *)event
{
//keydown?
if([event type] == NSEventTypeKeyDown)
{
//command?
if(([event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagCommand)
{
//+c
// copy
if(YES == [[event charactersIgnoringModifiers] isEqualToString:@"c"])
{
//copy
if([self sendAction:@selector(copy:) to:nil from:self])
{
return;
}
}
//+v
// paste
else if ([[event charactersIgnoringModifiers] isEqualToString:@"v"])
{
//paste
if([self sendAction:@selector(paste:) to:nil from:self])
{
return;
}
}
//+x
// cut
else if ([[event charactersIgnoringModifiers] isEqualToString:@"x"])
{
//cut
if([self sendAction:@selector(cut:) to:nil from:self])
{
return;
}
}
//+a
// select all
else if([[event charactersIgnoringModifiers] isEqualToString:@"a"])
{
if ([self sendAction:@selector(selectAll:) to:nil from:self])
{
return;
}
}
}
}
//super
[super sendEvent:event];
}
@end

View File

@ -0,0 +1,329 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="PrefsWindowController">
<connections>
<outlet property="actionView" destination="Jpg-nC-dWd" id="WHf-rQ-BXU"/>
<outlet property="executeArgsButton" destination="YqA-7j-gYp" id="tbC-AW-etO"/>
<outlet property="executePath" destination="z32-0s-dpb" id="HAL-Mg-ygB"/>
<outlet property="modesView" destination="K6i-xr-27e" id="DOb-L3-eeR"/>
<outlet property="toolbar" destination="V8g-Ya-LK4" id="SH2-6E-QST"/>
<outlet property="updateButton" destination="Mtn-pi-zIl" id="Oe1-Jy-nMH"/>
<outlet property="updateIndicator" destination="o0T-ra-4H0" id="DwL-OB-WQR"/>
<outlet property="updateLabel" destination="Oe2-Ye-1s6" id="1gP-K0-cpP"/>
<outlet property="updateView" destination="1OV-sl-cSe" id="Rbj-Y0-Mdg"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<rect key="contentRect" x="196" y="240" width="600" height="365"/>
<rect key="screenRect" x="0.0" y="0.0" width="3440" height="1415"/>
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="600" height="365"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Tx4-yW-fcJ">
<rect key="frame" x="138" y="2" width="325" height="65"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="OverSight" id="m7R-HO-YMp"/>
</imageView>
</subviews>
</view>
<toolbar key="toolbar" implicitIdentifier="C2AC49E3-FD8B-4919-9001-868355500E38" autosavesConfiguration="NO" displayMode="iconAndLabel" sizeMode="regular" id="V8g-Ya-LK4">
<allowedToolbarItems>
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="J66-tT-qAf"/>
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="6nW-4K-zf4"/>
<toolbarItem implicitItemIdentifier="3F78D487-BA4D-4E1B-8FC6-A844D292C69B" explicitItemIdentifier="mode" label="mode" paletteLabel="mode" image="PrefsMode" selectable="YES" id="k0w-Oi-kwd" userLabel="mode">
<size key="minSize" width="22" height="22"/>
<size key="maxSize" width="22" height="22"/>
<connections>
<action selector="toolbarButtonHandler:" target="-2" id="5Bh-RO-nSN"/>
</connections>
</toolbarItem>
<toolbarItem implicitItemIdentifier="F54FFA8D-5F3A-4A92-BCB5-F0200A133E49" explicitItemIdentifier="action" label="action" paletteLabel="action" tag="1" image="PrefsAction" selectable="YES" id="gji-hP-t04">
<size key="minSize" width="22" height="22"/>
<size key="maxSize" width="22" height="22"/>
<connections>
<action selector="toolbarButtonHandler:" target="-2" id="c6F-yA-Qd8"/>
</connections>
</toolbarItem>
<toolbarItem implicitItemIdentifier="384D69C6-2351-417E-9C93-B14039FE440F" explicitItemIdentifier="update" label="update" paletteLabel="update" tag="2" image="PrefsUpdate" selectable="YES" id="rrF-xj-cXp">
<size key="minSize" width="22" height="22"/>
<size key="maxSize" width="22" height="22"/>
<connections>
<action selector="toolbarButtonHandler:" target="-2" id="i0e-Tc-OaX"/>
</connections>
</toolbarItem>
</allowedToolbarItems>
<defaultToolbarItems>
<toolbarItem reference="k0w-Oi-kwd"/>
<toolbarItem reference="J66-tT-qAf"/>
<toolbarItem reference="gji-hP-t04"/>
<toolbarItem reference="J66-tT-qAf"/>
<toolbarItem reference="rrF-xj-cXp"/>
</defaultToolbarItems>
</toolbar>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="-823" y="488.5"/>
</window>
<customView id="K6i-xr-27e" userLabel="Modes">
<rect key="frame" x="0.0" y="0.0" width="600" height="275"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<button fixedFrame="YES" tag="1" translatesAutoresizingMaskIntoConstraints="NO" id="4sq-mb-bci">
<rect key="frame" x="39" y="223" width="29" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="RwL-kA-7QL">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="menu" size="14"/>
</buttonCell>
<connections>
<action selector="togglePreference:" target="-2" id="tpK-Y8-G90"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xbe-1S-lpy">
<rect key="frame" x="72" y="221" width="160" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Start at Login" id="JVI-Or-h0u">
<font key="font" size="13" name="Menlo-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" preferredMaxLayoutWidth="471" translatesAutoresizingMaskIntoConstraints="NO" id="tFB-E4-zbp">
<rect key="frame" x="72" y="207" width="475" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Automatically (re)start each time you login." id="E0T-Ug-P8f">
<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 fixedFrame="YES" tag="2" translatesAutoresizingMaskIntoConstraints="NO" id="Bog-hz-gcd">
<rect key="frame" x="39" y="161" width="29" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="Aaw-XT-TEt">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="menu" size="14"/>
</buttonCell>
<connections>
<action selector="togglePreference:" target="-2" id="aP4-WS-lmI"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kjh-jc-STu">
<rect key="frame" x="72" y="159" width="160" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="No Icon Mode" id="4EN-j2-enV">
<font key="font" size="13" name="Menlo-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button fixedFrame="YES" tag="3" translatesAutoresizingMaskIntoConstraints="NO" id="vQ5-vs-uTC">
<rect key="frame" x="39" y="99" width="29" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="RC0-1U-dH3">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="menu" size="14"/>
</buttonCell>
<connections>
<action selector="togglePreference:" target="-2" id="Usk-5k-Wca"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Enr-tn-mwG">
<rect key="frame" x="72" y="97" width="266" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disable 'inactive' alerts" id="a5n-tt-65v">
<font key="font" size="13" name="Menlo-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" preferredMaxLayoutWidth="471" translatesAutoresizingMaskIntoConstraints="NO" id="AAL-QY-azK">
<rect key="frame" x="72" y="83" width="522" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Do not show an alert when the camera/microphone is deactivated. " id="SYJ-Qv-cRq">
<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="DuP-BJ-gMJ">
<rect key="frame" x="212" y="13" width="176" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="View Allowed Items" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="KsW-is-XrR">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Menlo-Bold"/>
</buttonCell>
<connections>
<action selector="viewRules:" target="-2" id="jHD-eK-n4u"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" preferredMaxLayoutWidth="471" translatesAutoresizingMaskIntoConstraints="NO" id="xaO-g8-rdS">
<rect key="frame" x="72" y="146" width="475" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Run without showing an icon in the status menu bar." id="SG5-YM-BoV">
<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>
</subviews>
<point key="canvasLocation" x="-166" y="391.5"/>
</customView>
<customView id="Jpg-nC-dWd" userLabel="Action">
<rect key="frame" x="0.0" y="0.0" width="600" height="275"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<button fixedFrame="YES" tag="4" translatesAutoresizingMaskIntoConstraints="NO" id="vh6-KZ-8sT">
<rect key="frame" x="39" y="223" width="29" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="egz-lN-TtL">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system" size="14"/>
</buttonCell>
<connections>
<action selector="togglePreference:" target="-2" id="7qn-r4-nxl"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8Tb-Ih-SsC">
<rect key="frame" x="72" y="221" width="121" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Execute Action" id="ncJ-bF-dAA">
<font key="font" size="13" name="Menlo-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" preferredMaxLayoutWidth="471" translatesAutoresizingMaskIntoConstraints="NO" id="GyI-og-F35">
<rect key="frame" x="72" y="196" width="475" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Perform an action (script, binary, etc)." id="bky-MW-yBn">
<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 fixedFrame="YES" tag="5" translatesAutoresizingMaskIntoConstraints="NO" id="YqA-7j-gYp">
<rect key="frame" x="39" y="161" width="29" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="egX-c0-Flf">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system" size="14"/>
</buttonCell>
<connections>
<action selector="togglePreference:" target="-2" id="vje-WY-Fst"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dH0-Xi-Qnd">
<rect key="frame" x="72" y="159" width="114" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Pass Arguments" id="WL4-fV-4Wq">
<font key="font" size="13" name="Menlo-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" preferredMaxLayoutWidth="471" translatesAutoresizingMaskIntoConstraints="NO" id="GP0-UX-66J">
<rect key="frame" x="72" y="66" width="475" height="92"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" id="7Ah-kZ-gGe">
<font key="font" size="13" name="Menlo-Regular"/>
<string key="title">OverSight will pass in the following arguments:-d &lt;camera|microphone&gt; -event &lt;on|off&gt; -process &lt;pid&gt;Note: -process is only specified on activation events.</string>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="z32-0s-dpb">
<rect key="frame" x="198" y="218" width="354" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="path" drawsBackground="YES" id="Oe0-3Y-Srk">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<outlet property="delegate" destination="-2" id="dSg-PZ-Igl"/>
</connections>
</textField>
</subviews>
<point key="canvasLocation" x="-166" y="739"/>
</customView>
<customView id="1OV-sl-cSe" userLabel="Update">
<rect key="frame" x="0.0" y="0.0" width="618" height="275"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<button fixedFrame="YES" tag="6" translatesAutoresizingMaskIntoConstraints="NO" id="dml-JS-liI">
<rect key="frame" x="39" y="223" width="29" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="ljs-bE-JTP">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="menu" size="14"/>
</buttonCell>
<connections>
<action selector="togglePreference:" target="-2" id="YQd-OG-Lhs"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YB6-Vv-lQu">
<rect key="frame" x="72" y="221" width="177" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disable Update Checks" id="z5b-fA-WCk">
<font key="font" size="13" name="Menlo-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" preferredMaxLayoutWidth="471" translatesAutoresizingMaskIntoConstraints="NO" id="TmH-6A-xdG">
<rect key="frame" x="72" y="207" width="475" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Do not automatically check for new versions." id="JV5-cY-ff4">
<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="Mtn-pi-zIl">
<rect key="frame" x="242" y="135" width="134" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Check Now" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="2qr-2x-A3U">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Menlo-Bold"/>
</buttonCell>
<connections>
<action selector="check4Update:" target="-2" id="GJb-mB-UIz"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Oe2-Ye-1s6">
<rect key="frame" x="201" y="101" width="216" height="33"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="5gt-xh-Ti5">
<font key="font" size="13" name="Menlo-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<progressIndicator wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="o0T-ra-4H0">
<rect key="frame" x="378" y="145" width="16" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</progressIndicator>
</subviews>
<point key="canvasLocation" x="694" y="709"/>
</customView>
</objects>
<resources>
<image name="OverSight" width="1301" height="227"/>
<image name="PrefsAction" width="128" height="128"/>
<image name="PrefsMode" width="72" height="72"/>
<image name="PrefsUpdate" width="72" height="72"/>
</resources>
</document>

View File

@ -0,0 +1,76 @@
//
// file: PrefsWindowController.h
// project: OverSight (main app)
// description: preferences window controller (header)
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
@import Cocoa;
#import "UpdateWindowController.h"
/* CONSTS */
//modes view
#define TOOLBAR_MODES 0
//action view
#define TOOLBAR_ACTION 1
//update view
#define TOOLBAR_UPDATE 2
//to select, need string ID
#define TOOLBAR_MODES_ID @"mode"
@interface PrefsWindowController : NSWindowController <NSTextFieldDelegate, NSToolbarDelegate>
/* PROPERTIES */
//preferences
@property(nonatomic, retain)NSDictionary* preferences;
//toolbar
@property (weak) IBOutlet NSToolbar *toolbar;
//rules prefs view
@property (weak) IBOutlet NSView *rulesView;
//modes view
@property (strong) IBOutlet NSView *modesView;
//action view
@property (strong) IBOutlet NSView *actionView;
//path to action
@property (weak) IBOutlet NSTextField *executePath;
//execute args button
@property (weak) IBOutlet NSButton *executeArgsButton;
//update view
@property (weak) IBOutlet NSView *updateView;
//update button
@property (weak) IBOutlet NSButton *updateButton;
//update indicator (spinner)
@property (weak) IBOutlet NSProgressIndicator *updateIndicator;
//update label
@property (weak) IBOutlet NSTextField *updateLabel;
//update window controller
@property(nonatomic, retain)UpdateWindowController* updateWindowController;
/* METHODS */
//toolbar button handler
-(IBAction)toolbarButtonHandler:(id)sender;
//button handler for all preference buttons
-(IBAction)togglePreference:(id)sender;
@end

View File

@ -0,0 +1,394 @@
//
// file: PrefsWindowController.h
// project: OverSight (main app)
// description: preferences window controller (header)
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
#import "consts.h"
#import "Update.h"
#import "utilities.h"
#import "AppDelegate.h"
#import "PrefsWindowController.h"
#import "UpdateWindowController.h"
/* GLOBALS */
//log handle
extern os_log_t logHandle;
@implementation PrefsWindowController
@synthesize toolbar;
@synthesize modesView;
@synthesize rulesView;
@synthesize actionView;
@synthesize updateView;
@synthesize updateWindowController;
//start at login button
#define BUTTON_AUTOSTART_MODE 1
//'no-icon mode' button
#define BUTTON_NO_ICON_MODE 2
//'disable inactive' button
#define BUTTON_DISABLE_INACTIVE_MODE 3
//action
#define BUTTON_EXECUTE_ACTION 4
//args for action
#define BUTTON_EXECUTE_ACTION_ARGS 5
//'update mode' button
#define BUTTON_NO_UPDATE_MODE 6
//init 'general' view
// add it, and make it selected
-(void)awakeFromNib
{
//set title
self.window.title = [NSString stringWithFormat:@"%@ v%@", PRODUCT_NAME, getAppVersion()];
//set rules prefs as default
[self toolbarButtonHandler:nil];
//set rules prefs as default
[self.toolbar setSelectedItemIdentifier:TOOLBAR_MODES_ID];
return;
}
//toolbar view handler
// toggle view based on user selection
-(IBAction)toolbarButtonHandler:(id)sender
{
//view
NSView* view = nil;
//when we've prev added a view
// remove the prev view cuz adding a new one
if(nil != sender)
{
//remove
[[[self.window.contentView subviews] lastObject] removeFromSuperview];
}
//assign view
switch(((NSToolbarItem*)sender).tag)
{
//modes
case TOOLBAR_MODES:
//set view
view = self.modesView;
//start at login
((NSButton*)[view viewWithTag:BUTTON_AUTOSTART_MODE]).state = [NSUserDefaults.standardUserDefaults boolForKey:PREF_AUTOSTART_MODE];
//no icon
((NSButton*)[view viewWithTag:BUTTON_NO_ICON_MODE]).state = [NSUserDefaults.standardUserDefaults boolForKey:PREF_NO_ICON_MODE];
//disable inactive alerts
((NSButton*)[view viewWithTag:BUTTON_DISABLE_INACTIVE_MODE]).state = [NSUserDefaults.standardUserDefaults boolForKey:PREF_DISABLE_INACTIVE];
break;
//modes
case TOOLBAR_ACTION:
//set view
view = self.actionView;
//action
((NSButton*)[view viewWithTag:BUTTON_EXECUTE_ACTION]).state = [NSUserDefaults.standardUserDefaults boolForKey:PREF_EXECUTE_ACTION];
//set 'execute action' path
if(0 != [NSUserDefaults.standardUserDefaults objectForKey:PREF_EXECUTE_PATH])
{
//set
self.executePath.stringValue = [NSUserDefaults.standardUserDefaults objectForKey:PREF_EXECUTE_PATH];
}
//set state of 'execute action' to match
self.executePath.enabled = [NSUserDefaults.standardUserDefaults boolForKey:PREF_EXECUTE_ACTION];
//set action + args
((NSButton*)[view viewWithTag:BUTTON_EXECUTE_ACTION_ARGS]).state = [NSUserDefaults.standardUserDefaults boolForKey:PREF_EXECUTE_ACTION_ARGS];
//set state of 'execute action' to match
self.executeArgsButton.enabled = [NSUserDefaults.standardUserDefaults boolForKey:PREF_EXECUTE_ACTION];
break;
//update
case TOOLBAR_UPDATE:
//set view
view = self.updateView;
//set 'update' button state
((NSButton*)[view viewWithTag:BUTTON_NO_UPDATE_MODE]).state = [NSUserDefaults.standardUserDefaults boolForKey:PREF_NO_UPDATE_MODE];
break;
default:
//bail
goto bail;
}
//set frame rect
view.frame = CGRectMake(0, 75, self.window.contentView.frame.size.width, self.window.contentView.frame.size.height-75);
//add to window
[self.window.contentView addSubview:view];
bail:
return;
}
//automatically called when 'enter' is hit
// save values that were entered in text field
-(void)controlTextDidEndEditing:(NSNotification *)notification
{
//execute path?
if([notification object] != self.executePath)
{
//bail
goto bail;
}
//save & sync
[NSUserDefaults.standardUserDefaults setObject:self.executePath.stringValue forKey:PREF_EXECUTE_PATH];
[NSUserDefaults.standardUserDefaults synchronize];
bail:
return;
}
//invoked when user toggles button
// update preferences for that button
-(IBAction)togglePreference:(id)sender
{
//preferences
NSMutableDictionary* updatedPreferences = nil;
//button state
BOOL state = NO;
//init
updatedPreferences = [NSMutableDictionary dictionary];
//get button state
state = ((NSButton*)sender).state;
//set appropriate preference
switch(((NSButton*)sender).tag)
{
//autostart
case BUTTON_AUTOSTART_MODE:
{
//toggle login item
toggleLoginItem([NSURL fileURLWithPath:NSBundle.mainBundle.bundlePath], state);
//set
[NSUserDefaults.standardUserDefaults setBool:state forKey:PREF_PASSIVE_MODE];
break;
}
//no icon mode
case BUTTON_NO_ICON_MODE:
{
//toggle
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) toggleIcon:!state];
//set
[NSUserDefaults.standardUserDefaults setBool:state forKey:PREF_NO_ICON_MODE];
break;
}
//disable inactive mode
case BUTTON_DISABLE_INACTIVE_MODE:
{
//set
[NSUserDefaults.standardUserDefaults setBool:state forKey:PREF_DISABLE_INACTIVE];
break;
}
//execute action
// also toggle state of path
case BUTTON_EXECUTE_ACTION:
{
//set
[NSUserDefaults.standardUserDefaults setBool:state forKey:PREF_EXECUTE_ACTION];
//set path field state to match
self.executePath.enabled = state;
//set path field state to match
self.executeArgsButton.enabled = state;
break;
}
//execute action
// also toggle state of path
case BUTTON_EXECUTE_ACTION_ARGS:
{
//set
[NSUserDefaults.standardUserDefaults setBool:state forKey:PREF_EXECUTE_ACTION_ARGS];
break;
}
//no update mode
case BUTTON_NO_UPDATE_MODE:
{
//set
[NSUserDefaults.standardUserDefaults setBool:state forKey:PREF_NO_UPDATE_MODE];
break;
}
default:
break;
}
//sync
[NSUserDefaults.standardUserDefaults synchronize];
return;
}
//'view rules' button handler
// call helper method to show rule's window
-(IBAction)viewRules:(id)sender
{
//call into app delegate to show app rules
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) showRules:nil];
return;
}
//'check for update' button handler
-(IBAction)check4Update:(id)sender
{
//update obj
Update* update = nil;
//disable button
self.updateButton.enabled = NO;
//reset
self.updateLabel.stringValue = @"";
//show/start spinner
[self.updateIndicator startAnimation:self];
//init update obj
update = [[Update alloc] init];
//check for update
// 'updateResponse newVersion:' method will be called when check is done
[update checkForUpdate:^(NSUInteger result, NSString* newVersion) {
//process response
[self updateResponse:result newVersion:newVersion];
}];
return;
}
//process update response
// error, no update, update/new version
-(void)updateResponse:(NSInteger)result newVersion:(NSString*)newVersion
{
//re-enable button
self.updateButton.enabled = YES;
//stop/hide spinner
[self.updateIndicator stopAnimation:self];
switch(result)
{
//error
case -1:
//set label
self.updateLabel.stringValue = @"error: update check failed";
break;
//no updates
case 0:
//dbg msg
os_log_debug(logHandle, "no updates available");
//set label
self.updateLabel.stringValue = [NSString stringWithFormat:@"Installed version (%@),\r\nis the latest.", getAppVersion()];
break;
//new version
case 1:
//dbg msg
os_log_debug(logHandle, "a new version (%@) is available", newVersion);
//alloc update window
updateWindowController = [[UpdateWindowController alloc] initWithWindowNibName:@"UpdateWindow"];
//configure
[self.updateWindowController configure:[NSString stringWithFormat:@"a new version (%@) is available!", newVersion] buttonTitle:@"Update"];
//center window
[[self.updateWindowController window] center];
//show it
[self.updateWindowController showWindow:self];
//invoke function in background that will make window modal
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//make modal
makeModal(self.updateWindowController);
});
break;
}
return;
}
//on window close
// set activation policy
-(void)windowWillClose:(NSNotification *)notification
{
//wait a bit, then set activation policy
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
//on main thread
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//set activation policy
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) setActivationPolicy];
});
});
return;
}
@end

View File

@ -0,0 +1,14 @@
//
// file: RuleRow.h
// project: OverSight (main app)
// description: row for 'rules' table (header)
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
@import Cocoa;
@interface RuleRow : NSTableRowView
@end

View File

@ -1,9 +1,10 @@
//
// RuleRow.m
// OverSight
// file: RuleRow.m
// project: OverSight (main app)
// description: row for 'rules' table
//
// Created by Patrick Wardle on 4/4/15.
// Copyright (c) 2017 Objective-See. All rights reserved.
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
#import "RuleRow.h"
@ -17,7 +18,10 @@
NSRect selectionRect = {0};
//selection path
NSBezierPath *selectionPath = nil;
NSBezierPath* selectionPath = nil;
//selection color
NSColor* selectionColor = nil;
//highlight selected rows
if(self.selectionHighlightStyle != NSTableViewSelectionHighlightStyleNone)
@ -25,14 +29,25 @@
//make selection rect
selectionRect = NSInsetRect(self.bounds, 2.5, 2.5);
//init color
selectionColor = [NSColor systemGrayColor];
//on 10.14+
// set to transparent
if (@available(macOS 10.14, *)) {
//set color
selectionColor = [NSColor unemphasizedSelectedContentBackgroundColor];
}
//set stroke
[[NSColor colorWithCalibratedWhite:.65 alpha:1.0] setStroke];
[selectionColor setStroke];
//set fill
[[NSColor colorWithCalibratedWhite:.82 alpha:1.0] setFill];
[selectionColor setFill];
//create selection path
// ->with rounded corners
// with rounded corners (5x5)
selectionPath = [NSBezierPath bezierPathWithRoundedRect:selectionRect xRadius:5 yRadius:5];
//fill

View File

@ -0,0 +1,14 @@
//
// file: RuleRowCell.h
// project: OverSight (main app)
// description: cell for 'rules' table (header)
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
@import Cocoa;
@interface RuleRowCell : NSTableCellView
@end

View File

@ -0,0 +1,23 @@
//
// file: RuleRowCell.m
// project: OverSight (main app)
// description: cell for 'rules' table
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
#import "RuleRowCell.h"
@implementation RuleRowCell
//set background style
-(void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle
{
//light
[super setBackgroundStyle: NSBackgroundStyleRaised];
return;
}
@end

View File

@ -0,0 +1,249 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="RulesWindowController">
<connections>
<outlet property="addRulePanel" destination="cna-Y1-PEc" id="a7H-3I-8qq"/>
<outlet property="overlay" destination="hpe-dB-Ek8" id="vIp-9F-c33"/>
<outlet property="tableView" destination="rpa-sZ-jQp" id="SvF-Yi-OKB"/>
<outlet property="view" destination="se5-gp-TjO" id="wqw-Gz-fhe"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
<rect key="contentRect" x="913" y="419" width="1108" height="581"/>
<rect key="screenRect" x="0.0" y="0.0" width="3440" height="1415"/>
<value key="minSize" type="size" width="1104" height="552"/>
<view key="contentView" wantsLayer="YES" misplaced="YES" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="1108" height="581"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="qQ9-BU-evn">
<rect key="frame" x="488" y="0.0" width="137" height="42"/>
<constraints>
<constraint firstAttribute="width" constant="137" id="xbv-ZD-7Vu"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="OverSight" id="Foc-ee-gdz"/>
</imageView>
<scrollView misplaced="YES" autohidesScrollers="YES" horizontalLineScroll="56" horizontalPageScroll="10" verticalLineScroll="56" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gth-To-Lf2">
<rect key="frame" x="-1" y="71" width="1110" height="511"/>
<clipView key="contentView" id="ubO-Ur-NA2">
<rect key="frame" x="1" y="1" width="1108" height="509"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="firstColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="54" rowSizeStyle="automatic" headerView="MjO-gV-r1W" viewBased="YES" id="rpa-sZ-jQp">
<rect key="frame" x="0.0" y="0.0" width="1112" height="486"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn editable="NO" width="727" minWidth="40" maxWidth="1500" id="ocl-vV-SZu">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title=" Process">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="pc6-VQ-D0H">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="processCell" id="moN-VP-hzI" userLabel="Process Row Cell" customClass="RuleRowCell">
<rect key="frame" x="1" y="1" width="732" height="54"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView translatesAutoresizingMaskIntoConstraints="NO" id="JiF-Nx-dl5">
<rect key="frame" x="3" y="7" width="40" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="Ek4-aX-zze"/>
<constraint firstAttribute="width" constant="40" id="LIx-0S-38a"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSColorPanel" id="v3A-16-OyI"/>
</imageView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" tag="100" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JMN-i9-vxR">
<rect key="frame" x="54" y="23" width="68" height="20"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Process" id="2GD-5k-sEf">
<font key="font" size="15" name="Menlo-Bold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" tag="101" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="w9P-yZ-pho">
<rect key="frame" x="54" y="6" width="677" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" selectable="YES" editable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="file:" id="EZN-NC-GXx">
<font key="font" metaFont="fixedUser" size="11"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="w9P-yZ-pho" secondAttribute="bottom" constant="6" id="8ET-Mt-gF3"/>
<constraint firstItem="JMN-i9-vxR" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="11" id="O8w-Nb-paE"/>
<constraint firstAttribute="bottom" secondItem="JiF-Nx-dl5" secondAttribute="bottom" constant="7" id="SIi-FU-8f2"/>
<constraint firstItem="w9P-yZ-pho" firstAttribute="leading" secondItem="JiF-Nx-dl5" secondAttribute="trailing" constant="13" id="fQn-oM-uvI"/>
<constraint firstAttribute="trailing" secondItem="w9P-yZ-pho" secondAttribute="trailing" constant="3" id="fjC-8I-WAo"/>
<constraint firstItem="JiF-Nx-dl5" firstAttribute="leading" secondItem="moN-VP-hzI" secondAttribute="leading" constant="3" id="lYV-gy-fqI"/>
<constraint firstItem="w9P-yZ-pho" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="31" id="moi-p4-d9X"/>
<constraint firstItem="JMN-i9-vxR" firstAttribute="leading" secondItem="JiF-Nx-dl5" secondAttribute="trailing" constant="13" id="p3K-7u-mdM"/>
<constraint firstAttribute="bottom" secondItem="JMN-i9-vxR" secondAttribute="bottom" constant="23" id="sYy-ZF-GpO"/>
<constraint firstItem="JiF-Nx-dl5" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="7" id="wcd-hM-fV6"/>
</constraints>
<connections>
<outlet property="imageView" destination="JiF-Nx-dl5" id="pVf-M3-mAH"/>
<outlet property="textField" destination="JMN-i9-vxR" id="sds-eR-7bO"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn editable="NO" width="370" minWidth="100" maxWidth="10000" id="QUv-cU-tQS">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Allowed Device">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="NSU-hn-EU3">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
<prototypeCellViews>
<tableCellView identifier="ruleCell" id="cqk-B9-OZf" customClass="RuleRowCell">
<rect key="frame" x="736" y="1" width="374" height="54"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button verticalHuggingPriority="750" tag="110" translatesAutoresizingMaskIntoConstraints="NO" id="ygq-ha-4ac">
<rect key="frame" x="351" y="16" width="20" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="9A5-FS-nJF"/>
<constraint firstAttribute="height" constant="15" id="zE2-xM-T10"/>
</constraints>
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSStopProgressFreestandingTemplate" imagePosition="overlaps" alignment="center" imageScaling="proportionallyDown" inset="2" id="4nO-hs-GGE">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="deleteRule:" target="-2" id="AzS-bQ-k7z"/>
</connections>
</button>
<textField horizontalHuggingPriority="249" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" tag="100" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WZT-5Q-dUx">
<rect key="frame" x="30" y="16" width="291" height="20"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="action" id="UoV-OW-a04">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView translatesAutoresizingMaskIntoConstraints="NO" id="698-T7-ArC">
<rect key="frame" x="3" y="15" width="24" height="23"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="r1c-E9-LZf"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="allow" id="vhh-iZ-06M"/>
</imageView>
</subviews>
<constraints>
<constraint firstItem="698-T7-ArC" firstAttribute="leading" secondItem="cqk-B9-OZf" secondAttribute="leading" constant="3" id="0gJ-IW-oQK"/>
<constraint firstAttribute="trailing" secondItem="ygq-ha-4ac" secondAttribute="trailing" constant="3" id="2cO-eu-LXZ"/>
<constraint firstAttribute="bottom" secondItem="WZT-5Q-dUx" secondAttribute="bottom" constant="16" id="B2m-Zm-j8M"/>
<constraint firstItem="698-T7-ArC" firstAttribute="centerY" secondItem="ygq-ha-4ac" secondAttribute="centerY" id="JIM-mI-oSy"/>
<constraint firstItem="698-T7-ArC" firstAttribute="top" secondItem="cqk-B9-OZf" secondAttribute="top" constant="16" id="NTN-Rh-etv"/>
<constraint firstItem="WZT-5Q-dUx" firstAttribute="firstBaseline" secondItem="ygq-ha-4ac" secondAttribute="baseline" constant="-5" id="boS-d9-zMz"/>
<constraint firstItem="ygq-ha-4ac" firstAttribute="top" secondItem="cqk-B9-OZf" secondAttribute="top" constant="20" id="hEy-oA-byu"/>
<constraint firstItem="WZT-5Q-dUx" firstAttribute="leading" secondItem="698-T7-ArC" secondAttribute="trailing" constant="5" id="uI9-kc-xj2"/>
<constraint firstAttribute="bottom" secondItem="ygq-ha-4ac" secondAttribute="bottom" constant="19" id="uqq-xE-bb8"/>
<constraint firstItem="ygq-ha-4ac" firstAttribute="leading" secondItem="WZT-5Q-dUx" secondAttribute="trailing" constant="32" id="vFM-xr-YmN"/>
</constraints>
<connections>
<outlet property="imageView" destination="698-T7-ArC" id="svd-HO-LXj"/>
<outlet property="textField" destination="WZT-5Q-dUx" id="aWB-Y4-fn6"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="-2" id="cSg-At-Ff8"/>
<outlet property="delegate" destination="-2" id="gZb-ih-AfV"/>
</connections>
</tableView>
</subviews>
</clipView>
<scroller key="horizontalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="VKw-SH-33M">
<rect key="frame" x="1" y="494" width="1108" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="ecy-l3-ARc">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" wantsLayer="YES" id="MjO-gV-r1W">
<rect key="frame" x="0.0" y="0.0" width="1112" height="23"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<customView hidden="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hpe-dB-Ek8" userLabel="Overlay">
<rect key="frame" x="306" y="165" width="500" height="200"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="m0M-KX-7D6" userLabel="loading data">
<rect key="frame" x="18" y="87" width="464" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="Currently, there are no allowed items..." id="Wjb-RO-P6A">
<font key="font" size="13" name="Menlo-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="500" id="7BV-bx-6En"/>
<constraint firstAttribute="height" constant="200" id="zSL-Rb-x0k"/>
</constraints>
</customView>
</subviews>
<constraints>
<constraint firstItem="qQ9-BU-evn" firstAttribute="centerX" secondItem="gth-To-Lf2" secondAttribute="centerX" id="2W7-N3-WMW"/>
<constraint firstItem="gth-To-Lf2" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="-1" id="44L-X5-eF7"/>
<constraint firstItem="gth-To-Lf2" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="-1" id="5t7-SX-3Rs"/>
<constraint firstItem="qQ9-BU-evn" firstAttribute="top" secondItem="gth-To-Lf2" secondAttribute="bottom" id="DWM-Ik-pz9"/>
<constraint firstAttribute="bottom" secondItem="gth-To-Lf2" secondAttribute="bottom" constant="42" id="SbQ-dG-FEs"/>
<constraint firstAttribute="trailing" secondItem="gth-To-Lf2" secondAttribute="trailing" constant="-1" id="WvL-Bq-dxN"/>
<constraint firstItem="hpe-dB-Ek8" firstAttribute="centerY" secondItem="se5-gp-TjO" secondAttribute="centerY" id="jWl-Z5-Mfh"/>
<constraint firstItem="hpe-dB-Ek8" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="rCn-Jj-WSh"/>
<constraint firstAttribute="bottom" secondItem="qQ9-BU-evn" secondAttribute="bottom" id="zPc-WN-oyQ"/>
</constraints>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="62" y="442.5"/>
</window>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="pch-yV-NXy" userLabel="Add Rule" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="272" y="172" width="276" height="378"/>
<rect key="screenRect" x="0.0" y="0.0" width="3440" height="1415"/>
<view key="contentView" id="cna-Y1-PEc">
<rect key="frame" x="0.0" y="0.0" width="276" height="378"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<point key="canvasLocation" x="-388" y="1048"/>
</window>
</objects>
<resources>
<image name="NSColorPanel" width="32" height="32"/>
<image name="NSStopProgressFreestandingTemplate" width="15" height="15"/>
<image name="OverSight" width="1301" height="227"/>
<image name="allow" width="72" height="72"/>
</resources>
</document>

View File

@ -0,0 +1,76 @@
//
// file: RulesWindowController.h
// project: OverSight (main app)
// description: window controller for 'rules' table (header)
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
@import Cocoa;
/* CONSTS */
//id (tag) for detailed text in category table
#define TABLE_ROW_NAME_TAG 100
//id (tag) for detailed text (file)
#define TABLE_ROW_SUB_TEXT_FILE 101
//id (tag) for detailed text (item)
#define TABLE_ROW_SUB_TEXT_ITEM 102
//id (tag) for delete button
#define TABLE_ROW_DELETE_TAG 110
//menu item for block
#define MENU_ITEM_BLOCK 0
//menu item for allow
#define MENU_ITEM_ALLOW 1
//menu item for delete
#define MENU_ITEM_DELETE 2
/* INTERFACE */
@interface RulesWindowController : NSWindowController <NSWindowDelegate, NSTableViewDataSource, NSTableViewDelegate, NSMenuDelegate>
{
}
/* PROPERTIES */
//overlay
@property (weak) IBOutlet NSView *overlay;
//observer for rules changed
@property(nonatomic, retain)id rulesObserver;
//table items
// all of the rules
@property(nonatomic, retain)NSMutableArray* rules;
//top level view
@property (weak) IBOutlet NSView *view;
//table view
@property (weak) IBOutlet NSTableView *tableView;
//panel for 'add rule'
@property (weak) IBOutlet NSView *addRulePanel;
/* METHODS */
//configure (UI)
-(void)configure;
//get rules from daemon
// then, re-load rules table
-(void)loadRules;
//delete a rule
-(IBAction)deleteRule:(id)sender;
@end

View File

@ -0,0 +1,276 @@
//
// file: RulesWindowController.m
// project: OverSight (main app)
// description: window controller for 'rules' table
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
#import "consts.h"
#import "RuleRow.h"
#import "utilities.h"
#import "AppDelegate.h"
#import "RulesWindowController.h"
/* GLOBALS */
//log handle
extern os_log_t logHandle;
@implementation RulesWindowController
@synthesize rules;
//alloc/init
// get rules and listen for new ones
-(void)windowDidLoad
{
//setup observer for new rules
self.rulesObserver = [[NSNotificationCenter defaultCenter] addObserverForName:RULES_CHANGED object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification)
{
//get new rules
[self loadRules];
}];
return;
}
//configure (UI)
-(void)configure
{
//load rules
[self loadRules];
//center window
[self.window center];
//show window
[self showWindow:self];
//make it key window
[self.window makeKeyAndOrderFront:self];
return;
}
//get rules from daemon
// then, re-load rules table
-(void)loadRules
{
//dbg msg
os_log_debug(logHandle, "loading rules...");
//in background get rules
// ...then load rule table table
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
//get rules
self.rules = [[NSUserDefaults.standardUserDefaults objectForKey:PREFS_ALLOWED_ITEMS] mutableCopy];
//dbg msg
os_log_debug(logHandle, "loaded %lu allowed items", (unsigned long)self.rules.count);
//sort
// case insenstive on name
self.rules = [[self.rules sortedArrayUsingComparator:^NSComparisonResult(NSDictionary* a, NSDictionary* b)
{
return [getProcessName(a[EVENT_PROCESS_PATH]) caseInsensitiveCompare: getProcessName(b[EVENT_PROCESS_PATH])];
}] mutableCopy];
//show rules in UI
// ...gotta do this on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
//reload table
[self.tableView reloadData];
//select first row
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
//set overlay vibility
self.overlay.hidden = !(0 == self.rules.count);
});
});
return;
}
//delete an allowed item
-(IBAction)deleteRule:(id)sender
{
//index of row
// either clicked or selected row
NSInteger row = 0;
//allowed items
NSMutableArray* allowedItems = nil;
//item to delete
NSDictionary* item = nil;
//dbg msg
os_log_debug(logHandle, "deleting allowed item");
//get selected row
row = [self.tableView rowForView:sender];
if(-1 == row) goto bail;
//get item
item = self.rules[row];
//dbg msg
os_log_debug(logHandle, "allowed item: %{public}@ (device: %@)", item[EVENT_PROCESS_PATH], item[EVENT_DEVICE]);
//(re)load items
allowedItems = [[NSUserDefaults.standardUserDefaults objectForKey:PREFS_ALLOWED_ITEMS] mutableCopy];
//find/remove item
for (NSInteger i = allowedItems.count - 1; i >= 0; i--)
{
if( (item[EVENT_DEVICE] != allowedItems[i][EVENT_DEVICE]) ||
(item[EVENT_PROCESS_PATH] != allowedItems[i][EVENT_PROCESS_PATH]) )
{
//no match
continue;
}
//remove
[allowedItems removeObjectAtIndex:i];
}
//save & sync
[NSUserDefaults.standardUserDefaults setObject:allowedItems forKey:PREFS_ALLOWED_ITEMS];
[NSUserDefaults.standardUserDefaults synchronize];
//reload rules
[self loadRules];
bail:
return;
}
#pragma mark -
#pragma mark table delegate methods
//number of rows
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
//row's count
return self.rules.count;
}
//cell for table column
-(NSView*)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
//cell
NSTableCellView *tableCell = nil;
//item
NSDictionary* allowedItem = nil;
//process path
NSString* processPath = nil;
//grab item
allowedItem = self.rules[row];
//column: 'process'
// set process icon, name and path
if(tableColumn == tableView.tableColumns[0])
{
//init table cell
tableCell = [tableView makeViewWithIdentifier:@"processCell" owner:self];
if(nil == tableCell)
{
//bail
goto bail;
}
//extract path
processPath = allowedItem[EVENT_PROCESS_PATH];
//set icon
tableCell.imageView.image = getIconForProcess(processPath);
//set process name
tableCell.textField.stringValue = getProcessName(processPath);
//set sub text (path)
((NSTextField*)[tableCell viewWithTag:TABLE_ROW_SUB_TEXT_FILE]).stringValue = processPath;
//set text color to gray
((NSTextField*)[tableCell viewWithTag:TABLE_ROW_SUB_TEXT_FILE]).textColor = [NSColor secondaryLabelColor];
}
//column: 'rule'
// set icon and rule action
else
{
//init table cell
tableCell = [tableView makeViewWithIdentifier:@"ruleCell" owner:self];
if(nil == tableCell)
{
//bail
goto bail;
}
tableCell.textField.stringValue = (Device_Camera == [allowedItem[EVENT_DEVICE] intValue]) ? @"camera" : @"microphone";
}
bail:
return tableCell;
}
//row for view
-(NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
{
//row view
RuleRow* rowView = nil;
//row ID
static NSString* const kRowIdentifier = @"RowView";
//try grab existing row view
rowView = [tableView makeViewWithIdentifier:kRowIdentifier owner:self];
//make new if needed
if(nil == rowView)
{
//create new
// ->size doesn't matter
rowView = [[RuleRow alloc] initWithFrame:NSZeroRect];
//set row ID
rowView.identifier = kRowIdentifier;
}
return rowView;
}
//on window close
// set activation policy
-(void)windowWillClose:(NSNotification *)notification
{
//wait a bit, then set activation policy
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
//on main thread
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//set activation policy
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) setActivationPolicy];
});
});
return;
}
@end

View File

@ -1,15 +1,15 @@
<?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" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="RulesWindowController">
<connections>
<outlet property="message" destination="UMM-Qt-ZkS" id="cfb-Qd-7Jn"/>
<outlet property="overlay" destination="QQF-7i-jAB" id="IN5-0M-F2x"/>
<outlet property="spinner" destination="OSA-Md-quD" id="hbq-E1-1pa"/>
<outlet property="refreshing" destination="cbu-EU-t72" id="oUy-SL-skJ"/>
<outlet property="refreshingIndicator" destination="hmR-9d-tQz" id="6Fd-Jh-0rj"/>
<outlet property="tableView" destination="rpa-sZ-jQp" id="SvF-Yi-OKB"/>
<outlet property="view" destination="se5-gp-TjO" id="wqw-Gz-fhe"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
@ -17,33 +17,71 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="White Listed Applications" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<window title="Allowed Items" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="611" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1418"/>
<value key="minSize" type="size" width="500" height="100"/>
<value key="maxSize" type="size" width="1500" height="500"/>
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="611" height="270"/>
<autoresizingMask key="autoresizingMask"/>
<rect key="contentRect" x="913" y="419" width="1000" height="550"/>
<rect key="screenRect" x="0.0" y="0.0" width="3440" height="1415"/>
<value key="minSize" type="size" width="1000" height="550"/>
<view key="contentView" wantsLayer="YES" misplaced="YES" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="1000" height="504"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="qQ9-BU-evn">
<rect key="frame" x="432" y="0.0" width="137" height="42"/>
<constraints>
<constraint firstAttribute="height" constant="42" id="OQi-ZU-78C"/>
<constraint firstAttribute="width" constant="137" id="RUo-4e-7QE"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="OverSight" id="Foc-ee-gdz"/>
</imageView>
<button toolTip="Refresh Rules" tag="10001" translatesAutoresizingMaskIntoConstraints="NO" id="dch-dH-0L1">
<rect key="frame" x="8" y="5" width="32" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="cIQ-2H-Pwm"/>
<constraint firstAttribute="width" constant="32" id="nrI-KT-pSx"/>
</constraints>
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="refresh" imagePosition="overlaps" alignment="center" imageScaling="proportionallyDown" inset="2" id="USG-J4-Dcw">
<behavior key="behavior" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="refresh:" target="-2" id="b3e-RO-mVQ"/>
</connections>
</button>
<progressIndicator wantsLayer="YES" maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="hmR-9d-tQz">
<rect key="frame" x="48" y="13" width="16" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="12Z-j4-ITO"/>
<constraint firstAttribute="width" constant="16" id="dPJ-qG-JB4"/>
</constraints>
</progressIndicator>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="cbu-EU-t72">
<rect key="frame" x="71" y="14" width="227" height="15"/>
<constraints>
<constraint firstAttribute="height" constant="15" id="HC9-Z6-SbC"/>
<constraint firstAttribute="width" constant="223" id="UFF-mV-lAy"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="refreshing allowed items..." id="QYb-zx-KOl">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<scrollView autohidesScrollers="YES" horizontalLineScroll="56" horizontalPageScroll="10" verticalLineScroll="56" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gth-To-Lf2">
<rect key="frame" x="-1" y="-1" width="613" height="272"/>
<rect key="frame" x="-1" y="50" width="1002" height="409"/>
<clipView key="contentView" id="ubO-Ur-NA2">
<rect key="frame" x="1" y="0.0" width="611" height="271"/>
<rect key="frame" x="1" y="1" width="1000" height="407"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="54" rowSizeStyle="automatic" headerView="MjO-gV-r1W" viewBased="YES" id="rpa-sZ-jQp">
<rect key="frame" x="0.0" y="0.0" width="611" height="248"/>
<autoresizingMask key="autoresizingMask"/>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="54" rowSizeStyle="automatic" headerView="MjO-gV-r1W" viewBased="YES" id="rpa-sZ-jQp">
<rect key="frame" x="0.0" y="0.0" width="1000" height="384"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="608" minWidth="40" maxWidth="1500" id="ocl-vV-SZu">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<tableColumn editable="NO" width="988" minWidth="550" maxWidth="10000" id="ocl-vV-SZu">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title=" Process">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
@ -52,66 +90,70 @@
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
<prototypeCellViews>
<tableCellView identifier="itemCell" id="moN-VP-hzI" customClass="RuleRowCell">
<rect key="frame" x="1" y="1" width="608" height="54"/>
<tableCellView identifier="processCell" id="moN-VP-hzI" userLabel="Process Row Cell" customClass="RuleRowCell">
<rect key="frame" x="1" y="1" width="997" height="54"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView translatesAutoresizingMaskIntoConstraints="NO" id="JiF-Nx-dl5">
<rect key="frame" x="3" y="7" width="40" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="Ek4-aX-zze"/>
<constraint firstAttribute="width" constant="40" id="LIx-0S-38a"/>
<constraint firstAttribute="width" constant="40" id="PZT-gB-xLQ"/>
<constraint firstAttribute="height" constant="40" id="xmL-At-zsg"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSColorPanel" id="v3A-16-OyI"/>
</imageView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" tag="100" translatesAutoresizingMaskIntoConstraints="NO" id="JMN-i9-vxR">
<rect key="frame" x="54" y="21" width="525" height="26"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Item Category" id="2GD-5k-sEf">
<font key="font" size="17" name="Menlo-Bold"/>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" tag="100" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JMN-i9-vxR">
<rect key="frame" x="54" y="23" width="914" height="18"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Process" id="2GD-5k-sEf">
<font key="font" size="15" name="Menlo-Bold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" tag="101" translatesAutoresizingMaskIntoConstraints="NO" id="w9P-yZ-pho">
<rect key="frame" x="54" y="8" width="525" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Item Category Description" id="EZN-NC-GXx">
<font key="font" size="11" name="Menlo-Regular"/>
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" tag="101" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="w9P-yZ-pho">
<rect key="frame" x="54" y="3" width="914" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="910" id="Bwh-ZW-Pzh"/>
<constraint firstAttribute="height" constant="17" id="OHU-le-p6c"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" selectable="YES" editable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="file:" id="EZN-NC-GXx">
<font key="font" metaFont="fixedUser" size="11"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aWW-9r-3lC">
<rect key="frame" x="585" y="19" width="20" height="15"/>
<button verticalHuggingPriority="750" tag="110" translatesAutoresizingMaskIntoConstraints="NO" id="ygq-ha-4ac">
<rect key="frame" x="974" y="16" width="20" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="6gy-ZI-MZN"/>
<constraint firstAttribute="height" constant="15" id="iy3-ST-MU7"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="15" id="6sn-5E-oqD"/>
<constraint firstAttribute="width" constant="20" id="9A5-FS-nJF"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="20" id="e4w-Ap-iC3"/>
<constraint firstAttribute="height" constant="15" id="zE2-xM-T10"/>
</constraints>
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSStopProgressFreestandingTemplate" imagePosition="overlaps" alignment="center" imageScaling="proportionallyDown" inset="2" id="wdw-rZ-IR3">
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSStopProgressFreestandingTemplate" imagePosition="overlaps" alignment="center" imageScaling="proportionallyDown" inset="2" id="4nO-hs-GGE">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="deleteRule:" target="-2" id="vM0-zk-yrN"/>
<action selector="deleteRule:" target="-2" id="AzS-bQ-k7z"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="w9P-yZ-pho" secondAttribute="bottom" constant="8" id="8ET-Mt-gF3"/>
<constraint firstAttribute="bottom" secondItem="aWW-9r-3lC" secondAttribute="bottom" constant="19" id="8wP-dc-ILG"/>
<constraint firstAttribute="trailing" secondItem="aWW-9r-3lC" secondAttribute="trailing" constant="3" id="Lhf-xZ-vWk"/>
<constraint firstItem="JMN-i9-vxR" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="7" id="O8w-Nb-paE"/>
<constraint firstItem="aWW-9r-3lC" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="20" id="QgG-WW-zbV"/>
<constraint firstAttribute="bottom" secondItem="JiF-Nx-dl5" secondAttribute="bottom" constant="7" id="SIi-FU-8f2"/>
<constraint firstItem="aWW-9r-3lC" firstAttribute="leading" secondItem="w9P-yZ-pho" secondAttribute="trailing" constant="8" id="dQs-ek-ECt"/>
<constraint firstItem="w9P-yZ-pho" firstAttribute="leading" secondItem="JiF-Nx-dl5" secondAttribute="trailing" constant="13" id="fQn-oM-uvI"/>
<constraint firstItem="aWW-9r-3lC" firstAttribute="leading" secondItem="JMN-i9-vxR" secondAttribute="trailing" constant="8" id="kgo-T3-47B"/>
<constraint firstItem="JiF-Nx-dl5" firstAttribute="leading" secondItem="moN-VP-hzI" secondAttribute="leading" constant="3" id="lYV-gy-fqI"/>
<constraint firstItem="w9P-yZ-pho" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="29" id="moi-p4-d9X"/>
<constraint firstItem="JMN-i9-vxR" firstAttribute="leading" secondItem="JiF-Nx-dl5" secondAttribute="trailing" constant="13" id="p3K-7u-mdM"/>
<constraint firstAttribute="bottom" secondItem="JMN-i9-vxR" secondAttribute="bottom" constant="21" id="sYy-ZF-GpO"/>
<constraint firstItem="JiF-Nx-dl5" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="7" id="wcd-hM-fV6"/>
<constraint firstItem="JMN-i9-vxR" firstAttribute="leading" secondItem="JiF-Nx-dl5" secondAttribute="trailing" constant="13" id="131-X6-JpR"/>
<constraint firstAttribute="bottom" secondItem="ygq-ha-4ac" secondAttribute="bottom" constant="19" id="3iA-Wu-jHB"/>
<constraint firstAttribute="bottom" secondItem="w9P-yZ-pho" secondAttribute="bottom" constant="3" id="6eg-oo-jo5"/>
<constraint firstItem="JiF-Nx-dl5" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="7" id="8gu-cU-wzB"/>
<constraint firstItem="w9P-yZ-pho" firstAttribute="leading" secondItem="JiF-Nx-dl5" secondAttribute="trailing" constant="13" id="HyK-e2-Axe"/>
<constraint firstItem="JMN-i9-vxR" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="13" id="U2t-P4-WTg"/>
<constraint firstItem="JiF-Nx-dl5" firstAttribute="leading" secondItem="moN-VP-hzI" secondAttribute="leading" constant="3" id="YId-FS-vwt"/>
<constraint firstAttribute="trailing" secondItem="w9P-yZ-pho" secondAttribute="trailing" constant="31" id="bYN-vN-bNA"/>
<constraint firstItem="ygq-ha-4ac" firstAttribute="top" secondItem="moN-VP-hzI" secondAttribute="top" constant="20" symbolic="YES" id="f7R-d2-thF"/>
<constraint firstAttribute="trailing" secondItem="JMN-i9-vxR" secondAttribute="trailing" constant="31" id="lzs-QR-tcd"/>
<constraint firstAttribute="bottom" secondItem="JiF-Nx-dl5" secondAttribute="bottom" constant="7" id="pFa-GM-LAb"/>
<constraint firstAttribute="trailing" secondItem="ygq-ha-4ac" secondAttribute="trailing" constant="3" id="qoi-gP-cgx"/>
</constraints>
<connections>
<outlet property="imageView" destination="JiF-Nx-dl5" id="pVf-M3-mAH"/>
@ -128,73 +170,45 @@
</tableView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="VKw-SH-33M">
<rect key="frame" x="1" y="255" width="480" height="16"/>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="VKw-SH-33M">
<rect key="frame" x="1" y="484" width="1000" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="ecy-l3-ARc">
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="ecy-l3-ARc">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" id="MjO-gV-r1W">
<rect key="frame" x="0.0" y="0.0" width="611" height="23"/>
<tableHeaderView key="headerView" wantsLayer="YES" id="MjO-gV-r1W">
<rect key="frame" x="0.0" y="0.0" width="1000" height="23"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
</subviews>
<constraints>
<constraint firstItem="gth-To-Lf2" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="-1" id="0Jc-HW-sRY"/>
<constraint firstAttribute="bottom" secondItem="gth-To-Lf2" secondAttribute="bottom" constant="-1" id="1XM-Vv-cJW"/>
<constraint firstAttribute="trailing" secondItem="gth-To-Lf2" secondAttribute="trailing" constant="-1" id="GjA-Ta-LSr"/>
<constraint firstItem="gth-To-Lf2" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="-1" id="ehW-gX-Et6"/>
<constraint firstAttribute="bottom" secondItem="gth-To-Lf2" secondAttribute="bottom" constant="-1" id="gFc-Mm-n7w"/>
<constraint firstAttribute="trailing" secondItem="gth-To-Lf2" secondAttribute="trailing" constant="-1" id="ga8-g4-rt6"/>
<constraint firstItem="gth-To-Lf2" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="-1" id="gtf-7O-hid"/>
<constraint firstItem="gth-To-Lf2" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="-1" id="jrL-0c-zSr"/>
<constraint firstItem="dch-dH-0L1" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="8" id="1xM-TH-TB3"/>
<constraint firstItem="qQ9-BU-evn" firstAttribute="top" secondItem="gth-To-Lf2" secondAttribute="bottom" constant="8" symbolic="YES" id="2cD-C1-rKM"/>
<constraint firstItem="gth-To-Lf2" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="-1" id="7dB-jy-7vM"/>
<constraint firstAttribute="bottom" secondItem="hmR-9d-tQz" secondAttribute="bottom" constant="13" id="AFJ-W6-5tf"/>
<constraint firstAttribute="trailing" secondItem="gth-To-Lf2" secondAttribute="trailing" constant="-1" id="Fgi-WJ-IP8"/>
<constraint firstAttribute="bottom" secondItem="cbu-EU-t72" secondAttribute="bottom" constant="14" id="LRd-CH-FAn"/>
<constraint firstAttribute="bottom" secondItem="qQ9-BU-evn" secondAttribute="bottom" id="PVq-Uh-I1N"/>
<constraint firstAttribute="bottom" secondItem="dch-dH-0L1" secondAttribute="bottom" constant="5" id="WR0-wd-6Ba"/>
<constraint firstItem="gth-To-Lf2" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="-1" id="ZJL-er-G6Y"/>
<constraint firstItem="cbu-EU-t72" firstAttribute="leading" secondItem="hmR-9d-tQz" secondAttribute="trailing" constant="9" id="lsj-qe-LhS"/>
<constraint firstItem="qQ9-BU-evn" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="vBz-uq-KiQ"/>
<constraint firstItem="hmR-9d-tQz" firstAttribute="leading" secondItem="dch-dH-0L1" secondAttribute="trailing" constant="8" id="yaI-rB-AiI"/>
</constraints>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="205.5" y="162"/>
<point key="canvasLocation" x="304" y="487"/>
</window>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" id="8rY-Kl-bJs">
<rect key="frame" x="0.0" y="0.0" width="38" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="AAC-Kf-Jrf">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<point key="canvasLocation" x="145" y="385.5"/>
</textField>
<customView id="QQF-7i-jAB" userLabel="Overlay">
<rect key="frame" x="0.0" y="0.0" width="538" height="306"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="UMM-Qt-ZkS">
<rect key="frame" x="209" y="140" width="121" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="deleting..." id="wtv-3g-ec4">
<font key="font" size="17" name="Menlo-Bold"/>
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<progressIndicator wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" bezeled="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="OSA-Md-quD">
<rect key="frame" x="253" y="173" width="32" height="32"/>
</progressIndicator>
</subviews>
<constraints>
<constraint firstItem="UMM-Qt-ZkS" firstAttribute="centerX" secondItem="QQF-7i-jAB" secondAttribute="centerX" id="MfM-Fj-7rY"/>
<constraint firstItem="OSA-Md-quD" firstAttribute="centerX" secondItem="UMM-Qt-ZkS" secondAttribute="centerX" id="dyY-6q-geR"/>
<constraint firstItem="UMM-Qt-ZkS" firstAttribute="top" secondItem="OSA-Md-quD" secondAttribute="bottom" constant="8" id="oY1-X0-hpZ"/>
<constraint firstItem="UMM-Qt-ZkS" firstAttribute="centerY" secondItem="QQF-7i-jAB" secondAttribute="centerY" id="ydZ-am-uZs"/>
</constraints>
<point key="canvasLocation" x="908" y="151"/>
</customView>
</objects>
<resources>
<image name="NSColorPanel" width="32" height="32"/>
<image name="NSStopProgressFreestandingTemplate" width="14" height="14"/>
<image name="NSStopProgressFreestandingTemplate" width="15" height="15"/>
<image name="OverSight" width="1301" height="227"/>
<image name="refresh" width="512" height="512"/>
</resources>
</document>

View File

@ -0,0 +1,35 @@
//
// file: StatusBarMenu.h
// project: OverSight (login item)
// description: menu handler for status bar icon (header)
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
@import Cocoa;
@interface StatusBarItem : NSObject <NSPopoverDelegate>
{
}
//status item
@property(nonatomic, strong, readwrite)NSStatusItem *statusItem;
//popover
@property(retain, nonatomic)NSPopover *popover;
//disabled flag
@property BOOL isDisabled;
/* METHODS */
//init
-(id)init:(NSMenu*)menu;
//remove status item
-(void)removeStatusItem;
@end

View File

@ -0,0 +1,285 @@
//
// file: StatusBarMenu.m
// project: OverSight (login item)
// description: menu handler for status bar icon
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
#import "consts.h"
#import "utilities.h"
#import "AppDelegate.h"
#import "StatusBarItem.h"
#import "StatusBarPopoverController.h"
/* GLOBALS */
//log handle
extern os_log_t logHandle;
//menu items
enum menuItems
{
status = 100,
toggle,
rules,
prefs,
quit,
end
};
@implementation StatusBarItem
@synthesize isDisabled;
@synthesize statusItem;
//init method
// set some intial flags
-(id)init:(NSMenu*)menu
{
//token
static dispatch_once_t onceToken = 0;
//super
self = [super init];
if(self != nil)
{
//create item
[self createStatusItem:menu];
//only once
// show popover
dispatch_once(&onceToken, ^{
//first time?
// show popover
if(YES == [[[NSProcessInfo processInfo] arguments] containsObject:INITIAL_LAUNCH])
{
//dbg msg
os_log_debug(logHandle, "initial launch, will show popover");
//show
[self showPopover];
}
});
//set state based on (existing) preferences
self.isDisabled = [NSUserDefaults.standardUserDefaults boolForKey:PREF_IS_DISABLED];
//set initial menu state
[self setState];
}
return self;
}
//create status item
-(void)createStatusItem:(NSMenu*)menu
{
//init status item
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
//set menu
self.statusItem.menu = menu;
//set action handler for all menu items
for(int i=toggle; i<end; i++)
{
//set action
[self.statusItem.menu itemWithTag:i].action = @selector(handler:);
//set state
[self.statusItem.menu itemWithTag:i].enabled = YES;
//set target
[self.statusItem.menu itemWithTag:i].target = self;
}
return;
}
//remove status item
-(void)removeStatusItem
{
//remove item
[[NSStatusBar systemStatusBar] removeStatusItem:self.statusItem];
//unset
self.statusItem = nil;
return;
}
//show popver
-(void)showPopover
{
//alloc popover
self.popover = [[NSPopover alloc] init];
//don't want highlight for popover
self.statusItem.button.cell.highlighted = NO;
//set target
self.statusItem.button.target = self;
//set view controller
self.popover.contentViewController = [[StatusBarPopoverController alloc] initWithNibName:@"StatusBarPopover" bundle:nil];
//set behavior
// auto-close if user clicks button in status bar
self.popover.behavior = NSPopoverBehaviorTransient;
//set delegate
self.popover.delegate = self;
//show popover
// have to wait cuz...
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(),
^{
//show
[self.popover showRelativeToRect:self.statusItem.button.bounds ofView:self.statusItem.button preferredEdge:NSMinYEdge];
});
//wait a bit
// then automatically hide popup if user has not closed it
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(),
^{
//still visible?
// close it then...
if(YES == self.popover.shown)
{
//close
[self.popover performClose:nil];
}
//remove action handler
self.statusItem.button.action = nil;
//reset highlight mode
((NSButtonCell*)self.statusItem.button.cell).highlightsBy = NSContentsCellMask | NSChangeBackgroundCellMask;
});
return;
}
//cleanup popover
-(void)popoverDidClose:(NSNotification *)notification
{
//unset
self.popover = nil;
//reset highlight mode
((NSButtonCell*)self.statusItem.button.cell).highlightsBy = NSContentsCellMask | NSChangeBackgroundCellMask;
return;
}
//menu handler
-(void)handler:(id)sender
{
//dbg msg
os_log_debug(logHandle, "user clicked status menu item %lu", ((NSMenuItem*)sender).tag);
//handle user selection
switch(((NSMenuItem*)sender).tag)
{
//toggle
case toggle:
//dbg msg
os_log_debug(logHandle, "toggling (%d)", self.isDisabled);
//invert since toggling
self.isDisabled = !self.isDisabled;
//set menu state
[self setState];
//set & sync
[NSUserDefaults.standardUserDefaults setBool:self.isDisabled forKey:PREF_IS_DISABLED];
[NSUserDefaults.standardUserDefaults synchronize];
//stop monitor
if(YES == self.isDisabled)
{
//stop
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).avMonitor stop];
}
break;
//rules
case rules:
//show rules
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) showRules:nil];
break;
//prefs
case prefs:
//show prefs
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) showPreferences:nil];
break;
//prefs
case quit:
//exit
[NSApp terminate:self];
break;
default:
break;
}
bail:
return;
}
//set menu status
// logic based on 'isEnabled' iVar
-(void)setState
{
//dbg msg
os_log_debug(logHandle, "setting state to: %@", (self.isDisabled) ? @"disabled" : @"enabled");
//set to disabled
if(YES == self.isDisabled)
{
//update status
[self.statusItem.menu itemWithTag:status].title = [NSString stringWithFormat:@"%@: disabled", PRODUCT_NAME];
//set icon
self.statusItem.button.image = [NSImage imageNamed:@"StatusInactive"];
self.statusItem.button.image.template = YES;
//change toggle text
[self.statusItem.menu itemWithTag:toggle].title = @"Enable";
}
//set to enabled
else
{
//update status
[self.statusItem.menu itemWithTag:status].title = [NSString stringWithFormat:@"%@: enabled", PRODUCT_NAME];
//set icon
self.statusItem.button.image = [NSImage imageNamed:@"StatusActive"];
self.statusItem.button.image.template = YES;
//change toggle text
[self.statusItem.menu itemWithTag:toggle].title = @"Disable";
}
return;
}
@end

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="StatusBarPopoverController">
<connections>
<outlet property="view" destination="1" id="2"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="1">
<rect key="frame" x="0.0" y="0.0" width="320" height="88"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="14">
<rect key="frame" x="20" y="20" width="48" height="48"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="AppIcon" id="15"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="16" userLabel="title">
<rect key="frame" x="78" y="46" width="200" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="OverSight up &amp; running!" id="17">
<font key="font" size="13" name="HelveticaNeue-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="21" userLabel="details">
<rect key="frame" x="78" y="18" width="161" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="...access it from here anytime" id="22">
<font key="font" metaFont="label" size="11"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button fixedFrame="YES" tag="2" translatesAutoresizingMaskIntoConstraints="NO" id="hL7-ZY-bJK">
<rect key="frame" x="288" y="56" width="32" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="bevel" title="⌃" bezelStyle="rounded" alignment="center" inset="2" id="2jG-uf-4XV">
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="closePopover:" target="-2" id="hnx-jW-0Ie"/>
</connections>
</button>
</subviews>
<point key="canvasLocation" x="258" y="142"/>
</customView>
</objects>
<resources>
<image name="AppIcon" width="128" height="128"/>
</resources>
</document>

View File

@ -0,0 +1,14 @@
//
// file: StatusBarPopoverController.h
// project: OverSight (login item)
// description: popover for status bar (header)
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
@import Cocoa;
@interface StatusBarPopoverController : NSViewController
@end

View File

@ -0,0 +1,25 @@
//
// file: StatusBarPopoverController.m
// project: OverSight (login item)
// description: popover for status bar
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
#import "AppDelegate.h"
#import "StatusBarPopoverController.h"
@implementation StatusBarPopoverController
//'close' button handler
// simply dismiss/close popover
-(IBAction)closePopover:(NSControl *)sender
{
//close
[[[self view] window] close];
return;
}
@end

View File

@ -0,0 +1,24 @@
//
// file: Update.h
// project: OverSight (shared)
// description: checks for new versions (header)
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
#ifndef Update_h
#define Update_h
@import Cocoa;
@import Foundation;
@interface Update : NSObject
//check for an update
// will invoke app delegate method to update UI when check completes
-(void)checkForUpdate:(void (^)(NSUInteger result, NSString* latestVersion))completionHandler;
@end
#endif

View File

@ -0,0 +1,105 @@
//
// file: Update.m
// project: OverSight (shared)
// description: checks for new versions
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
@import OSLog;
#import "consts.h"
#import "Update.h"
#import "utilities.h"
#import "AppDelegate.h"
/* GLOBALS */
//log handle
extern os_log_t logHandle;
@implementation Update
//check for an update
// ->will invoke app delegate method to update UI when check completes
-(void)checkForUpdate:(void (^)(NSUInteger result, NSString* latestVersion))completionHandler
{
//latest version
__block NSString* latestVersion = nil;
//result
__block NSInteger result = -1;
//get latest version in background
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//grab latest version
latestVersion = [self getLatestVersion];
if(nil != latestVersion)
{
//check
result = (NSOrderedAscending == [getAppVersion() compare:latestVersion options:NSNumericSearch]);
}
//invoke app delegate method
// ->will update UI/show popup if necessart
dispatch_async(dispatch_get_main_queue(),
^{
completionHandler(result, latestVersion);
});
});
return;
}
//query interwebz to get latest version
-(NSString*)getLatestVersion
{
//product version(s) data
NSData* productsVersionData = nil;
//version dictionary
NSDictionary* productsVersionDictionary = nil;
//latest version
NSString* latestVersion = nil;
//get version from remote URL
productsVersionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSIONS_URL]];
if(nil == productsVersionData)
{
//bail
goto bail;
}
//convert JSON to dictionary
// ->wrap as may throw exception
@try
{
//convert
productsVersionDictionary = [NSJSONSerialization JSONObjectWithData:productsVersionData options:0 error:nil];
if(nil == productsVersionDictionary)
{
//bail
goto bail;
}
}
@catch(NSException* exception)
{
//bail
goto bail;
}
//extract latest version
latestVersion = [[productsVersionDictionary objectForKey:@"OverSight"] objectForKey:@"version"];
//dbg msg
os_log_debug(logHandle, "latest version: %@", latestVersion);
bail:
return latestVersion;
}
@end

View File

@ -1,14 +1,14 @@
<?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="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="InfoWindowController">
<connections>
<outlet property="actionButton" destination="tDD-5C-L8e" id="VA6-H5-ZZO"/>
<outlet property="firstButton" destination="Igd-bt-R7A" id="ocU-Rd-7oX"/>
<outlet property="infoLabel" destination="OSm-xS-Dmd" id="Ffv-9q-5a6"/>
<outlet property="overlayView" destination="afp-ji-No6" id="qDV-BW-3l3"/>
<outlet property="progressIndicator" destination="1oq-WT-B5T" id="f6G-ch-c3m"/>
@ -17,24 +17,27 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/>
<rect key="contentRect" x="196" y="240" width="422" height="123"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES"/>
<rect key="contentRect" x="196" y="240" width="422" height="133"/>
<rect key="screenRect" x="0.0" y="0.0" width="3440" height="1415"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="422" height="123"/>
<rect key="frame" x="0.0" y="0.0" width="422" height="133"/>
<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"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon" id="xKf-GK-m0k"/>
<rect key="frame" x="20" y="29" width="89" height="85"/>
<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"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="overSight" id="Ws8-bD-j2R"/>
<rect key="frame" x="142" y="70" width="260" height="44"/>
<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="142" y="51" width="263" height="19"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OSm-xS-Dmd">
<rect key="frame" x="142" y="48" width="263" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" id="bBK-v0-ypq">
<font key="font" size="12" name="Menlo-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@ -42,7 +45,8 @@
</textFieldCell>
</textField>
<button wantsLayer="YES" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tDD-5C-L8e">
<rect key="frame" x="272" y="13" width="136" height="32"/>
<rect key="frame" x="272" y="12" width="136" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nMV-1f-RyK">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Menlo-Regular"/>
@ -51,21 +55,13 @@
<action selector="buttonHandler:" target="-2" id="tap-If-b45"/>
</connections>
</button>
<button wantsLayer="YES" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Igd-bt-R7A">
<rect key="frame" x="135" y="13" width="136" height="32"/>
<buttonCell key="cell" type="push" title="update check" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="xCE-Fi-OFb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Menlo-Regular"/>
</buttonCell>
<connections>
<action selector="updateBtnHandler:" target="-2" id="Vk0-Tx-f5d"/>
</connections>
</button>
<customView hidden="YES" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="afp-ji-No6">
<rect key="frame" x="0.0" y="0.0" width="422" height="123"/>
<rect key="frame" x="0.0" y="0.0" width="422" height="133"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" maxValue="100" bezeled="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="1oq-WT-B5T">
<rect key="frame" x="195" y="45" width="32" height="32"/>
<rect key="frame" x="195" y="50" width="32" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</progressIndicator>
</subviews>
</customView>
@ -74,11 +70,11 @@
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="318" y="246.5"/>
<point key="canvasLocation" x="318" y="251.5"/>
</window>
</objects>
<resources>
<image name="icon" width="512" height="512"/>
<image name="overSight" width="1396" height="286"/>
<image name="Icon" width="512" height="512"/>
<image name="OverSight" width="1301" height="227"/>
</resources>
</document>

View File

@ -1,14 +1,15 @@
//
// PrefsWindowController.h
// DHS
// file: UpdateWindowController.m
// project: OverSight (shared)
// description: window handler for update window/popup (header)
//
// Created by Patrick Wardle on 2/6/15.
// Copyright (c) 2015 Objective-See, LLC. All rights reserved.
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@import Cocoa;
@interface InfoWindowController : NSWindowController <NSWindowDelegate>
@interface UpdateWindowController : NSWindowController <NSWindowDelegate>
{
}

View File

@ -0,0 +1,121 @@
//
// file: UpdateWindowController.m
// project: OverSight (shared)
// description: window handler for update window/popup
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
#import "consts.h"
#import "utilities.h"
#import "AppDelegate.h"
#import "UpdateWindowController.h"
@implementation UpdateWindowController
@synthesize infoLabel;
@synthesize overlayView;
@synthesize firstButton;
@synthesize actionButton;
@synthesize infoLabelString;
@synthesize actionButtonTitle;
@synthesize progressIndicator;
//automatically called when nib is loaded
// ->center window
-(void)awakeFromNib
{
//center
[self.window center];
return;
}
//automatically invoked when window is loaded
// ->set to white
-(void)windowDidLoad
{
//super
[super windowDidLoad];
//not in dark mode?
// make window white
if(YES != isDarkMode())
{
//make white
self.window.backgroundColor = NSColor.whiteColor;
}
//indicated title bar is tranparent (too)
self.window.titlebarAppearsTransparent = YES;
//set main label
[self.infoLabel setStringValue:self.infoLabelString];
//set button text
self.actionButton.title = self.actionButtonTitle;
//hide 1st button on 'update'
// ...don't need update check button
if(YES == [self.actionButton.title isEqualToString:@"Update"])
{
//hide
self.firstButton.hidden = YES;
//then make action button first responder
[self.window makeFirstResponder:self.actionButton];
}
//make it key window
[self.window makeKeyAndOrderFront:self];
//make window front
[NSApp activateIgnoringOtherApps:YES];
return;
}
//automatically invoked when window is closing
// ->make ourselves unmodal
-(void)windowWillClose:(NSNotification *)notification
{
//make un-modal
[[NSApplication sharedApplication] stopModal];
return;
}
//save the main label's & button title's text
// ->invoked before window is loaded (and thus buttons, etc are nil)
-(void)configure:(NSString*)label buttonTitle:(NSString*)buttonTitle
{
//save label's string
self.infoLabelString = label;
//save button's title
self.actionButtonTitle = buttonTitle;
return;
}
//invoked when user clicks button
// trigger action such as opening product website, updating, etc
-(IBAction)buttonHandler:(id)sender
{
//handle 'update' / 'more info', etc
// ->open OverSight's webpage, if they *didn't* click 'close'
if(YES != [((NSButton*)sender).title isEqualToString:@"close"])
{
//open URL
// ->invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:PRODUCT_URL]];
}
//always close window
[[self window] close];
return;
}
@end

View File

@ -0,0 +1,100 @@
//
// file: main.m
// project: OverSight (login item)
// description: main; 'nuff said
//
// created by Patrick Wardle
// copyright (c) 2017 Objective-See. All rights reserved.
//
//FOR LOGGING:
// % log stream --level debug --predicate="subsystem='com.objective-see.oversight'"
@import Cocoa;
@import OSLog;
@import Sentry;
#import "consts.h"
#import "utilities.h"
/* GLOBALS */
//log handle
os_log_t logHandle = nil;
//main interface
// sanity checks, then kick off app
int main(int argc, const char * argv[])
{
//status
int status = 0;
//(v1.0) allowed items
NSArray* allowedItems = nil;
//init log
logHandle = os_log_create(BUNDLE_ID, "application");
//dbg msg(s)
os_log_debug(logHandle, "started: %{public}@ (pid: %d / uid: %d)", NSProcessInfo.processInfo.arguments.firstObject, getpid(), getuid());
os_log_debug(logHandle, "arguments: %{public}@", NSProcessInfo.processInfo.arguments);
//init crash reporting
[SentrySDK startWithConfigureOptions:^(SentryOptions *options) {
options.dsn = SENTRY_DSN;
options.debug = YES;
}];
//upgrade allowed items?
// convert into 'NSUserDefaults' and then exit
if(YES == [NSProcessInfo.processInfo.arguments containsObject:CMD_UPGRADE])
{
//dbg msg
os_log_debug(logHandle, "upgrading allowed items (from: %@)", NSProcessInfo.processInfo.arguments.lastObject);
//load rules
allowedItems = [NSArray arrayWithContentsOfFile:NSProcessInfo.processInfo.arguments.lastObject];
if(0 != allowedItems.count)
{
//set & snyc
[NSUserDefaults.standardUserDefaults setValue:allowedItems forKey:PREFS_ALLOWED_ITEMS];
[NSUserDefaults.standardUserDefaults synchronize];
}
//done
goto bail;
}
//initial launch?
// set defaults/handle login item persistence
if(YES == [NSProcessInfo.processInfo.arguments containsObject:INITIAL_LAUNCH])
{
// autostart mode
// not specified? set to true
if(nil == [NSUserDefaults.standardUserDefaults objectForKey:PREF_AUTOSTART_MODE])
{
//set & snyc
[NSUserDefaults.standardUserDefaults setBool:YES forKey:PREF_AUTOSTART_MODE];
[NSUserDefaults.standardUserDefaults synchronize];
}
//autostart mode enabled?
// since this is initial launch, check that login item is set
if(YES == [NSUserDefaults.standardUserDefaults boolForKey:PREF_AUTOSTART_MODE])
{
//dbg msg
os_log_debug(logHandle, "first launch + auto-start is set ...will ensure app is persisted as login item");
//persist
toggleLoginItem([NSURL fileURLWithPath:NSBundle.mainBundle.bundlePath], NSControlStateValueOn);
}
}
//launch app normally
status = NSApplicationMain(argc, argv);
bail:
return status;
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Contact</key>
<string>security@objective-see.com</string>
<key>Preferred-Languages</key>
<string>en</string>
</dict>
</plist>

1
Cartfile Normal file
View File

@ -0,0 +1 @@
github "getsentry/sentry-cocoa" "6.0.10"

1
Cartfile.resolved Normal file
View File

@ -0,0 +1 @@
github "getsentry/sentry-cocoa" "6.0.0"

View File

@ -1,428 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
7D17C4E51D658D9F0066232A /* AboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4D81D658D9F0066232A /* AboutWindow.xib */; };
7D17C4E61D658D9F0066232A /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4DA1D658D9F0066232A /* AboutWindowController.m */; };
7D17C4E71D658D9F0066232A /* Exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4DD1D658D9F0066232A /* Exception.m */; };
7D17C4E81D658D9F0066232A /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4DF1D658D9F0066232A /* icon.png */; };
7D17C4E91D658D9F0066232A /* overSight.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4E01D658D9F0066232A /* overSight.png */; };
7D17C4EA1D658D9F0066232A /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4E21D658D9F0066232A /* Logging.m */; };
7D17C4EB1D658D9F0066232A /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4E41D658D9F0066232A /* Utilities.m */; };
7D17C4F91D658DB90066232A /* Configure.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4EC1D658DB90066232A /* Configure.m */; };
7D17C4FA1D658DB90066232A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4ED1D658DB90066232A /* AppDelegate.m */; };
7D17C4FB1D658DB90066232A /* ConfigureWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4EE1D658DB90066232A /* ConfigureWindowController.m */; };
7D17C4FC1D658DB90066232A /* ConfigureWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4EF1D658DB90066232A /* ConfigureWindowController.xib */; };
7D17C4FD1D658DB90066232A /* ErrorWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4F31D658DB90066232A /* ErrorWindowController.m */; };
7D17C4FE1D658DB90066232A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4F41D658DB90066232A /* main.m */; };
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 */
7D17C4D81D658D9F0066232A /* AboutWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutWindow.xib; sourceTree = "<group>"; };
7D17C4D91D658D9F0066232A /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutWindowController.h; sourceTree = "<group>"; };
7D17C4DA1D658D9F0066232A /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutWindowController.m; sourceTree = "<group>"; };
7D17C4DB1D658D9F0066232A /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
7D17C4DC1D658D9F0066232A /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = "<group>"; };
7D17C4DD1D658D9F0066232A /* Exception.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Exception.m; sourceTree = "<group>"; };
7D17C4DF1D658D9F0066232A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
7D17C4E01D658D9F0066232A /* overSight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = overSight.png; sourceTree = "<group>"; };
7D17C4E11D658D9F0066232A /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
7D17C4E21D658D9F0066232A /* Logging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Logging.m; sourceTree = "<group>"; };
7D17C4E31D658D9F0066232A /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
7D17C4E41D658D9F0066232A /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
7D17C4EC1D658DB90066232A /* Configure.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Configure.m; path = Installer/Configure.m; sourceTree = SOURCE_ROOT; };
7D17C4ED1D658DB90066232A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Installer/AppDelegate.m; sourceTree = SOURCE_ROOT; };
7D17C4EE1D658DB90066232A /* ConfigureWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ConfigureWindowController.m; path = Installer/ConfigureWindowController.m; sourceTree = SOURCE_ROOT; };
7D17C4EF1D658DB90066232A /* ConfigureWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ConfigureWindowController.xib; path = Installer/ConfigureWindowController.xib; sourceTree = SOURCE_ROOT; };
7D17C4F01D658DB90066232A /* Configure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Configure.h; path = Installer/Configure.h; sourceTree = SOURCE_ROOT; };
7D17C4F11D658DB90066232A /* ConfigureWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConfigureWindowController.h; path = Installer/ConfigureWindowController.h; sourceTree = SOURCE_ROOT; };
7D17C4F21D658DB90066232A /* ErrorWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ErrorWindowController.h; path = Installer/ErrorWindowController.h; sourceTree = SOURCE_ROOT; };
7D17C4F31D658DB90066232A /* ErrorWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ErrorWindowController.m; path = Installer/ErrorWindowController.m; sourceTree = SOURCE_ROOT; };
7D17C4F41D658DB90066232A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Installer/main.m; sourceTree = SOURCE_ROOT; };
7D17C4F51D658DB90066232A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Installer/Info.plist; sourceTree = SOURCE_ROOT; };
7D17C4F61D658DB90066232A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Installer/AppDelegate.h; sourceTree = SOURCE_ROOT; };
7D17C4F81D658DB90066232A /* ErrorWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ErrorWindowController.xib; path = Installer/ErrorWindowController.xib; sourceTree = SOURCE_ROOT; };
7D17C5031D658DEC0066232A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Installer/Base.lproj/MainMenu.xib; sourceTree = SOURCE_ROOT; };
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 */
7D24C8621D2CDEA7009932EE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
7D17C4D71D658D9F0066232A /* Shared */ = {
isa = PBXGroup;
children = (
7D6245821D87C3D700870565 /* Images.xcassets */,
7D17C4D81D658D9F0066232A /* AboutWindow.xib */,
7D17C4D91D658D9F0066232A /* AboutWindowController.h */,
7D17C4DA1D658D9F0066232A /* AboutWindowController.m */,
7D17C4DB1D658D9F0066232A /* Consts.h */,
7D17C4DC1D658D9F0066232A /* Exception.h */,
7D17C4DD1D658D9F0066232A /* Exception.m */,
7D17C4DE1D658D9F0066232A /* Images */,
7D17C4E11D658D9F0066232A /* Logging.h */,
7D17C4E21D658D9F0066232A /* Logging.m */,
7D17C4E31D658D9F0066232A /* Utilities.h */,
7D17C4E41D658D9F0066232A /* Utilities.m */,
);
path = Shared;
sourceTree = "<group>";
};
7D17C4DE1D658D9F0066232A /* Images */ = {
isa = PBXGroup;
children = (
7D17C4DF1D658D9F0066232A /* icon.png */,
7DEABC471E907DF10024C6AB /* objectiveSee.png */,
7D17C4E01D658D9F0066232A /* overSight.png */,
);
path = Images;
sourceTree = "<group>";
};
7D24C85C1D2CDEA7009932EE = {
isa = PBXGroup;
children = (
7D17C4D71D658D9F0066232A /* Shared */,
7D24C8671D2CDEA7009932EE /* Source */,
7D24C8661D2CDEA7009932EE /* Products */,
);
sourceTree = "<group>";
};
7D24C8661D2CDEA7009932EE /* Products */ = {
isa = PBXGroup;
children = (
7D24C8651D2CDEA7009932EE /* OverSight_Installer.app */,
);
name = Products;
sourceTree = "<group>";
};
7D24C8671D2CDEA7009932EE /* Source */ = {
isa = PBXGroup;
children = (
7D17C5021D658DEC0066232A /* MainMenu.xib */,
7D17C4F61D658DB90066232A /* AppDelegate.h */,
7D17C4ED1D658DB90066232A /* AppDelegate.m */,
7D17C4F11D658DB90066232A /* ConfigureWindowController.h */,
7D17C4EE1D658DB90066232A /* ConfigureWindowController.m */,
7D17C4EF1D658DB90066232A /* ConfigureWindowController.xib */,
7D17C4F01D658DB90066232A /* Configure.h */,
7D17C4EC1D658DB90066232A /* Configure.m */,
7D17C4F21D658DB90066232A /* ErrorWindowController.h */,
7D17C4F31D658DB90066232A /* ErrorWindowController.m */,
7D17C4F81D658DB90066232A /* ErrorWindowController.xib */,
7D24C86B1D2CDEA7009932EE /* Supporting Files */,
);
name = Source;
path = WhatsYourSign;
sourceTree = "<group>";
};
7D24C86B1D2CDEA7009932EE /* Supporting Files */ = {
isa = PBXGroup;
children = (
7D17C4F51D658DB90066232A /* Info.plist */,
7D17C4F41D658DB90066232A /* main.m */,
7D9A7DE91D8A8BDA0091C1AF /* main.h */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
7D24C8641D2CDEA7009932EE /* Installer */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7D24C8761D2CDEA7009932EE /* Build configuration list for PBXNativeTarget "Installer" */;
buildPhases = (
7D24C8611D2CDEA7009932EE /* Sources */,
7D24C8621D2CDEA7009932EE /* Frameworks */,
7D24C8631D2CDEA7009932EE /* Resources */,
7D1A49C71D502D4A000E2BEB /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = Installer;
productName = WhatsYourSign;
productReference = 7D24C8651D2CDEA7009932EE /* OverSight_Installer.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
7D24C85D1D2CDEA7009932EE /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = "Objective-See";
TargetAttributes = {
7D24C8641D2CDEA7009932EE = {
CreatedOnToolsVersion = 7.3;
DevelopmentTeam = VBG97UB4TA;
};
};
};
buildConfigurationList = 7D24C8601D2CDEA7009932EE /* Build configuration list for PBXProject "Installer" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 7D24C85C1D2CDEA7009932EE;
productRefGroup = 7D24C8661D2CDEA7009932EE /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
7D24C8641D2CDEA7009932EE /* Installer */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
7D24C8631D2CDEA7009932EE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
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 */,
7D17C5041D658DEC0066232A /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
7D1A49C71D502D4A000E2BEB /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "#for normal builds\n# ->just copy in app\n#cp -R -f $BUILT_PRODUCTS_DIR/OverSight.app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources/\n\n#for achieving\n# ->first build OverSight in release mode (don't archive it)\n# then archive (this) installer app\n#cp -R -f ~/objective-see/OverSight/DerivedData/OverSight/Build/Products/Release/OverSight.app ~/objective-see/OverSight/DerivedData/OverSight/Build/Intermediates/ArchiveIntermediates/Installer/BuildProductsPath/Release/OverSight_Installer.app/Contents/Resources/\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
7D24C8611D2CDEA7009932EE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7D17C4FB1D658DB90066232A /* ConfigureWindowController.m in Sources */,
7D17C4FA1D658DB90066232A /* AppDelegate.m in Sources */,
7D17C4E71D658D9F0066232A /* Exception.m in Sources */,
7D17C4EB1D658D9F0066232A /* Utilities.m in Sources */,
7D17C4F91D658DB90066232A /* Configure.m in Sources */,
7D17C4EA1D658D9F0066232A /* Logging.m in Sources */,
7D17C4FD1D658DB90066232A /* ErrorWindowController.m in Sources */,
7D17C4FE1D658DB90066232A /* main.m in Sources */,
7D17C4E61D658D9F0066232A /* AboutWindowController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
7D17C5021D658DEC0066232A /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
7D17C5031D658DEC0066232A /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
7D24C8741D2CDEA7009932EE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
7D24C8751D2CDEA7009932EE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
7D24C8771D2CDEA7009932EE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = VBG97UB4TA;
GCC_PREPROCESSOR_DEFINITIONS = (
"IS_INSTALLER_APP=1",
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
INFOPLIST_FILE = Installer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = "com.objectiveSee.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = OverSight_Installer;
USER_HEADER_SEARCH_PATHS = ../Shared;
};
name = Debug;
};
7D24C8781D2CDEA7009932EE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = VBG97UB4TA;
GCC_PREPROCESSOR_DEFINITIONS = "IS_INSTALLER_APP=1";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
INFOPLIST_FILE = Installer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = "com.objectiveSee.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = OverSight_Installer;
USER_HEADER_SEARCH_PATHS = ../Shared;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
7D24C8601D2CDEA7009932EE /* Build configuration list for PBXProject "Installer" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7D24C8741D2CDEA7009932EE /* Debug */,
7D24C8751D2CDEA7009932EE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7D24C8761D2CDEA7009932EE /* Build configuration list for PBXNativeTarget "Installer" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7D24C8771D2CDEA7009932EE /* Debug */,
7D24C8781D2CDEA7009932EE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 7D24C85D1D2CDEA7009932EE /* Project object */;
}

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:WhatsYourSign.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,42 +0,0 @@
//
// AppDelegate.h
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "AboutWindowController.h"
#import "ErrorWindowController.h"
#import "ConfigureWindowController.h"
@interface AppDelegate : NSObject <NSApplicationDelegate, NSMenuDelegate, NSWindowDelegate>
{
}
/* PROPERTIES */
//about window controller
@property(nonatomic, retain)AboutWindowController* aboutWindowController;
//configure window controller
@property(nonatomic, retain)ConfigureWindowController* configureWindowController;
//error window controller
@property(nonatomic, retain)ErrorWindowController* errorWindowController;
/* METHODS */
//display configuration window w/ 'install' || 'uninstall' button
-(void)displayConfigureWindow:(BOOL)isInstalled;
//display error window
-(void)displayErrorWindow:(NSDictionary*)errorInfo;
@end

View File

@ -1,140 +0,0 @@
//
// AppDelegate.m
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "Configure.h"
#import "Exception.h"
#import "Utilities.h"
#import "AppDelegate.h"
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@end
@implementation AppDelegate
@synthesize aboutWindowController;
@synthesize errorWindowController;
@synthesize configureWindowController;
//main app interface
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
//config object
Configure* configureObj = nil;
//first thing...
// ->install exception handlers
installExceptionHandlers();
//alloc/init Config obj
configureObj = [[Configure alloc] init];
//show window
[self displayConfigureWindow:[configureObj isInstalled]];
//bail
bail:
return;
}
//automatically invoked when user clicks 'About/Info'
// ->show about window
-(IBAction)about:(id)sender
{
//alloc/init settings window
if(nil == self.aboutWindowController)
{
//alloc/init
aboutWindowController = [[AboutWindowController alloc] initWithWindowNibName:@"AboutWindow"];
}
//center window
[[self.aboutWindowController window] center];
//show it
[self.aboutWindowController showWindow:self];
return;
}
//display configuration window w/ 'install' || 'uninstall' button
-(void)displayConfigureWindow:(BOOL)isInstalled
{
//alloc/init
configureWindowController = [[ConfigureWindowController alloc] initWithWindowNibName:@"ConfigureWindowController"];
//indicated title bar is tranparent (too)
self.configureWindowController.window.titlebarAppearsTransparent = YES;
//display it
// ->call this first to so that outlets are connected
[self.configureWindowController display];
//configure it
[self.configureWindowController configure:isInstalled];
return;
}
//display error window
-(void)displayErrorWindow:(NSDictionary*)errorInfo
{
//alloc error window
errorWindowController = [[ErrorWindowController alloc] initWithWindowNibName:@"ErrorWindowController"];
//main thread
// ->just show UI alert, unless its fatal (then load URL)
if(YES == [NSThread isMainThread])
{
//non-fatal errors
// ->show error popup
if(YES != [errorInfo[KEY_ERROR_URL] isEqualToString:FATAL_ERROR_URL])
{
//display it
// ->call this first to so that outlets are connected
[self.errorWindowController display];
//configure it
[self.errorWindowController configure:errorInfo];
}
//fatal error
// ->launch browser to go to fatal error page, then exit
else
{
//launch browser
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:errorInfo[KEY_ERROR_URL]]];
//then exit
[NSApp terminate:self];
}
}
//background thread
// ->have to show error window on main thread
else
{
//show alert
// ->in main UI thread
dispatch_sync(dispatch_get_main_queue(), ^{
//display it
// ->call this first to so that outlets are connected
[self.errorWindowController display];
//configure it
[self.errorWindowController configure:errorInfo];
});
}
return;
}
@end

View File

@ -1,40 +0,0 @@
<?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" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="OverSight" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="OverSight" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="about:" target="Voe-Tx-rLC" id="Hep-HT-rEM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</objects>
</document>

View File

@ -1,39 +0,0 @@
//
// Configure.h
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#ifndef __WYS_Configure_h
#define __WYS_Configure_h
#import <Foundation/Foundation.h>
@interface Configure : NSObject
{
}
/* METHODS */
//determine if extension is installed
-(BOOL)isInstalled;
//invokes appropriate install || uninstall logic
-(BOOL)configure:(NSUInteger)parameter;
//install
-(BOOL)install;
//uninstall
-(BOOL)uninstall:(NSUInteger)type;
//build path to logged in user's app support directory + '/Objective-See'
-(NSString*)appSupportPath:(NSString*)user;
@end
#endif

View File

@ -1,567 +0,0 @@
//
// Configure.m
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <ServiceManagement/ServiceManagement.h>
#import "Consts.h"
#import "Logging.h"
#import "Utilities.h"
#import "Configure.h"
@implementation Configure
//invokes appropriate install || uninstall logic
-(BOOL)configure:(NSUInteger)parameter
{
//return var
BOOL wasConfigured = NO;
//install
// ->starts on success
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 main app/login item/XPC service
[self stop];
//uninstall
// ->but do partial (leave whitelist)
if(YES != [self uninstall:UNINSTALL_PARIAL])
{
//bail
goto bail;
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"uninstalled");
#endif
}
//install
if(YES != [self install])
{
//err msg
logMsg(LOG_ERR, @"installation failed");
//bail
goto bail;
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"installed, now will start");
#endif
//start login item
if(YES != [self start])
{
//err msg
logMsg(LOG_ERR, @"starting failed");
//bail
goto bail;
}
}
//uninstall
// ->stops login item (also w/ stop XPC service)
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])
{
//bail
goto bail;
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"uninstalled!");
#endif
}
//no errors
wasConfigured = YES;
bail:
return wasConfigured;
}
//determine if installed
// ->simply checks if application exists in /Applications
-(BOOL)isInstalled
{
//check if extension exists
return [[NSFileManager defaultManager] fileExistsAtPath:[APPS_FOLDER stringByAppendingPathComponent:APP_NAME]];
}
//install
// a) copy to /Applications
// b) chown/chmod XPC component
-(BOOL)install
{
//return/status var
BOOL wasInstalled = NO;
//error
NSError* error = nil;
//path to app (src)
NSString* appPathSrc = nil;
//path to app (dest)
NSString* appPathDest = nil;
//path to XPC service
NSString* xpcServicePath = nil;
//path to login item
NSString* loginItem = nil;
//logged in user info
NSMutableDictionary* userInfo = nil;
//white list
NSString* whiteList = nil;
//set src path
// ->orginally stored in installer app's /Resource bundle
appPathSrc = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:APP_NAME];
//set dest path
appPathDest = [APPS_FOLDER stringByAppendingPathComponent:APP_NAME];
//move app into /Applications
if(YES != [[NSFileManager defaultManager] copyItemAtPath:appPathSrc toPath:appPathDest error:&error])
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to copy %@ -> %@ (%@)", appPathSrc, appPathDest, error]);
//bail
goto 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], NO);
//dbg msg
#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"];
//get user info
userInfo = loggedinUser();
if(nil == userInfo[@"user"])
{
//err msg
logMsg(LOG_ERR, @"failed to determine logged-in user");
//bail
goto bail;
}
//create app support directory
if(YES != [self createAppSupport:userInfo[@"user"]])
{
//err msg
logMsg(LOG_ERR, @"failed to create app support directory for current user");
//bail
goto bail;
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"created app support directory");
#endif
//init path to whitelist
whiteList = [[NSString pathWithComponents:@[@"/Users/", userInfo[@"user"], APP_SUPPORT_DIRECTORY]] stringByAppendingPathComponent:FILE_WHITELIST];
//if whitelist exists
// ->make sure it's owned by root
if(YES == [[NSFileManager defaultManager] fileExistsAtPath:whiteList])
{
//set owner, root
setFileOwner(whiteList, @0, @0, NO);
}
//call into login item to install itself
execTask(loginItem, @[[NSString stringWithUTF8String:CMD_INSTALL]], NO);
//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"];
//set XPC service to be owned; root:wheel
if(YES != setFileOwner(xpcServicePath, @0, @0, YES))
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to set file owner to root:wheel on %@", xpcServicePath]);
//bail
goto bail;
}
//set XPC service binary to setuid
if(YES != setFilePermissions(xpcServicePath, 06755, YES))
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to set file permissions to 06755 on %@", xpcServicePath]);
//bail
goto bail;
}
//no error
wasInstalled = YES;
bail:
return wasInstalled;
}
//start login item
// ->exec as logged in user, since might be called via 'sudo' (cmdline install)
-(BOOL)start
{
//flag
BOOL bStarted = NO;
//path to login item
NSString* loginItem = nil;
//init path
loginItem = [[APPS_FOLDER stringByAppendingPathComponent:APP_NAME] stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"];
//start it!
// ->don't wait, as it won't exit
execTask(loginItem, nil, NO);
//happy
bStarted = YES;
bail:
return bStarted;
}
//stop
-(void)stop
{
//kill main app
execTask(PKILL, @[[APP_NAME stringByDeletingPathExtension]], YES);
//kill helper app
execTask(PKILL, @[APP_HELPER], YES);
//kill xpc
execTask(PKILL, @[APP_HELPER_XPC], YES);
return;
}
//uninstall
// ->delete app, remove login item, etc
-(BOOL)uninstall:(NSUInteger)type
{
//return/status var
BOOL wasUninstalled = NO;
//status var
// ->since want to try (most) uninstall steps, but record if any fail
BOOL bAnyErrors = NO;
//path to login item
NSString* loginItem = nil;
//installed version
NSString* installedVersion = nil;
//path to installed app
NSString* installedAppPath = nil;
//error
NSError* error = nil;
//logged in user info
NSMutableDictionary* userInfo = nil;
//uninstall command
// ->changed between v1.0 and 1.1+
NSString* uninstallCmd = nil;
//init path to login item
loginItem = [[APPS_FOLDER stringByAppendingPathComponent:APP_NAME] stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"];
//init path to installed app
installedAppPath = [APPS_FOLDER stringByAppendingPathComponent:APP_NAME];
//get installed app version
installedVersion = [[NSBundle bundleWithPath:installedAppPath] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey];
//set uninstall command for version 1.0.0
if(YES == [installedVersion isEqualToString:@"1.0.0"])
{
//set command
uninstallCmd = ACTION_UNINSTALL;
}
//set uninstall command for version 1.1.+
else
{
//set command
uninstallCmd = [NSString stringWithUTF8String:CMD_UNINSTALL];
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"uninstalling login item, with command: '%@'", uninstallCmd]);
#endif
//get user
userInfo = loggedinUser();
if(nil == userInfo[@"user"])
{
//err msg
logMsg(LOG_ERR, @"failed to determine logged-in user");
//bail since lots else depends on this
goto bail;
}
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"telling login item %@, to uninstall itself", loginItem]);
#endif
//call into login item to uninstall itself
execTask(loginItem, @[uninstallCmd], YES);
//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])
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete app %@ (%@)", installedAppPath, error]);
//set flag
bAnyErrors = YES;
//keep uninstalling...
}
//full uninstall?
// ->remove app support directory too
if(UNINSTALL_FULL == type)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"full uninstall, so also deleting app support directory");
#endif
//delete app's app support folder
if(YES == [[NSFileManager defaultManager] fileExistsAtPath:[self appSupportPath:userInfo[@"user"]]])
{
//delete
if(YES != [self removeAppSupport:userInfo[@"user"]])
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete app support directory %@", [self appSupportPath:userInfo[@"user"]]]);
//set flag
bAnyErrors = YES;
//keep uninstalling...
}
//dbg msg
#ifdef DEBUG
else
{
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"removed app support directory %@", [self appSupportPath:userInfo[@"user"]]]);
}
#endif
}
}
//only success when there were no errors
if(YES != bAnyErrors)
{
//happy
wasUninstalled = YES;
}
bail:
return wasUninstalled;
}
//build path to logged in user's app support directory + '/Objective-See/OverSight'
// ->do this manually, since installer might be run via sudo, etc, so can just expand '~'
-(NSString*)appSupportPath:(NSString*)user
{
//build path
return [NSString pathWithComponents:@[@"/Users/", user, APP_SUPPORT_DIRECTORY]];
}
//create directory app support
// ->store whitelist file, log file, etc
-(BOOL)createAppSupport:(NSString*)user
{
//flag
BOOL createdDirectory = NO;
//directory
NSString* appSupportDirectory = nil;
//user's directory permissions
// ->used to match any created directories
NSDictionary* userDirAttributes = nil;
//build path
appSupportDirectory = [self appSupportPath:user];
//create if not present
if(YES != [[NSFileManager defaultManager] fileExistsAtPath:appSupportDirectory])
{
//create it
if(YES != [[NSFileManager defaultManager] createDirectoryAtPath:appSupportDirectory withIntermediateDirectories:YES attributes:nil error:NULL])
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to create app support directory (%@)", appSupportDirectory]);
//bail
goto bail;
}
}
//get permissions of one directory up
// -> ~/Library
userDirAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[@"/Users/" stringByAppendingPathComponent:user] error:nil];
//assuming required attributes were found
// ->make sure ~/Library/Application Support/Objective-See is owned by user
if( (nil != userDirAttributes) &&
(nil != userDirAttributes[@"NSFileGroupOwnerAccountID"]) &&
(nil != userDirAttributes[@"NSFileOwnerAccountID"]) )
{
//match newly created directory w/ user
setFileOwner([appSupportDirectory stringByDeletingLastPathComponent], userDirAttributes[@"NSFileGroupOwnerAccountID"], userDirAttributes[@"NSFileOwnerAccountID"], YES);
}
//happy
createdDirectory = YES;
bail:
return createdDirectory;
}
//remove ~/Library/Application Support/Objective-See/OverSight
// and also ~/Library/Application Support/Objective-See/ if nothing else is in there (no other products)
-(BOOL)removeAppSupport:(NSString*)user
{
//flag
BOOL removedDirectory = NO;
//directory
NSString* appSupportDirectory = nil;
//error
NSError* error = nil;
//build path
appSupportDirectory = [self appSupportPath:user];
//delete OverSight's app support directory
if(YES != [[NSFileManager defaultManager] removeItemAtPath:appSupportDirectory error:&error])
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete OverSight's app support directory %@ (%@)", appSupportDirectory, error]);
//bail
goto bail;
}
//anything left in ~/Library/Application Support/Objective-See/?
// ->nope: delete it
if(0 == [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:[appSupportDirectory stringByDeletingLastPathComponent] error:nil] count])
{
if(YES != [[NSFileManager defaultManager] removeItemAtPath:[appSupportDirectory stringByDeletingLastPathComponent] error:&error])
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete Objective-See's app support directory %@ (%@)", [appSupportDirectory stringByDeletingLastPathComponent], error]);
//bail
goto bail;
}
}
//happy
removedDirectory = YES;
bail:
return removedDirectory;
}
@end

View File

@ -1,45 +0,0 @@
//
// ConfigureWindowController.h
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface ConfigureWindowController : NSWindowController <NSWindowDelegate>
{
}
/* PROPERTIES */
@property (weak) IBOutlet NSProgressIndicator *activityIndicator;
@property (weak) IBOutlet NSTextField *statusMsg;
@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 */
//install/uninstall button handler
-(IBAction)buttonHandler:(id)sender;
//(more) info button handler
-(IBAction)info:(id)sender;
//configure window/buttons
// ->also brings to front
-(void)configure:(BOOL)isInstalled;
//display (show) window
-(void)display;
@end

View File

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

View File

@ -1,144 +0,0 @@
<?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="11762"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ConfigureWindowController">
<connections>
<outlet property="activityIndicator" destination="b1w-5W-ayX" id="P5e-PO-Ozi"/>
<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>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
<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="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"/>
</buttonCell>
<connections>
<action selector="buttonHandler:" target="-2" id="GAh-S3-3e6"/>
</connections>
</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"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</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"/>
</buttonCell>
<connections>
<action selector="buttonHandler:" target="-2" id="1wC-Ls-D2F"/>
</connections>
</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"/>
</buttonCell>
<connections>
<action selector="info:" target="-2" id="tg3-SV-AH9"/>
</connections>
</button>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
<outlet property="initialFirstResponder" destination="553-2y-kvm" id="zb6-Px-IcH"/>
</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

@ -1,42 +0,0 @@
//
// ErrorWindowController.h
// OverSight
//
// Created by Patrick Wardle on 7/15/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface ErrorWindowController : NSWindowController <NSWindowDelegate>
{
}
//main msg in window
@property (weak) IBOutlet NSTextField *errMsg;
//sub msg in window
@property (weak) IBOutlet NSTextField *errSubMsg;
//info/help/fix button
@property (weak) IBOutlet NSButton *infoButton;
//close button
@property (weak) IBOutlet NSButton *closeButton;
//(optional) url for 'Info' button
@property(nonatomic, retain) NSURL* errorURL;
//flag indicating close button should exit app
@property BOOL shouldExit;
/* METHODS */
//configure the object/window
-(void)configure:(NSDictionary*)errorInfo;
//display (show) window
-(void)display;
@end

View File

@ -1,153 +0,0 @@
//
// ErrorWindowController.m
// OverSight
//
// Created by Patrick Wardle on 7/15/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "Logging.h"
#import "ErrorWindowController.h"
@interface ErrorWindowController ()
@end
@implementation ErrorWindowController
@synthesize errorURL;
@synthesize shouldExit;
@synthesize closeButton;
//automatically called when nib is loaded
// ->center window
-(void)awakeFromNib
{
//center
[self.window center];
return;
}
//configure the object/window
-(void)configure:(NSDictionary*)errorInfo
{
//set error msg
self.errMsg.stringValue = errorInfo[KEY_ERROR_MSG];
//set error sub msg
self.errSubMsg.stringValue = errorInfo[KEY_ERROR_SUB_MSG];
//save exit
self.shouldExit = [errorInfo[KEY_ERROR_SHOULD_EXIT] boolValue];
//grab optional error url
if(nil != errorInfo[KEY_ERROR_URL])
{
//extract/convert
self.errorURL = [NSURL URLWithString:errorInfo[KEY_ERROR_URL]];
}
//when exiting
// ->change 'close' to 'exit'
if(YES == self.shouldExit)
{
//change title
self.closeButton.title = @"Exit";
}
//for fatal errors
// ->change 'Info' to 'help fix'
if(YES == [[self.errorURL absoluteString] isEqualToString:FATAL_ERROR_URL])
{
//change title
self.infoButton.title = @"Help Fix";
}
//set delegate
[self.window setDelegate:self];
return;
}
//display (show) window
-(void)display
{
//show (now configured), alert
[self showWindow:self];
//make it key window
[self.window makeKeyAndOrderFront:self];
//make window front
[NSApp activateIgnoringOtherApps:YES];
//make 'close' have focus
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
//make close button active
[self.window makeFirstResponder:closeButton];
});
//make white
[self.window setBackgroundColor: NSColor.whiteColor];
return;
}
//invoked when user clicks '?' (help button)
// ->open url with more info about the error(s)
-(IBAction)help:(id)sender
{
//if a url was specified
// ->use that one
if(nil != self.errorURL)
{
//open URL
// ->invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:self.errorURL];
}
//use default URL
else
{
//open URL
// ->invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:ERRORS_URL]];
}
return;
}
//invoked when user clicks 'close'
// ->just close window
-(IBAction)close:(id)sender
{
//close
[self.window close];
return;
}
//automatically invoked when window is closing
// ->exit the app if specified...
-(void)windowWillClose:(NSNotification *)notification
{
//check if should exit process
// ->e.g. an error during install, etc
if(YES == self.shouldExit)
{
//dbg msg
#ifdef DEBUG
logMsg(LOG_DEBUG, @"exiting application");
#endif
//exit
[NSApp terminate:self];
}
return;
}
@end

View File

@ -1,84 +0,0 @@
<?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="11762"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ErrorWindowController">
<connections>
<outlet property="closeButton" destination="zbs-ul-WTJ" id="1fu-zv-brA"/>
<outlet property="errMsg" destination="Act-U9-8G9" id="PMa-6M-gwu"/>
<outlet property="errSubMsg" destination="tQ0-f2-G4C" id="4hz-9M-Gub"/>
<outlet property="infoButton" destination="sNV-Jw-9dZ" id="qdf-xQ-ubf"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="OVERSIGHT? Error" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<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="744" y="427" width="380" height="137"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="380" height="137"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zbs-ul-WTJ">
<rect key="frame" x="284" y="13" width="82" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Close" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Pjp-Se-SfK">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="close:" target="-2" id="sez-MW-4AE"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="tQ0-f2-G4C">
<rect key="frame" x="114" y="49" width="248" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Label" id="sk0-EF-ZfG">
<font key="font" size="11" name="Menlo-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="Act-U9-8G9">
<rect key="frame" x="114" y="88" width="248" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="ERROR msg" id="VCu-zh-Vcs">
<font key="font" size="13" name="Menlo-Bold"/>
<color key="textColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sNV-Jw-9dZ">
<rect key="frame" x="202" y="13" width="82" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Info" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="L1w-RB-EC4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="help:" target="-2" id="gCm-No-dHq"/>
</connections>
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sRj-I0-15e">
<rect key="frame" x="12" y="35" width="92" height="92"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon" id="9iG-ot-b7i"/>
</imageView>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="257" y="269.5"/>
</window>
</objects>
<resources>
<image name="icon" width="512" height="512"/>
</resources>
</document>

69
Installer/Helper/Helper.m Normal file
View File

@ -0,0 +1,69 @@
//
// file: Helper.m
// project: (open-source) installer
// description: main/entry point of daemon
//
// created by Patrick Wardle
// copyright (c) 2018 Objective-See. All rights reserved.
//
@import OSLog;
@import Foundation;
#import "consts.h"
#import "XPCProtocol.h"
#import "HelperListener.h"
#import "HelperInterface.h"
/* GLOBALS */
//log handle
os_log_t logHandle = nil;
//helper daemon entry point
// create XPC listener object and then just wait
int main(int argc, const char * argv[])
{
//pragmas
#pragma unused(argc)
#pragma unused(argv)
//status
int status = -1;
//init log
logHandle = os_log_create(BUNDLE_ID, "installer (helper)");
//pool
@autoreleasepool
{
//helper listener (XPC) obj
HelperListener* helperListener = nil;
//alloc/init helper listener XPC obj
helperListener = [[HelperListener alloc] init];
if(nil == helperListener)
{
//err msg
os_log_error(logHandle, "ERROR: failed to initialize user comms XPC listener");
//bail
goto bail;
}
//dbg msg
os_log_debug(logHandle, "listening for client XPC connections...");
//run loop
[[NSRunLoop currentRunLoop] run];
} //pool
//happy
// though not sure how we'll ever get here?
status = 0;
bail:
return status;
}

View File

@ -0,0 +1,20 @@
//
// file: HelperInterface.h
// project: (open-source) installer
// description: interface for app installer comms (header)
//
// created by Patrick Wardle
// copyright (c) 2018 Objective-See. All rights reserved.
//
@import Foundation;
#import "XPCProtocol.h"
#import "HelperInterface.h"
@interface HelperInterface : NSObject <XPCProtocol>
{
}
@end

View File

@ -0,0 +1,352 @@
//
// file: HelperInterface.m
// project: (open-source) installer
// description: interface for app installer comms
//
// created by Patrick Wardle
// copyright (c) 2018 Objective-See. All rights reserved.
//
@import OSLog;
#import "consts.h"
#import "utilities.h"
#import "HelperInterface.h"
#import <signal.h>
//script name
#define CONF_SCRIPT @"configure.sh"
//signing auth
#define SIGNING_AUTH @"Developer ID Application: Objective-See, LLC (VBG97UB4TA)"
/* GLOBALS */
//log handle
extern os_log_t logHandle;
//dispatch source for SIGTERM
dispatch_source_t dispatchSource = nil;
@implementation HelperInterface
//uninstall
// do uninstall logic and return result
-(void)uninstall:(NSString*)app prefs:(NSString*)prefs reply:(void (^)(NSNumber*))reply;
{
//response
BOOL response = NO;
//dbg msg
os_log_debug(logHandle, "XPC-request: uninstall");
//configure
// pass in 'uninstall' flag
if(0 == [self configure:app arguments:@[CMD_UNINSTALL, prefs]])
{
//happy
response = YES;
}
//reply to client
reply([NSNumber numberWithBool:response]);
return;
}
//configure
// install or uninstall
-(int)configure:(NSString*)app arguments:(NSArray*)args
{
//result
int result = -1;
//valdiated (copy) of app
NSString* validatedApp = nil;
//validate app
validatedApp = [self validateApp:app];
if(nil == validatedApp)
{
//err msg
os_log_error(logHandle, "ERROR: failed to validate copy of app");
//bail
goto bail;
}
//dbg msg
os_log_debug(logHandle, "validated %@", app);
//exec script
result = [self execScript:validatedApp arguments:args];
if(noErr != result)
{
//err msg
os_log_error(logHandle, "ERROR: failed to execute config script %@ (%d)", CONF_SCRIPT, result);
//bail
goto bail;
}
//happy
result = 0;
bail:
//always try to remove validated app
if(YES != [[NSFileManager defaultManager] removeItemAtPath:validatedApp error:nil])
{
//err msg
os_log_error(logHandle, "ERROR: failed to remove validated app %@", validatedApp);
//set err
result = -1;
}
return result;
}
//cleanup by removing self
// since system install/launches us as root, client can't directly remove us
-(void)cleanup:(void (^)(NSNumber*))reply
{
//response
__block BOOL response = NO;
//flag
__block BOOL noErrors = YES;
//helper plist
__block NSString* helperPlist = nil;
//binary
__block NSString* helperBinary = nil;
//error
__block NSError* error = nil;
//dbg msg
os_log_debug(logHandle, "XPC-request: cleanup (removing self)");
//ignore sigterm
// handling it via GCD dispatch
signal(SIGTERM, SIG_IGN);
//init dispatch source for SIGTERM
dispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0, dispatch_get_main_queue());
//set handler
// deletes plist and self
dispatch_source_set_event_handler(dispatchSource, ^{
//dbg msg
os_log_debug(logHandle, "XPC: got SIGTERM, deleting plist & self!");
//init path to plist
helperPlist = [@"/Library/LaunchDaemons" stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", CONFIG_HELPER_ID]];
//delete plist
if(YES != [[NSFileManager defaultManager] removeItemAtPath:helperPlist error:&error])
{
//err msg
os_log_error(logHandle, "ERROR: failed to delete %@ (%@)", helperPlist, error.description);
//set error
noErrors = NO;
}
//init path to binary
helperBinary = [@"/Library/PrivilegedHelperTools" stringByAppendingPathComponent:CONFIG_HELPER_ID];
//delete self
if(YES != [[NSFileManager defaultManager] removeItemAtPath:helperBinary error:&error])
{
//err msg
os_log_error(logHandle, "ERROR: failed to delete %@ (%@)", helperBinary, error.description);
//set error
noErrors = NO;
}
//no errors?
// display dbg msg
if(YES == noErrors)
{
//happy
response = YES;
//dbg msg
os_log_debug(logHandle, "removed %@ and %@", helperPlist, helperBinary);
}
//reply to client
reply([NSNumber numberWithBool:response]);
//bye!
exit(SIGTERM);
});
//resume
dispatch_resume(dispatchSource);
//init path to plist
helperPlist = [@"/Library/LaunchDaemons" stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", CONFIG_HELPER_ID]];
//unload
// will trigger sigterm
execTask(LAUNCHCTL, @[@"unload", helperPlist], YES, NO);
return;
}
//make copy of app and validate
-(NSString*)validateApp:(NSString*)app
{
//copy of app
NSString* appCopy = nil;
//file manager
NSFileManager* defaultManager = nil;
//path to (now) validated app
NSString* validatedApp = nil;
//error
NSError* error = nil;
//grab default file manager
defaultManager = [NSFileManager defaultManager];
//init path to app copy
// *root-owned* tmp directory
appCopy = [NSTemporaryDirectory() stringByAppendingPathComponent:app.lastPathComponent];
//dbg msg
os_log_debug(logHandle, "validating %@", appCopy);
//delete if old copy is there
if(YES == [defaultManager fileExistsAtPath:appCopy])
{
//delete
if(YES != [defaultManager removeItemAtPath:appCopy error:&error])
{
//err msg
os_log_error(logHandle, "ERROR: failed to delete %@ (error: %@)", appCopy, error.description);
}
}
//copy app bundle to *root-owned* directory
if(YES != [defaultManager copyItemAtPath:app toPath:appCopy error:&error])
{
//err msg
os_log_error(logHandle, "ERROR: failed to copy %@ to %@ (error: %@)", app, appCopy, error.description);
//bail
goto bail;
}
//set group/owner to root/wheel
if(YES != setFileOwner(appCopy, @0, @0, YES))
{
//err msg
os_log_error(logHandle, "ERROR: failed to set %@ to be owned by root", appCopy);
//bail
goto bail;
}
//verify app
// make sure it's signed, and by our signing auth
if(noErr != verifyApp(appCopy, SIGNING_AUTH))
{
//err msg
os_log_error(logHandle, "ERROR: failed to validate %@", appCopy);
//bail
goto bail;
}
//happy
validatedApp = appCopy;
bail:
return validatedApp;
}
//execute config script
-(int)execScript:(NSString*)validatedApp arguments:(NSArray*)arguments
{
//result
int result = -1;
//results
NSDictionary* results = nil;
//script
NSString* script = nil;
//app bundle
NSBundle* appBundle = nil;
//file manager
NSFileManager* fileManager = nil;
//current working directory
NSString* currentWorkingDir = nil;
//init file manager
fileManager = [NSFileManager defaultManager];
//load app bundle
appBundle = [NSBundle bundleWithPath:validatedApp];
if(nil == appBundle)
{
//err msg
os_log_error(logHandle, "ERROR: failed to load app bundle for %@", validatedApp);
//bail
goto bail;
}
//get path to config script
script = [[appBundle resourcePath] stringByAppendingPathComponent:CONF_SCRIPT];
if(nil == script)
{
//err msg
os_log_error(logHandle, "ERROR: failed to find config script %@", CONF_SCRIPT);
//bail
goto bail;
}
//get current working directory
currentWorkingDir = [fileManager currentDirectoryPath];
//change working directory
// set it to (validated) app path's resources
[fileManager changeCurrentDirectoryPath:[NSString stringWithFormat:@"%@/Contents/Resources/", validatedApp]];
//exec script
// wait, but don't grab output
results = execTask(script, arguments, YES, NO);
//exit code?
if(nil != results[EXIT_CODE])
{
//grab
result = [results[EXIT_CODE] intValue];
}
//(re)set current working directory
[fileManager changeCurrentDirectoryPath:currentWorkingDir];
bail:
return result;
}
@end

View File

@ -0,0 +1,37 @@
//
// file: HelperListener.h
// project: (open-source) installer
// description: XPC listener for connections for user components (header)
//
// created by Patrick Wardle
// copyright (c) 2018 Objective-See. All rights reserved.
//
@import Foundation;
#import "HelperInterface.h"
//function def
OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
@interface HelperListener : NSObject <NSXPCListenerDelegate>
{
}
/* PROPERTIES */
//XPC listener obj
@property(nonatomic, retain)NSXPCListener* listener;
/* METHODS */
//setup XPC listener
-(BOOL)initListener;
//automatically invoked
// allows NSXPCListener to configure/accept/resume a new incoming NSXPCConnection
// note: we only allow binaries signed by Objective-See to talk to this to be extra safe :)
-(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection;
@end

View File

@ -0,0 +1,271 @@
//
// file: HelperListener.m
// project: (open-source) installer
// description: XPC listener for connections for user components
//
// created by Patrick Wardle
// copyright (c) 2018 Objective-See. All rights reserved.
//
@import OSLog;
@import Foundation;
#import "consts.h"
#import "XPCProtocol.h"
#import "HelperListener.h"
#import "HelperInterface.h"
#import <bsm/libbsm.h>
#import <Security/AuthSession.h>
#import <EndpointSecurity/EndpointSecurity.h>
/* GLOBALS */
//log handle
extern os_log_t logHandle;
//interface for 'extension' to NSXPCConnection
// allows us to access the 'private' auditToken iVar
@interface ExtendedNSXPCConnection : NSXPCConnection
//private iVar
@property (nonatomic) audit_token_t auditToken;
@end
//implementation for 'extension' to NSXPCConnection
// allows us to access the 'private' auditToken iVar
@implementation ExtendedNSXPCConnection
//private iVar
@synthesize auditToken;
@end
@implementation HelperListener
@synthesize listener;
//init
// create XPC listener
-(id)init
{
//init super
self = [super init];
if(nil != self)
{
//setup XPC listener
if(YES != [self initListener])
{
//unset
self = nil;
//bail
goto bail;
}
}
bail:
return self;
}
//setup XPC listener
-(BOOL)initListener
{
//result
BOOL result = NO;
//init listener
listener = [[NSXPCListener alloc] initWithMachServiceName:CONFIG_HELPER_ID];
if(nil == self.listener)
{
//err msg
os_log_error(logHandle, "ERROR: failed to create mach service %@", CONFIG_HELPER_ID);
//bail
goto bail;
}
//dbg msg
os_log_debug(logHandle, "created mach service %@", CONFIG_HELPER_ID);
//set delegate
self.listener.delegate = self;
//ready to accept connections
[self.listener resume];
//happy
result = YES;
bail:
return result;
}
#pragma mark -
#pragma mark NSXPCConnection method overrides
//automatically invoked
// allows NSXPCListener to configure/accept/resume a new incoming NSXPCConnection
// shoutout to: https://blog.obdev.at/what-we-have-learned-from-a-vulnerability/
-(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
{
//pragma
#pragma unused(listener)
//flag
BOOL shouldAccept = NO;
//status
OSStatus status = !errSecSuccess;
//audit token
audit_token_t auditToken = {0};
//task ref
SecTaskRef taskRef = 0;
//code ref
SecCodeRef codeRef = NULL;
//code signing info
CFDictionaryRef csInfo = NULL;
//cs flags
uint32_t csFlags = 0;
//signing req string (main app)
NSString* requirement = nil;
//extract audit token
auditToken = ((ExtendedNSXPCConnection*)newConnection).auditToken;
//dbg msg
os_log_debug(logHandle, "received request to connect to XPC interface");
//obtain dynamic code ref
status = SecCodeCopyGuestWithAttributes(NULL, (__bridge CFDictionaryRef _Nullable)(@{(__bridge NSString *)kSecGuestAttributeAudit : [NSData dataWithBytes:&auditToken length:sizeof(audit_token_t)]}), kSecCSDefaultFlags, &codeRef);
if(errSecSuccess != status)
{
//err msg
os_log_error(logHandle, "ERROR: 'SecCodeCopyGuestWithAttributes' failed with': %#x", status);
//bail
goto bail;
}
//validate code
status = SecCodeCheckValidity(codeRef, kSecCSDefaultFlags, NULL);
if(errSecSuccess != status)
{
//err msg
os_log_error(logHandle, "ERROR: 'SecCodeCheckValidity' failed with': %#x", status);
//bail
goto bail;
}
//get code signing info
status = SecCodeCopySigningInformation(codeRef, kSecCSDynamicInformation, &csInfo);
if(errSecSuccess != status)
{
//err msg
os_log_error(logHandle, "ERROR: 'SecCodeCopySigningInformation' failed with': %#x", status);
//bail
goto bail;
}
//extract flags
csFlags = [((__bridge NSDictionary *)csInfo)[(__bridge NSString *)kSecCodeInfoStatus] unsignedIntValue];
//dbg msg
os_log_debug(logHandle, "code signing flags: %#x", csFlags);
//gotta have hardened runtime
if( !(CS_VALID & csFlags) &&
!(CS_RUNTIME & csFlags) )
{
//err msg
os_log_error(logHandle, "ERROR: invalid code signing flags: %#x", csFlags);
//bail
goto bail;
}
//dbg msg
os_log_debug(logHandle, "code signing flags, ok ('CS_RUNTIME' is set)");
//init signing req
requirement = [NSString stringWithFormat:@"anchor apple generic and identifier \"%@\" and certificate leaf [subject.CN] = \"%@\"", INSTALLER_ID, SIGNING_AUTH];
//step 1: create task ref
// uses NSXPCConnection's (private) 'auditToken' iVar
taskRef = SecTaskCreateWithAuditToken(NULL, ((ExtendedNSXPCConnection*)newConnection).auditToken);
if(NULL == taskRef)
{
//bail
goto bail;
}
//step 2: validate
// check that client is signed with Objective-See's dev cert and it's the BB's installer
if(0 != SecTaskValidateForRequirement(taskRef, (__bridge CFStringRef)(requirement)))
{
//err msg
os_log_error(logHandle, "ERROR: failed to validate against %@", requirement);
//bail
goto bail;
}
//set the interface that the exported object implements
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//set object exported by connection
newConnection.exportedObject = [[HelperInterface alloc] init];
//resume
[newConnection resume];
//dbg msg
os_log_debug(logHandle, "allowed XPC connection: %@", newConnection);
//happy
shouldAccept = YES;
bail:
//release task ref object
if(NULL != taskRef)
{
//release
CFRelease(taskRef);
taskRef = NULL;
}
//free cs info
if(NULL != csInfo)
{
//free
CFRelease(csInfo);
csInfo = NULL;
}
//free code ref
if(NULL != codeRef)
{
//free
CFRelease(codeRef);
codeRef = NULL;
}
return shouldAccept;
}
@end

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.objective-see.oversight.uninstallHelper</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>helper</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2020 Objective-See. All rights reserved.</string>
<key>SMAuthorizedClients</key>
<array>
<string>anchor apple generic and identifier &quot;com.objective-see.oversight.installer&quot; and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = VBG97UB4TA)</string>
</array>
</dict>
</plist>

Some files were not shown because too many files have changed in this diff Show More