18 Commits

Author SHA1 Message Date
relikd
d68e4ec869 Version 1.0.0 (33) 2020-09-14 12:56:30 +02:00
relikd
762263bfbd Default disconnect swdc + pre-check connect message 2020-09-14 12:56:06 +02:00
relikd
b1cddc796e Version 1.0.0 (32) 2020-09-14 11:52:21 +02:00
relikd
77e20f31f5 Persist recording logs in background 2020-09-14 11:51:44 +02:00
relikd
0175f5390e Fix crash trying to access userInfo 2020-09-13 12:13:14 +02:00
relikd
effc305b86 Version 1.0.0 (31) 2020-09-12 22:28:32 +02:00
relikd
c1fe258b0d Force disconnect to prevent domain spamming (optional in advanced settings) 2020-09-12 22:28:11 +02:00
relikd
36a8f0b97b Version 1.0.0 (30) 2020-09-12 11:35:11 +02:00
relikd
33b9cab8a8 Indicate background recording needs more time 2020-09-12 11:32:06 +02:00
relikd
b88874b38b Version 1.0.0 (29) 2020-09-12 10:57:20 +02:00
relikd
f55f3ea32d Disable copy menu on meta cells in 5 min context 2020-09-12 10:42:37 +02:00
relikd
c843bd76a2 Share notes opt-out, assuming notes are created for upload anyway 2020-09-12 10:31:13 +02:00
relikd
4dd2339ed8 Set recording segment color to indicate tap action 2020-09-12 10:27:04 +02:00
relikd
280526bef4 Hide filter button if new recording 2020-09-12 10:04:24 +02:00
relikd
34caffd4a7 Change tutorial text about app recording length 2020-09-12 09:57:53 +02:00
relikd
9e19b457e2 AppStore search: sort local apps case independent 2020-09-11 15:39:23 +02:00
relikd
e6846953b7 Copy upload key to clipboard 2020-09-08 18:35:30 +02:00
relikd
6d78aeac7b Fix header banner display issues 2020-09-08 18:16:21 +02:00
19 changed files with 208 additions and 42 deletions

View File

@@ -1397,7 +1397,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
CURRENT_PROJECT_VERSION = 28;
CURRENT_PROJECT_VERSION = 33;
INFOPLIST_FILE = main/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -1416,7 +1416,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
CURRENT_PROJECT_VERSION = 28;
CURRENT_PROJECT_VERSION = 33;
INFOPLIST_FILE = main/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -1435,7 +1435,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 28;
CURRENT_PROJECT_VERSION = 33;
INFOPLIST_FILE = GlassVPN/Info.plist;
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";
@@ -1453,7 +1453,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 28;
CURRENT_PROJECT_VERSION = 33;
INFOPLIST_FILE = GlassVPN/Info.plist;
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";

View File

@@ -1,5 +1,7 @@
import NetworkExtension
let connectMessage: Data = "CONNECT".data(using: .ascii)!
let swcdUserAgent: Data = "User-Agent: swcd".data(using: .ascii)!
fileprivate var hook : GlassVPNHook!
// MARK: ObserverFactory
@@ -15,8 +17,20 @@ class LDObserverFactory: ObserverFactory {
override func signal(_ event: ProxySocketEvent) {
switch event {
case .receivedRequest(let session, let socket):
if socket.isCancelled ||
(hook.forceDisconnectUnresolvable && session.ipAddress.isEmpty) {
hook.silentlyPrevented(session.host)
socket.forceDisconnect()
return
}
let kill = hook.processDNSRequest(session.host)
if kill { socket.forceDisconnect() }
case .readData(let data, on: let socket):
if hook.forceDisconnectSWCD,
data.starts(with: connectMessage),
data.range(of: swcdUserAgent) != nil {
socket.disconnect()
}
default:
break
}

View File

@@ -53,3 +53,5 @@ That means, AppCheck does not have to be active in the foreground all the time.
*information will be added soon™*
For now, go to the results page at [https://appchk.de/](https://appchk.de/).
Btw. we are searching for [help](https://appchk.de/help/) on our ongoing research project.

View File

@@ -30,14 +30,16 @@ struct NotificationBanner {
img.tintColor = fg
view.addSubview(lbl)
view.addSubview(img)
img.anchor([.centerY], to: view.layoutMarginsGuide)
lbl.anchor([.top, .bottom, .trailing], to: view.layoutMarginsGuide)
img.anchor([.centerY], to: lbl)
lbl.anchor([.bottom, .trailing], to: view.layoutMarginsGuide)
img.widthAnchor =&= 25
img.heightAnchor =&= 25
if #available(iOS 11, *) {
img.leadingAnchor =&= view.layoutMarginsGuide.leadingAnchor
lbl.topAnchor =&= view.layoutMarginsGuide.topAnchor
} else {
img.leadingAnchor =&= view.leadingAnchor + 8
lbl.topAnchor =&= view.topAnchor + 8
}
lbl.leadingAnchor =&= img.trailingAnchor + 8
img.bottomAnchor =<= view.bottomAnchor - 8 | .init(rawValue: 999)

View File

@@ -12,6 +12,7 @@ enum PrefsShared {
static func registerDefaults() {
suite.register(defaults: [
"ForceDisconnectSWCD" : true,
"RestartReminderEnabled" : true,
"RestartReminderWithText" : true,
"RestartReminderWithBadge" : true,
@@ -37,6 +38,14 @@ extension PrefsShared {
get { CurrentRecordingState(rawValue: Int("CurrentlyRecording")) ?? .Off }
set { Int("CurrentlyRecording", newValue.rawValue) }
}
static var ForceDisconnectUnresolvableDNS: Bool {
get { PrefsShared.Bool("ForceDisconnectUnresolvableDNS") }
set { PrefsShared.Bool("ForceDisconnectUnresolvableDNS", newValue) }
}
static var ForceDisconnectSWCD: Bool {
get { PrefsShared.Bool("ForceDisconnectSWCD") }
set { PrefsShared.Bool("ForceDisconnectSWCD", newValue) }
}
}

View File

@@ -405,7 +405,7 @@ extension SQLiteDatabase {
}
func appBundleList() -> [AppBundleInfo]? {
try? run(sql: "SELECT appid, title, subtitle FROM rec WHERE appid IS NOT NULL GROUP BY appid ORDER BY title ASC;") {
try? run(sql: "SELECT appid, title, subtitle FROM rec WHERE appid IS NOT NULL GROUP BY appid ORDER BY lower(title) ASC;") {
allRows($0) {
AppBundleInfo(col_text($0, 0)!, col_text($0, 1), col_text($0, 2))
}

View File

@@ -33,11 +33,13 @@ extension FilterOptions {
}
extension Recording {
static let minTimeLongTerm: Timestamp = .hours(1)
var fallbackTitle: String { get {
isLongTerm ? "Background Recording" : "Unnamed Recording #\(id)"
} }
var duration: Timestamp { get { (stop ?? .now()) - start } }
var isLongTerm: Bool { duration > Timestamp.hours(1) }
var isLongTerm: Bool { duration > Recording.minTimeLongTerm }
var isShared: Bool { uploadkey?.count ?? 0 > 0}
}

View File

@@ -87,14 +87,21 @@ struct TimeFormat {
/// Duration string with format `mm:ss` or `mm:ss.SSS`
static func from(_ duration: TimeInterval, millis: Bool = false, hours: Bool = false) -> String {
let t = Int(duration)
let min = t / 60
let sec = t % 60
var t = Int(duration)
var min = t / 60
var sec = t % 60
if millis {
let mil = Int(duration * 1000) % 1000
return String(format: "%02d:%02d.%03d", min, sec, mil)
} else if hours {
return String(format: "%02d:%02d:%02d", min / 60, min % 60, sec)
if t < Recording.minTimeLongTerm {
t = Int(Recording.minTimeLongTerm) - t
min = t / 60
sec = t % 60
return String(format: "-%02d:%02d:%02d", min / 60, min % 60, sec)
} else {
return String(format: "%02d:%02d:%02d", min / 60, min % 60, sec)
}
}
return String(format: "%02d:%02d", min, sec)
}

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="hm5-7q-Zfi">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="hm5-7q-Zfi">
<device id="retina4_0" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
@@ -128,6 +128,7 @@
<outlet property="buttonView" destination="La3-9e-6TK" id="UMg-xx-6OV"/>
<outlet property="headerView" destination="ppJ-js-Wwz" id="68u-8M-R2Q"/>
<outlet property="runningView" destination="9Yj-FX-eFd" id="L2C-YR-2HN"/>
<outlet property="startSegment" destination="2MI-6l-YQt" id="Jun-ct-Xag"/>
<outlet property="stopButton" destination="vAq-EZ-Gmx" id="XiW-1H-I9y"/>
<outlet property="timeLabel" destination="rbR-np-cXD" id="EEe-8F-HT6"/>
</connections>
@@ -413,15 +414,15 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="c0B-OV-ujb">
<rect key="frame" x="16" y="8" width="33.5" height="20.5"/>
<rect key="frame" x="16" y="9" width="33.5" height="20.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="4" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontSizeToFit="NO" id="sAD-Ns-DV6">
<rect key="frame" x="16" y="31.5" width="35" height="16"/>
<rect key="frame" x="16" y="32.5" width="33" height="14.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
@@ -656,6 +657,7 @@ Duration: 60:00</string>
<outlet property="appIcon" destination="rbW-pK-Kct" id="VlO-fG-y1a"/>
<outlet property="appTitle" destination="Et0-8d-CId" id="HgD-oI-0J8"/>
<outlet property="buttonCancel" destination="TGg-60-wZW" id="5Ej-7t-jaD"/>
<outlet property="buttonFilter" destination="LOr-e7-foG" id="qUx-1k-xJK"/>
<outlet property="buttonSave" destination="rWg-hE-Ydl" id="zfM-kx-erX"/>
<outlet property="chooseAppTap" destination="Jab-q2-U9X" id="Tzv-lm-sUm"/>
<outlet property="inputDetails" destination="pql-H5-k6U" id="NXm-8f-5E6"/>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="qdB-ZO-LHY">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="qdB-ZO-LHY">
<device id="retina4_0" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
@@ -243,25 +243,93 @@
</tableViewSection>
<tableViewSection headerTitle="Advanced Settings" id="Vlg-nm-VB3">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="VnR-9B-1zl">
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="rio-m6-pXN" detailTextLabel="rr4-sR-VxD" style="IBUITableViewCellStyleSubtitle" id="pQ5-lm-Rco">
<rect key="frame" x="0.0" y="687.5" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VnR-9B-1zl" id="ZTz-vZ-l5p">
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pQ5-lm-Rco" id="52Y-H3-jvJ">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="twS-Ne-dU0">
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Force disconnect unresolvable" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="rio-m6-pXN">
<rect key="frame" x="16" y="5" width="234" height="20.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="in case DNS returns empty IP" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="rr4-sR-VxD">
<rect key="frame" x="16" y="25.5" width="166" height="14.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TSu-IP-KFG">
<rect key="frame" x="257" y="6" width="49" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="togglePreventUnresolvable:" destination="qdB-ZO-LHY" eventType="valueChanged" id="xKv-Hp-Nyq"/>
</connections>
</switch>
</subviews>
</tableViewCellContentView>
<connections>
<outlet property="accessoryView" destination="TSu-IP-KFG" id="VGm-hN-DLK"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="x5u-5T-XTO" detailTextLabel="n7p-Ab-69G" style="IBUITableViewCellStyleSubtitle" id="lxs-NQ-Q7V">
<rect key="frame" x="0.0" y="731.5" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="lxs-NQ-Q7V" id="B1l-cb-yQg">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Force disconnect swcd" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="x5u-5T-XTO">
<rect key="frame" x="16" y="5" width="177" height="20.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="background deamon User-Agent: swcd" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="n7p-Ab-69G">
<rect key="frame" x="16" y="25.5" width="220.5" height="14.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="c5N-PN-qWU">
<rect key="frame" x="257" y="6" width="49" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="togglePreventSWCD:" destination="qdB-ZO-LHY" eventType="valueChanged" id="MAa-5v-djq"/>
</connections>
</switch>
</subviews>
</tableViewCellContentView>
<connections>
<outlet property="accessoryView" destination="c5N-PN-qWU" id="SAN-Q9-VOn"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="Tgc-re-gI7">
<rect key="frame" x="0.0" y="775.5" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Tgc-re-gI7" id="haV-RB-dEa">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vZa-EO-FAZ">
<rect key="frame" x="125" y="7" width="70" height="30"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
<state key="normal" title="Export DB"/>
<connections>
<action selector="exportDB" destination="qdB-ZO-LHY" eventType="touchUpInside" id="FYN-Zz-UK4"/>
<action selector="exportDB" destination="qdB-ZO-LHY" eventType="touchUpInside" id="NPx-9w-ua0"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="twS-Ne-dU0" firstAttribute="centerY" secondItem="ZTz-vZ-l5p" secondAttribute="centerY" id="LgK-8q-r6K"/>
<constraint firstItem="twS-Ne-dU0" firstAttribute="centerX" secondItem="ZTz-vZ-l5p" secondAttribute="centerX" id="ltC-Ba-Bxr"/>
<constraint firstItem="vZa-EO-FAZ" firstAttribute="centerX" secondItem="haV-RB-dEa" secondAttribute="centerX" id="h0M-qz-pHV"/>
<constraint firstItem="vZa-EO-FAZ" firstAttribute="centerY" secondItem="haV-RB-dEa" secondAttribute="centerY" id="xBJ-94-nJh"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
@@ -282,6 +350,8 @@
<outlet property="cellNotificationConnectionAlert" destination="OTC-Kt-LFT" id="XiG-CC-4lC"/>
<outlet property="cellNotificationReminder" destination="jZA-aP-aHG" id="sjo-2s-rqW"/>
<outlet property="cellPrivacyAutoDelete" destination="Qyy-0U-yhd" id="PzN-iv-kFl"/>
<outlet property="swcdToggle" destination="c5N-PN-qWU" id="qDy-BX-O85"/>
<outlet property="unresolvableToggle" destination="TSu-IP-KFG" id="Vdb-cm-Uy2"/>
<outlet property="vpnToggle" destination="ZAz-WT-FDb" id="lGX-J8-WrU"/>
</connections>
</tableViewController>
@@ -364,7 +434,7 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" id="zaV-mh-eqb">
<rect key="frame" x="252" y="6" width="54" height="31"/>
<rect key="frame" x="250" y="6" width="54" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="toggleAllowRestartReminder:" destination="JYM-cs-i4H" eventType="valueChanged" id="F4e-k2-bni"/>
@@ -391,7 +461,7 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" id="HaE-En-NH3">
<rect key="frame" x="252" y="6" width="54" height="31"/>
<rect key="frame" x="250" y="6" width="54" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="toggleAllowRestartNotify:" destination="JYM-cs-i4H" eventType="valueChanged" id="12C-h5-mrR"/>
@@ -418,7 +488,7 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" id="N2Q-cU-pkd">
<rect key="frame" x="252" y="6" width="54" height="31"/>
<rect key="frame" x="250" y="6" width="54" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="toggleAllowRestartBadge:" destination="JYM-cs-i4H" eventType="valueChanged" id="76l-6y-fOu"/>
@@ -476,7 +546,7 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mTm-Rm-1RQ">
<rect key="frame" x="255" y="6" width="49" height="31"/>
<rect key="frame" x="255" y="6" width="48" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="toggleAllowRecordingReminder:" destination="JYM-cs-i4H" eventType="valueChanged" id="unC-Ur-jPM"/>
@@ -602,7 +672,7 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="who-8G-voz">
<rect key="frame" x="256" y="6" width="48" height="31"/>
<rect key="frame" x="256" y="6" width="47" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="toggleShowNotifications:" destination="D2a-Po-vDU" eventType="valueChanged" id="Thg-6R-7wM"/>
@@ -832,6 +902,6 @@ If App Badge is enabled, display the letter "1" on the homescreen app icon, as l
</scenes>
<inferredMetricsTieBreakers>
<segue reference="tUF-Kv-koO"/>
<segue reference="6wc-d1-VYY"/>
<segue reference="EzT-Xq-wka"/>
</inferredMetricsTieBreakers>
</document>

View File

@@ -161,4 +161,12 @@ struct VPNAppMessage {
static func isRecording(_ state: CurrentRecordingState) -> Self {
.init("recording-now:\(state.rawValue)")
}
/// Triggered whenever user taps on the switch in settings
static func disconnectUnresolvable(_ state: Bool) -> Self {
.init("disconnect-unresolvable:\(state ? 1 : 0)")
}
/// Triggered whenever user taps on the switch in settings
static func disconnectSWCD(_ state: Bool) -> Self {
.init("disconnect-swcd:\(state ? 1 : 0)")
}
}

View File

@@ -10,6 +10,10 @@ class GlassVPNHook {
private var cachedNotify: CachedConnectionAlert!
private var currentlyRecording: Bool = false
public var forceDisconnectUnresolvable: Bool = false
public var forceDisconnectSWCD: Bool = false
init() { reset() }
/// Reload from stored settings and rebuilt binary search tree
@@ -18,6 +22,8 @@ class GlassVPNHook {
setAutoDelete(PrefsShared.AutoDeleteLogsDays)
cachedNotify = CachedConnectionAlert()
currentlyRecording = PrefsShared.CurrentlyRecording != .Off
forceDisconnectUnresolvable = PrefsShared.ForceDisconnectUnresolvableDNS
forceDisconnectSWCD = PrefsShared.ForceDisconnectSWCD
}
/// Invalidate auto-delete timer and release stored properties. You should nullify this instance afterwards.
@@ -28,6 +34,8 @@ class GlassVPNHook {
autoDeleteTimer?.invalidate()
cachedNotify = nil
currentlyRecording = false
forceDisconnectUnresolvable = false
forceDisconnectSWCD = false
}
/// Call this method from `PacketTunnelProvider.handleAppMessage(_:completionHandler:)`
@@ -50,6 +58,12 @@ class GlassVPNHook {
let newState = CurrentRecordingState(rawValue: Int(value) ?? 0)
currentlyRecording = newState != .Off
return
case "disconnect-unresolvable":
forceDisconnectUnresolvable = value == "1"
return
case "disconnect-swcd":
forceDisconnectSWCD = value == "1"
return
default: break
}
}
@@ -79,6 +93,11 @@ class GlassVPNHook {
return blockActive
}
func silentlyPrevented(_ domain: String) {
// TODO: persist in a separate db/table?
NSLog("[VPN.INFO] preventing connection to \(domain)")
}
/// Build binary tree for reverse DNS lookup
private func reloadDomainFilter() {
let tmp = AppDB?.loadFilters()?.map({
@@ -133,13 +152,12 @@ class GlassVPNHook {
@objc private func autoDeleteNow(_ sender: Timer) {
NSLog("[VPN.INFO] Auto-delete old logs")
queue.async {
guard sender.isValid else { return }
do {
try AppDB?.dnsLogsDeleteOlderThan(days: sender.userInfo as! Int)
} catch {
NSLog("[VPN.WARN] Couldn't delete logs, will retry in 5 minutes. \(error)")
if sender.isValid {
sender.fireDate = Date().addingTimeInterval(300) // retry in 5 min
}
sender.fireDate = Date().addingTimeInterval(300) // retry in 5 min
}
}
}

View File

@@ -6,7 +6,7 @@ class TVCShareRecording : UITableViewController, UITextViewDelegate, VCEditTextD
// vars
var record: Recording!
private var shareNotes: Bool = false // opt-in
private var shareNotes: Bool = true // green switch is more present
private lazy var hasNotes: Bool = (self.record.notes != nil)
private lazy var editedNotes: String = self.record.notes ?? ""
private lazy var weekInYear: String = {
@@ -86,6 +86,13 @@ class TVCShareRecording : UITableViewController, UITextViewDelegate, VCEditTextD
}
}
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
if motion == .motionShake, let key = record.uploadkey {
UIPasteboard.general.string = key
banner(.ok, "Copied to clipboard", timeout: 1)
}
}
// MARK: - Table Data Source
@@ -145,7 +152,7 @@ class TVCShareRecording : UITableViewController, UITextViewDelegate, VCEditTextD
cell = tableView.dequeueReusableCell(withIdentifier: "shareKeyValueCell")!
let src = dataSourceKeyValue[indexPath.row]
cell.textLabel?.text = src.key
let flag = shareNotes && indexPath.row == 4
let flag = indexPath.row == 4 && shareNotes && hasNotes
cell.detailTextLabel?.text = flag ? editedNotes : src.value
case 3:
cell = tableView.dequeueReusableCell(withIdentifier: "shareLogCell")!
@@ -234,8 +241,8 @@ class TVCShareRecording : UITableViewController, UITextViewDelegate, VCEditTextD
// MARK: - Alerts & Banner
private func banner(_ style: NotificationBanner.Style, _ msg: String) {
NotificationBanner(msg, style: style).present(in: self)
private func banner(_ style: NotificationBanner.Style, _ msg: String, timeout: TimeInterval = 3) {
NotificationBanner(msg, style: style).present(in: navigationController!, hideAfter: timeout)
}
private func showAlertAvailableSoon(_ urlStr: String, when: Int?) {

View File

@@ -16,13 +16,18 @@ class VCEditRecording: UIViewController, UITextFieldDelegate, UITextViewDelegate
@IBOutlet private var noteBottom: NSLayoutConstraint!
@IBOutlet private var chooseAppTap: UITapGestureRecognizer!
@IBOutlet private var buttonFilter: UIButton!
override func viewDidLoad() {
if deleteOnCancel { // aka newly created
RecordingsDB.persist(record)
if Prefs.RecordingReminder.Enabled {
PushNotification.scheduleRecordingReminder(force: true)
let r = record!
DispatchQueue.global().async {
RecordingsDB.persist(r)
if Prefs.RecordingReminder.Enabled {
PushNotification.scheduleRecordingReminder(force: true)
}
}
buttonFilter.isHidden = true
// mark as destructive
buttonCancel.tintColor = .systemRed
if #available(iOS 13.0, *) {

View File

@@ -10,8 +10,10 @@ class VCRecordings: UIViewController, UINavigationControllerDelegate {
@IBOutlet private var runningView: UIView!
@IBOutlet private var timeLabel: UILabel!
@IBOutlet private var stopButton: UIButton!
@IBOutlet private var startSegment: UISegmentedControl!
override func viewDidLoad() {
startSegment.setTitleTextAttributes([NSAttributedString.Key.foregroundColor : UIColor.sysLink], for: .normal)
timeLabel.font = timeLabel.font.monoSpace()
if let ongoing = RecordingsDB.getCurrent() {
currentRecording = ongoing

View File

@@ -70,7 +70,7 @@ class TVCOccurrenceContext: UITableViewController {
private var copyDomain: String? = nil
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if cellMenu.start(tableView, indexPath) {
if !firstOrLast(indexPath.row), cellMenu.start(tableView, indexPath) {
copyDomain = cellMenu.getSelected(dataSource)?.domain
self.becomeFirstResponder()
}

View File

@@ -3,6 +3,8 @@ import UIKit
class TVCSettings: UITableViewController {
@IBOutlet var vpnToggle: UISwitch!
@IBOutlet var unresolvableToggle: UISwitch!
@IBOutlet var swcdToggle: UISwitch!
@IBOutlet var cellDomainsIgnored: UITableViewCell!
@IBOutlet var cellDomainsBlocked: UITableViewCell!
@IBOutlet var cellPrivacyAutoDelete: UITableViewCell!
@@ -14,6 +16,7 @@ class TVCSettings: UITableViewController {
reloadVPNState()
reloadLoggingFilterUI()
reloadPrivacyUI()
reloadAdvancedUI()
NotifyVPNStateChanged.observe(call: #selector(reloadVPNState), on: self)
NotifyDNSFilterChanged.observe(call: #selector(reloadLoggingFilterUI), on: self)
}
@@ -191,6 +194,21 @@ extension TVCSettings {
// MARK: - Advanced
extension TVCSettings {
private func reloadAdvancedUI() {
unresolvableToggle.isOn = PrefsShared.ForceDisconnectUnresolvableDNS
swcdToggle.isOn = PrefsShared.ForceDisconnectSWCD
}
@IBAction private func togglePreventUnresolvable(_ sender: UISwitch) {
PrefsShared.ForceDisconnectUnresolvableDNS = sender.isOn
GlassVPN.send(.disconnectUnresolvable(sender.isOn))
}
@IBAction private func togglePreventSWCD(_ sender: UISwitch) {
PrefsShared.ForceDisconnectSWCD = sender.isOn
GlassVPN.send(.disconnectSWCD(sender.isOn))
}
@IBAction private func exportDB() {
AppDB?.vacuum()
let sheet = UIActivityViewController(activityItems: [URL.internalDB()], applicationActivities: nil)

View File

@@ -1,5 +1,5 @@
# What are Recordings?
Similar to the default logging, recordings will intercept every request and log it for later review. App recordings are usually 3  5 minutes long and cover a single application. You can utilize recordings for App analysis or to get a ground truth on background traffic.
Similar to the default logging, recordings will intercept every request and log it for later review. App recordings are usually 1  4 minutes long and cover a single application. You can utilize recordings for App analysis or to get a ground truth on background traffic.
Optionally, you can help us by providing your app specific recordings. Together with your findings we can create a community driven privacy monitor. The research results will help you and others avoid Apps that unnecessarily share data with third-party providers.

View File

@@ -1,6 +1,6 @@
# How to record?
Before you begin, there are two types of recordings: app specific recordings and general background activity. The former are usually 3  5 minutes long, the latter need to be at least an hour long.
Before you begin, there are two types of recordings: app specific recordings and general background activity. The former are usually less than 5 minutes long, the latter must be at least an hour long.
### Important notice