4 Commits

Author SHA1 Message Date
relikd
88a52fb92c Version 1.0.0 (24) 2020-07-02 14:12:26 +02:00
relikd
723f1665a7 fittingSize() 2020-07-02 12:26:34 +02:00
relikd
4f92d3d58d Co-Occurrence on domain level 2020-07-02 12:26:07 +02:00
relikd
05d06a4f31 Update readme 2020-07-01 13:31:29 +02:00
14 changed files with 183 additions and 92 deletions

View File

@@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
5404AEEB24A90717003B2F54 /* PrefsShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E67E4524A8B0FE0025D261 /* PrefsShared.swift */; }; 5404AEEB24A90717003B2F54 /* PrefsShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E67E4524A8B0FE0025D261 /* PrefsShared.swift */; };
5404AEED24A95F3F003B2F54 /* SlideInAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5404AEEC24A95F3F003B2F54 /* SlideInAnimation.swift */; }; 5404AEED24A95F3F003B2F54 /* SlideInAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5404AEEC24A95F3F003B2F54 /* SlideInAnimation.swift */; };
5404AEEF24ACC089003B2F54 /* VCAnalysisBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5404AEEE24ACC089003B2F54 /* VCAnalysisBar.swift */; };
540E6780242D2CF100871BBE /* VCRecordings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E677F242D2CF100871BBE /* VCRecordings.swift */; }; 540E6780242D2CF100871BBE /* VCRecordings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E677F242D2CF100871BBE /* VCRecordings.swift */; };
540E67822433483D00871BBE /* VCEditRecording.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67812433483D00871BBE /* VCEditRecording.swift */; }; 540E67822433483D00871BBE /* VCEditRecording.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67812433483D00871BBE /* VCEditRecording.swift */; };
540E67842433FAFE00871BBE /* TVCPreviousRecords.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */; }; 540E67842433FAFE00871BBE /* TVCPreviousRecords.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */; };
@@ -176,6 +177,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
5404AEEC24A95F3F003B2F54 /* SlideInAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideInAnimation.swift; sourceTree = "<group>"; }; 5404AEEC24A95F3F003B2F54 /* SlideInAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideInAnimation.swift; sourceTree = "<group>"; };
5404AEEE24ACC089003B2F54 /* VCAnalysisBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCAnalysisBar.swift; sourceTree = "<group>"; };
540E677F242D2CF100871BBE /* VCRecordings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCRecordings.swift; sourceTree = "<group>"; }; 540E677F242D2CF100871BBE /* VCRecordings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCRecordings.swift; sourceTree = "<group>"; };
540E67812433483D00871BBE /* VCEditRecording.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCEditRecording.swift; sourceTree = "<group>"; }; 540E67812433483D00871BBE /* VCEditRecording.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCEditRecording.swift; sourceTree = "<group>"; };
540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCPreviousRecords.swift; sourceTree = "<group>"; }; 540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCPreviousRecords.swift; sourceTree = "<group>"; };
@@ -347,7 +349,8 @@
5412F8ED24571B8100A63D7A /* VCDateFilter.swift */, 5412F8ED24571B8100A63D7A /* VCDateFilter.swift */,
54953E6023E0D69A0054345C /* TVCHosts.swift */, 54953E6023E0D69A0054345C /* TVCHosts.swift */,
54953E6E23E44CD00054345C /* TVCHostDetails.swift */, 54953E6E23E44CD00054345C /* TVCHostDetails.swift */,
541FC47424A12CE9009154D8 /* Analytics */, 544F911F24A67EC5001D4B00 /* TVCOccurrenceContext.swift */,
541FC47424A12CE9009154D8 /* Analysis */,
); );
path = Requests; path = Requests;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -415,13 +418,13 @@
path = main; path = main;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
541FC47424A12CE9009154D8 /* Analytics */ = { 541FC47424A12CE9009154D8 /* Analysis */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5404AEEE24ACC089003B2F54 /* VCAnalysisBar.swift */,
541FC47724A1453F009154D8 /* VCCoOccurrence.swift */, 541FC47724A1453F009154D8 /* VCCoOccurrence.swift */,
544F911F24A67EC5001D4B00 /* TVCOccurrenceContext.swift */,
); );
path = Analytics; path = Analysis;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
542E2A9B24051F79001462DC /* media */ = { 542E2A9B24051F79001462DC /* media */ = {
@@ -860,6 +863,7 @@
54E540F8247DB90F00F7C34A /* RecordingsDB.swift in Sources */, 54E540F8247DB90F00F7C34A /* RecordingsDB.swift in Sources */,
54E67E4F24A8E2910025D261 /* Equatable.swift in Sources */, 54E67E4F24A8E2910025D261 /* Equatable.swift in Sources */,
54E540F4247D3F2600F7C34A /* TestDataSource.swift in Sources */, 54E540F4247D3F2600F7C34A /* TestDataSource.swift in Sources */,
5404AEEF24ACC089003B2F54 /* VCAnalysisBar.swift in Sources */,
545DDDD424466D37003B6544 /* AutoLayout.swift in Sources */, 545DDDD424466D37003B6544 /* AutoLayout.swift in Sources */,
54B345AD241BBB00004C53CC /* DBExtensions.swift in Sources */, 54B345AD241BBB00004C53CC /* DBExtensions.swift in Sources */,
54E540F2247C423200F7C34A /* DomainFilter.swift in Sources */, 54E540F2247C423200F7C34A /* DomainFilter.swift in Sources */,
@@ -1157,7 +1161,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = main/main.entitlements; CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
CURRENT_PROJECT_VERSION = 23; CURRENT_PROJECT_VERSION = 24;
INFOPLIST_FILE = main/Info.plist; INFOPLIST_FILE = main/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -1176,7 +1180,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = main/main.entitlements; CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
CURRENT_PROJECT_VERSION = 23; CURRENT_PROJECT_VERSION = 24;
INFOPLIST_FILE = main/Info.plist; INFOPLIST_FILE = main/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -1195,7 +1199,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements; CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 23; CURRENT_PROJECT_VERSION = 24;
INFOPLIST_FILE = GlassVPN/Info.plist; INFOPLIST_FILE = GlassVPN/Info.plist;
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN"; PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";
@@ -1213,7 +1217,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements; CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 23; CURRENT_PROJECT_VERSION = 24;
INFOPLIST_FILE = GlassVPN/Info.plist; INFOPLIST_FILE = GlassVPN/Info.plist;
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN"; PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";

View File

@@ -16,6 +16,8 @@ Your data belongs to you.
Therefore, monitoring and analysis take place on your device only. Therefore, monitoring and analysis take place on your device only.
The app does not share any data with us or any other third-party unless you choose to. The app does not share any data with us or any other third-party unless you choose to.
Join [Testflight beta](https://testflight.apple.com/join/9jjaFeHO)
### How does it work? ### How does it work?
@@ -31,19 +33,23 @@ That means, AppCheck does not have to be active in the foreground all the time.
- See history of previous connections - See history of previous connections
- Block unwanted traffic based on domain names - Block unwanted traffic based on domain names
- Record app specific activity<sup>1</sup> - Record app specific activity<sup>1</sup>
- Apply logging filters - Apply logging filters (block or ignore) and display filters (specific range or last x minutes)
- Sort results by time, name, or occurrence count
- Context Analysis
- What other domains occur often at the same time?
- What happened immediately before or after the action?
- Export results for custom analysis
**… and soon:** **… and soon:**
- Alert Monitor & reminder - Alert Monitor & reminder
- Occurrence Context Analysis
- Participate in privacy research - Participate in privacy research
<sup>1</sup> Due to technical limitations, recording is not limited to any single application. Remember to force-quit all other applications before starting a recording. <sup>1</sup> Due to technical limitations, recordings can not be restricted to a single application. Remember to force-quit all other applications before starting a recording.
## Research Project ## Research Project
*information will be added soon* *information will be added soon*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 158 KiB

View File

@@ -344,9 +344,16 @@
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/> <rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<containerView key="tableHeaderView" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kh4-PQ-hy6">
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<connections>
<segue destination="1ba-SA-8sT" kind="embed" id="vf1-07-AS4"/>
</connections>
</containerView>
<prototypes> <prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="HostCell" textLabel="Rnk-SP-UHm" detailTextLabel="ovQ-lJ-hWJ" style="IBUITableViewCellStyleSubtitle" id="uv0-9B-Zbb"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="HostCell" textLabel="Rnk-SP-UHm" detailTextLabel="ovQ-lJ-hWJ" style="IBUITableViewCellStyleSubtitle" id="uv0-9B-Zbb">
<rect key="frame" x="0.0" y="28" width="320" height="57.5"/> <rect key="frame" x="0.0" y="77" width="320" height="57.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uv0-9B-Zbb" id="6vH-Du-gCg"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uv0-9B-Zbb" id="6vH-Du-gCg">
<rect key="frame" x="0.0" y="0.0" width="293" height="57.5"/> <rect key="frame" x="0.0" y="0.0" width="293" height="57.5"/>
@@ -392,17 +399,13 @@
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/> <rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<tabBar key="tableHeaderView" contentMode="scaleToFill" fixedFrame="YES" translucent="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1Jy-zg-CXR"> <containerView key="tableHeaderView" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SxM-2c-aJb">
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/> <rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<items>
<tabBarItem title="Co-Occurrence" image="intersection" id="KXh-kQ-rAF"/>
</items>
<connections> <connections>
<outlet property="delegate" destination="h7Z-Qr-pJ5" id="qNN-nI-Kub"/> <segue destination="1ba-SA-8sT" kind="embed" id="ueN-6L-cP7"/>
</connections> </connections>
</tabBar> </containerView>
<prototypes> <prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="HostDetailCell" textLabel="J2P-mU-Vad" detailTextLabel="eWb-mX-udN" style="IBUITableViewCellStyleValue1" id="ZCA-Dz-i92"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="HostDetailCell" textLabel="J2P-mU-Vad" detailTextLabel="eWb-mX-udN" style="IBUITableViewCellStyleValue1" id="ZCA-Dz-i92">
<rect key="frame" x="0.0" y="77" width="320" height="43.5"/> <rect key="frame" x="0.0" y="77" width="320" height="43.5"/>
@@ -438,25 +441,52 @@
</connections> </connections>
</tableView> </tableView>
<navigationItem key="navigationItem" title="Occurrences" prompt="com.domain.network.cdn" id="bys-2u-rHs"/> <navigationItem key="navigationItem" title="Occurrences" prompt="com.domain.network.cdn" id="bys-2u-rHs"/>
<connections>
<outlet property="actionsBar" destination="1Jy-zg-CXR" id="7x3-Vy-i9C"/>
<segue destination="W5Q-oz-bFb" kind="modal" identifier="segueAnalysisCoOccurrence" id="ukY-Dy-AIA"/>
</connections>
</tableViewController> </tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="UxH-PH-KQy" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="UxH-PH-KQy" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="2100" y="-1250"/> <point key="canvasLocation" x="2100" y="-1250"/>
</scene> </scene>
<!--Analysis Bar-->
<scene sceneID="1qq-WD-Lqq">
<objects>
<viewController id="1ba-SA-8sT" customClass="VCAnalysisBar" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="qp6-er-N6U">
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tabBar contentMode="scaleToFill" translucent="NO" id="1Jy-zg-CXR">
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<items>
<tabBarItem title="Co-Occurrence" image="intersection" id="KXh-kQ-rAF"/>
</items>
<connections>
<outlet property="delegate" destination="1ba-SA-8sT" id="bRS-kh-dOv"/>
</connections>
</tabBar>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="dtz-KG-P4C"/>
</view>
<connections>
<outlet property="tabBar" destination="1Jy-zg-CXR" id="VTV-xq-Aou"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="XnK-B9-RSJ" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1400" y="-1950"/>
</scene>
<!--Co Occurrence--> <!--Co Occurrence-->
<scene sceneID="Gbm-AP-b72"> <scene sceneID="Gbm-AP-b72">
<objects> <objects>
<viewController id="W5Q-oz-bFb" customClass="VCCoOccurrence" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController"> <viewController storyboardIdentifier="IBCoOccurrence" id="W5Q-oz-bFb" customClass="VCCoOccurrence" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="f34-NO-d8f"> <view key="view" contentMode="scaleToFill" id="f34-NO-d8f">
<rect key="frame" x="0.0" y="0.0" width="320" height="548"/> <rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rvt-nC-2Zr"> <navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rvt-nC-2Zr">
<rect key="frame" x="0.0" y="0.0" width="320" height="56"/> <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<items> <items>
<navigationItem title="Co-Occurrence" id="csY-x8-Rpe"> <navigationItem title="Co-Occurrence" id="csY-x8-Rpe">
<barButtonItem key="leftBarButtonItem" systemItem="done" id="eg9-p3-Xas"> <barButtonItem key="leftBarButtonItem" systemItem="done" id="eg9-p3-Xas">
@@ -466,7 +496,7 @@
</barButtonItem> </barButtonItem>
<barButtonItem key="rightBarButtonItem" id="bTi-7F-CFS"> <barButtonItem key="rightBarButtonItem" id="bTi-7F-CFS">
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="infoLight" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" id="kqK-SL-CxZ"> <button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="infoLight" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" id="kqK-SL-CxZ">
<rect key="frame" x="279" y="16" width="25" height="24"/> <rect key="frame" x="279" y="10" width="25" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<connections> <connections>
<action selector="showInfoScreen" destination="W5Q-oz-bFb" eventType="touchUpInside" id="TuI-R9-PNr"/> <action selector="showInfoScreen" destination="W5Q-oz-bFb" eventType="touchUpInside" id="TuI-R9-PNr"/>
@@ -477,7 +507,7 @@
</items> </items>
</navigationBar> </navigationBar>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="PGb-pB-cfO"> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="PGb-pB-cfO">
<rect key="frame" x="0.0" y="56" width="320" height="492"/> <rect key="frame" x="0.0" y="44" width="320" height="524"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<segmentedControl key="tableHeaderView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="7ye-tU-pdo"> <segmentedControl key="tableHeaderView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="7ye-tU-pdo">
<rect key="frame" x="0.0" y="0.0" width="320" height="32"/> <rect key="frame" x="0.0" y="0.0" width="320" height="32"/>
@@ -1376,6 +1406,7 @@ Duration: 60:00</string>
</scenes> </scenes>
<inferredMetricsTieBreakers> <inferredMetricsTieBreakers>
<segue reference="EzT-Xq-wka"/> <segue reference="EzT-Xq-wka"/>
<segue reference="ueN-6L-cP7"/>
</inferredMetricsTieBreakers> </inferredMetricsTieBreakers>
<resources> <resources>
<image name="filter-clear" width="20" height="20"/> <image name="filter-clear" width="20" height="20"/>

View File

@@ -33,6 +33,13 @@ class SearchBarManager: NSObject, UISearchResultsUpdating {
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
tvc?.navigationItem.searchController = controller tvc?.navigationItem.searchController = controller
} else { } else {
let thv = tvc?.tableView.tableHeaderView
guard thv == nil || thv is UISearchBar else {
// Don't overwrite actions bar (co-occurrence, etc.)
// FIXME: find alternative or iOS 9-10 users can't search in hosts
tvc = nil
return
}
controller.loadViewIfNeeded() // Fix: "Attempting to load the view of a view controller while it is deallocating" controller.loadViewIfNeeded() // Fix: "Attempting to load the view of a view controller while it is deallocating"
tvc?.definesPresentationContext = true // make search bar disappear if user changes scene (eg. select cell) tvc?.definesPresentationContext = true // make search bar disappear if user changes scene (eg. select cell)
//tvc?.tableView.backgroundView = UIView() // iOS 11+ bug: bright white background in dark mode //tvc?.tableView.backgroundView = UIView() // iOS 11+ bug: bright white background in dark mode
@@ -42,7 +49,7 @@ class SearchBarManager: NSObject, UISearchResultsUpdating {
} }
/// Search callback /// Search callback
func updateSearchResults(for controller: UISearchController) { internal func updateSearchResults(for controller: UISearchController) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(performSearch), object: nil) NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(performSearch), object: nil)
perform(#selector(performSearch), with: nil, afterDelay: 0.2) perform(#selector(performSearch), with: nil, afterDelay: 0.2)
} }

View File

@@ -172,18 +172,10 @@ private class StickyPresentationController: UIPresentationController {
let preferred = presentedViewController.preferredContentSize let preferred = presentedViewController.preferredContentSize
switch stickTo { switch stickTo {
case .left, .right: case .left, .right:
let fitted = target.systemLayoutSizeFitting( let fitted = target.fittingSize(fixedHeight: full.height, preferredWidth: preferred.width)
CGSize(width: preferred.width, height: full.height),
withHorizontalFittingPriority: .fittingSizeLevel,
verticalFittingPriority: .required
)
return CGSize(width: min(fitted.width, full.width), height: full.height) return CGSize(width: min(fitted.width, full.width), height: full.height)
case .top, .bottom: case .top, .bottom:
let fitted = target.systemLayoutSizeFitting( let fitted = target.fittingSize(fixedWidth: full.width, preferredHeight: preferred.height)
CGSize(width: full.width, height: preferred.height),
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel
)
return CGSize(width: full.width, height: min(fitted.height, full.height)) return CGSize(width: full.width, height: min(fitted.height, full.height))
} }
} }

View File

@@ -244,8 +244,9 @@ extension SQLiteDatabase {
} }
/// Get sorted, unique list of `ts` with given `fqdn`. /// Get sorted, unique list of `ts` with given `fqdn`.
func dnsLogsUniqTs(_ fqdn: String) -> [Timestamp]? { func dnsLogsUniqTs(_ domain: String, isFQDN flag: Bool) -> [Timestamp]? {
try? run(sql: "SELECT DISTINCT ts FROM heap WHERE fqdn = ? ORDER BY ts;", bind: [BindText(fqdn)]) { try? run(sql: "SELECT DISTINCT ts FROM heap WHERE \(flag ? "fqdn" : "domain") = ? ORDER BY ts;",
bind: [BindText(domain)]) {
allRows($0) { col_ts($0, 0) } allRows($0) { col_ts($0, 0) }
} }
} }
@@ -257,7 +258,7 @@ extension SQLiteDatabase {
/// - dt: Search for `ts - dt <= X <= ts + dt` /// - dt: Search for `ts - dt <= X <= ts + dt`
/// - fqdn: Rows matching this domain will be excluded from the result set. /// - fqdn: Rows matching this domain will be excluded from the result set.
/// - Returns: List of tuples ordered by rank (ASC). /// - Returns: List of tuples ordered by rank (ASC).
func contextAnalysis(coOccurrence times: [Timestamp], plusMinus dt: Timestamp, exclude fqdn: String) -> [ContextAnalysisResult]? { func contextAnalysis(coOccurrence times: [Timestamp], plusMinus dt: Timestamp, exclude domain: String, isFQDN flag: Bool) -> [ContextAnalysisResult]? {
guard times.count > 0 else { return nil } guard times.count > 0 else { return nil }
createFunction("fnDist") { createFunction("fnDist") {
let x = $0.first as! Timestamp let x = $0.first as! Timestamp
@@ -282,10 +283,10 @@ extension SQLiteDatabase {
SELECT fqdn, count, avg, (\(fnRank)) rank FROM ( SELECT fqdn, count, avg, (\(fnRank)) rank FROM (
SELECT fqdn, COUNT(*) count, AVG(dist) avg FROM ( SELECT fqdn, COUNT(*) count, AVG(dist) avg FROM (
SELECT fqdn, fnDist(ts) dist FROM heap SELECT fqdn, fnDist(ts) dist FROM heap
WHERE ts BETWEEN ? AND ? AND fqdn != ? AND dist <= ? WHERE ts BETWEEN ? AND ? AND \(flag ? "fqdn" : "domain") != ? AND dist <= ?
) GROUP BY fqdn ) GROUP BY fqdn
) ORDER BY rank ASC LIMIT 99; ) ORDER BY rank ASC LIMIT 99;
""", bind: [BindInt64(dt), BindInt64(low), BindInt64(high), BindText(fqdn), BindInt64(dt)]) { """, bind: [BindInt64(dt), BindInt64(low), BindInt64(high), BindText(domain), BindInt64(dt)]) {
allRows($0) { allRows($0) {
(col_text($0, 0) ?? "", sqlite3_column_int($0, 1), sqlite3_column_double($0, 2), sqlite3_column_double($0, 3)) (col_text($0, 0) ?? "", sqlite3_column_int($0, 1), sqlite3_column_double($0, 2), sqlite3_column_double($0, 3))
} }

View File

@@ -43,14 +43,6 @@ extension UITableView {
func safeMoveRow(_ from: Int, to: Int) { func safeMoveRow(_ from: Int, to: Int) {
isFrontmost ? moveRow(at: IndexPath(row: from), to: IndexPath(row: to)) : reloadData() isFrontmost ? moveRow(at: IndexPath(row: from), to: IndexPath(row: to)) : reloadData()
} }
/// Recalculate and apply new `tableHeaderView` height.
func sizeHeaderToFit() {
if let head = tableHeaderView {
head.frame.size.height = head.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
tableHeaderView = head
}
}
} }

View File

@@ -17,6 +17,18 @@ extension UIView {
return UIImage(cgImage: image!.cgImage!) return UIImage(cgImage: image!.cgImage!)
} }
} }
/// Find size that fits into frame with given `width` as precondition.
/// - Parameter preferredHeight:If unset, find smallest possible size.
func fittingSize(fixedWidth: CGFloat, preferredHeight: CGFloat = 0) -> CGSize {
systemLayoutSizeFitting(CGSize(width: fixedWidth, height: preferredHeight), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
}
/// Find size that fits into frame with given `height` as precondition.
/// - Parameter preferredWidth:If unset, find smallest possible size.
func fittingSize(fixedHeight: CGFloat, preferredWidth: CGFloat = 0) -> CGSize {
systemLayoutSizeFitting(CGSize(width: preferredWidth, height: fixedHeight), withHorizontalFittingPriority: .fittingSizeLevel, verticalFittingPriority: .required)
}
} }
extension UIEdgeInsets { extension UIEdgeInsets {

View File

@@ -0,0 +1,60 @@
import UIKit
protocol AnalysisBarDelegate {
func analysisBarWillOpenCoOccurrence() -> (domain: String, isFQDN: Bool)
}
class VCAnalysisBar: UIViewController, UITabBarDelegate {
@IBOutlet private var tabBar: UITabBar!
override func viewDidLoad() {
if #available(iOS 10.0, *) {
tabBar.unselectedItemTintColor = .sysLink
}
super.viewDidLoad()
}
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
let enabled = (parent as? AnalysisBarDelegate) != nil
for item in tabBar.items! { item.isEnabled = enabled }
}
// MARK: - Tab Bar Appearance
override func viewWillAppear(_: Bool) {
resizeTableViewHeader()
}
override func traitCollectionDidChange(_: UITraitCollection?) {
resizeTableViewHeader()
}
func resizeTableViewHeader() {
guard let tableView = (parent as? UITableViewController)?.tableView,
let head = tableView.tableHeaderView else { return }
// Recalculate and apply new height. Otherwise tabBar won't compress
tabBar.sizeToFit()
head.frame.size.height = tabBar.frame.height
tableView.tableHeaderView = head
}
// MARK: - Tab Bar Delegate
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
tabBar.selectedItem = nil
openCoOccurrence()
}
private func openCoOccurrence() {
guard let delegate = parent as? AnalysisBarDelegate,
let vc = storyboard?.instantiateViewController(withIdentifier: "IBCoOccurrence") as? VCCoOccurrence else {
return
}
let info = delegate.analysisBarWillOpenCoOccurrence()
vc.domainName = info.domain
vc.isFQDN = info.isFQDN
present(vc, animated: true)
}
}

View File

@@ -1,7 +1,8 @@
import UIKit import UIKit
class VCCoOccurrence: UIViewController, UITableViewDataSource { class VCCoOccurrence: UIViewController, UITableViewDataSource {
var fqdn: String! var domainName: String!
var isFQDN: Bool!
private var dataSource: [ContextAnalysisResult] = [] private var dataSource: [ContextAnalysisResult] = []
@IBOutlet private var tableView: UITableView! @IBOutlet private var tableView: UITableView!
@@ -30,14 +31,15 @@ class VCCoOccurrence: UIViewController, UITableViewDataSource {
dataSource = [("Loading …", 0, 0, 0)] dataSource = [("Loading …", 0, 0, 0)]
logMaxCount = 1 logMaxCount = 1
tableView.reloadData() tableView.reloadData()
let domain = fqdn! let domain = domainName!
let flag = isFQDN!
let time = Timestamp(selectedTime) let time = Timestamp(selectedTime)
DispatchQueue.global().async { [weak self] in DispatchQueue.global().async { [weak self] in
let temp: [ContextAnalysisResult] let temp: [ContextAnalysisResult]
let total: Int32 let total: Int32
if let db = AppDB, if let db = AppDB,
let times = db.dnsLogsUniqTs(domain), times.count > 0, let times = db.dnsLogsUniqTs(domain, isFQDN: flag), times.count > 0,
let result = db.contextAnalysis(coOccurrence: times, plusMinus: time, exclude: domain), let result = db.contextAnalysis(coOccurrence: times, plusMinus: time, exclude: domain, isFQDN: flag),
result.count > 0 result.count > 0
{ {
temp = result temp = result

View File

@@ -1,8 +1,6 @@
import UIKit import UIKit
class TVCHostDetails: UITableViewController, SyncUpdateDelegate, UITabBarDelegate { class TVCHostDetails: UITableViewController, SyncUpdateDelegate, AnalysisBarDelegate {
@IBOutlet private var actionsBar: UITabBar!
public var fullDomain: String! public var fullDomain: String!
private var dataSource: [GroupedTsOccurrence] = [] private var dataSource: [GroupedTsOccurrence] = []
@@ -14,9 +12,19 @@ class TVCHostDetails: UITableViewController, SyncUpdateDelegate, UITabBarDelegat
sync.addObserver(self) // calls `syncUpdate(reset:)` sync.addObserver(self) // calls `syncUpdate(reset:)`
if #available(iOS 10.0, *) { if #available(iOS 10.0, *) {
sync.allowPullToRefresh(onTVC: self, forObserver: self) sync.allowPullToRefresh(onTVC: self, forObserver: self)
actionsBar.unselectedItemTintColor = .sysLink
} }
UIDevice.orientationDidChangeNotification.observe(call: #selector(didChangeOrientation), on: self) }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let index = tableView.indexPathForSelectedRow?.row {
let tvc = segue.destination as? TVCOccurrenceContext
tvc?.domain = fullDomain
tvc?.ts = dataSource[index].ts
}
}
func analysisBarWillOpenCoOccurrence() -> (domain: String, isFQDN: Bool) {
(fullDomain, true)
} }
// MARK: - Table View Data Source // MARK: - Table View Data Source
@@ -33,34 +41,6 @@ class TVCHostDetails: UITableViewController, SyncUpdateDelegate, UITabBarDelegat
} }
} }
// #########################
// #
// # MARK: - Tab Bar
// #
// #########################
extension TVCHostDetails {
@objc private func didChangeOrientation(_ sender: Notification) {
tableView.sizeHeaderToFit() // otherwise TabBar won't compress
}
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
tabBar.selectedItem = nil
performSegue(withIdentifier: "segueAnalysisCoOccurrence", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueAnalysisCoOccurrence" {
(segue.destination as? VCCoOccurrence)?.fqdn = fullDomain
} else if let index = tableView.indexPathForSelectedRow?.row {
let tvc = segue.destination as? TVCOccurrenceContext
tvc?.domain = fullDomain
tvc?.ts = dataSource[index].ts
}
}
}
// ################################ // ################################
// # // #
// # MARK: - Partial Update // # MARK: - Partial Update

View File

@@ -1,6 +1,6 @@
import UIKit import UIKit
class TVCHosts: UITableViewController, GroupedDomainDataSourceDelegate { class TVCHosts: UITableViewController, GroupedDomainDataSourceDelegate, AnalysisBarDelegate {
lazy var source = GroupedDomainDataSource(withParent: parentDomain) lazy var source = GroupedDomainDataSource(withParent: parentDomain)
@@ -21,6 +21,10 @@ class TVCHosts: UITableViewController, GroupedDomainDataSourceDelegate {
} }
} }
func analysisBarWillOpenCoOccurrence() -> (domain: String, isFQDN: Bool) {
(parentDomain, false)
}
// MARK: - Table View Data Source // MARK: - Table View Data Source