Co-Occurrence on domain level

This commit is contained in:
relikd
2020-07-02 12:26:07 +02:00
parent 05d06a4f31
commit 4f92d3d58d
10 changed files with 155 additions and 74 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 */,

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

@@ -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

@@ -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