v2.0
(full) rewrite leveraging logging subsystem + M1 native support + execute script on event (#2) + improved Big Sur compatibility/issues (#3, #4, #6, #7)
|
@ -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
|
|
@ -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 */;
|
||||
}
|
|
@ -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>
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -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];
|
||||
|
||||
//not in dark mode?
|
||||
// make window white
|
||||
if(YES != isDarkMode())
|
||||
{
|
||||
//make white
|
||||
[self.window setBackgroundColor: NSColor.whiteColor];
|
||||
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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 158 KiB |
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
@ -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
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 17 KiB |
|
@ -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
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -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
|
||||
}
|
||||
}
|
377
Application/Application/Assets.xcassets/PrefsMode.imageset/prefsMode Any.pdf
vendored
Normal file
378
Application/Application/Assets.xcassets/PrefsMode.imageset/prefsMode Dark.pdf
vendored
Normal file
411
Application/Application/Assets.xcassets/PrefsMode.imageset/prefsMode Light.pdf
vendored
Normal 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
|
||||
}
|
||||
}
|
2548
Application/Application/Assets.xcassets/PrefsUpdate.imageset/prefsUpdate Any.pdf
vendored
Normal file
2550
Application/Application/Assets.xcassets/PrefsUpdate.imageset/prefsUpdate Dark.pdf
vendored
Normal file
2551
Application/Application/Assets.xcassets/PrefsUpdate.imageset/prefsUpdate Light.pdf
vendored
Normal 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"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 225 B |
16
Application/Application/Assets.xcassets/StatusInactive.imageset/Contents.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
BIN
Application/Application/Assets.xcassets/StatusInactive.imageset/inactive.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
|
@ -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
|
||||
}
|
||||
}
|
230
Application/Application/Assets.xcassets/allow.imageset/rulesAllow Light.pdf
vendored
Normal 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
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 <camera|microphone> -event <on|off> -process <pid>
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>
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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"/>
|
||||
<subviews>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="56" horizontalPageScroll="10" verticalLineScroll="56" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gth-To-Lf2">
|
||||
<rect key="frame" x="-1" y="-1" width="613" height="272"/>
|
||||
<clipView key="contentView" id="ubO-Ur-NA2">
|
||||
<rect key="frame" x="1" y="0.0" width="611" height="271"/>
|
||||
<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>
|
||||
<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"/>
|
||||
<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="50" width="1002" height="409"/>
|
||||
<clipView key="contentView" id="ubO-Ur-NA2">
|
||||
<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" 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>
|
|
@ -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
|
|
@ -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
|
|
@ -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 & 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>
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
||||
{
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
|||
github "getsentry/sentry-cocoa" "6.0.0"
|
|
@ -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 */;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:WhatsYourSign.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 "com.objective-see.oversight.installer" 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>
|