Auto-delete logs finished + custom App-to-VPN messages
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
5404AEEB24A90717003B2F54 /* PrefsShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E67E4524A8B0FE0025D261 /* PrefsShared.swift */; };
|
||||
540E6780242D2CF100871BBE /* VCRecordings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E677F242D2CF100871BBE /* VCRecordings.swift */; };
|
||||
540E67822433483D00871BBE /* VCEditRecording.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67812433483D00871BBE /* VCEditRecording.swift */; };
|
||||
540E67842433FAFE00871BBE /* TVCPreviousRecords.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */; };
|
||||
@@ -917,6 +918,7 @@
|
||||
54CA02722426B2FD003A5E04 /* IPInterval.swift in Sources */,
|
||||
54CA029A2426B2FD003A5E04 /* Observer.swift in Sources */,
|
||||
54CA025C2426B2FD003A5E04 /* ConnectSession.swift in Sources */,
|
||||
5404AEEB24A90717003B2F54 /* PrefsShared.swift in Sources */,
|
||||
54CA02C42426DCCD003A5E04 /* GCDAsyncUdpSocket.m in Sources */,
|
||||
54CA02BA2426B2FD003A5E04 /* ProxySocket.swift in Sources */,
|
||||
54CA025E2426B2FD003A5E04 /* ResponseGeneratorFactory.swift in Sources */,
|
||||
|
||||
@@ -80,18 +80,26 @@ class LDObserverFactory: ObserverFactory {
|
||||
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
|
||||
let proxyServerPort: UInt16 = 9090
|
||||
let proxyServerAddress = "127.0.0.1"
|
||||
var proxyServer: GCDHTTPProxyServer!
|
||||
private let proxyServerPort: UInt16 = 9090
|
||||
private let proxyServerAddress = "127.0.0.1"
|
||||
private var proxyServer: GCDHTTPProxyServer!
|
||||
|
||||
private var autoDeleteTimer: Timer? = nil
|
||||
|
||||
private func reloadSettings() {
|
||||
reloadDomainFilter()
|
||||
setAutoDelete(PrefsShared.AutoDeleteLogsDays)
|
||||
}
|
||||
|
||||
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||
DDLogVerbose("startTunnel with with options: \(String(describing: options))")
|
||||
do {
|
||||
try SQLiteDatabase.open().initCommonScheme()
|
||||
} catch {
|
||||
completionHandler(error)
|
||||
return
|
||||
}
|
||||
reloadDomainFilter()
|
||||
reloadSettings()
|
||||
|
||||
if proxyServer != nil {
|
||||
proxyServer.stop()
|
||||
@@ -145,12 +153,63 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
proxyServer = nil
|
||||
filterDomains = nil
|
||||
filterOptions = nil
|
||||
autoDeleteTimer?.fire() // one last time before we quit
|
||||
autoDeleteTimer?.invalidate()
|
||||
completionHandler()
|
||||
exit(EXIT_SUCCESS)
|
||||
}
|
||||
|
||||
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
|
||||
reloadDomainFilter()
|
||||
let message = String(data: messageData, encoding: .utf8)
|
||||
if let msg = message, let i = msg.firstIndex(of: ":") {
|
||||
let action = msg.prefix(upTo: i)
|
||||
let value = msg.suffix(from: msg.index(after: i))
|
||||
switch action {
|
||||
case "filter-update":
|
||||
reloadDomainFilter() // TODO: reload only selected domain?
|
||||
return
|
||||
case "auto-delete":
|
||||
setAutoDelete(Int(value) ?? PrefsShared.AutoDeleteLogsDays)
|
||||
return
|
||||
default: break
|
||||
}
|
||||
}
|
||||
DDLogWarn("This should never happen! Received unknown handleAppMessage: \(message ?? messageData.base64EncodedString())")
|
||||
reloadSettings() // just in case we fallback to do everything
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ################################################################
|
||||
// #
|
||||
// # MARK: - Auto-delete Timer
|
||||
// #
|
||||
// ################################################################
|
||||
|
||||
extension PacketTunnelProvider {
|
||||
|
||||
private func setAutoDelete(_ days: Int) {
|
||||
autoDeleteTimer?.invalidate()
|
||||
guard days > 0 else { return }
|
||||
// Repeat interval uses days as hours. min 1 hr, max 24 hrs.
|
||||
let interval = TimeInterval(min(24, days) * 60 * 60)
|
||||
autoDeleteTimer = Timer.scheduledTimer(timeInterval: interval,
|
||||
target: self, selector: #selector(autoDeleteNow),
|
||||
userInfo: days, repeats: true)
|
||||
autoDeleteTimer!.fire()
|
||||
}
|
||||
|
||||
@objc private func autoDeleteNow(_ sender: Timer) {
|
||||
DDLogInfo("Auto-delete old logs")
|
||||
queue.async {
|
||||
do {
|
||||
try AppDB?.dnsLogsDeleteOlderThan(days: sender.userInfo as! Int)
|
||||
} catch {
|
||||
DDLogWarn("Couldn't delete logs, will retry in 5 minutes. \(error)")
|
||||
if sender.isValid {
|
||||
sender.fireDate = Date().addingTimeInterval(300) // retry in 5 min
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
sync.start()
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
TheGreatDestroyer.deleteLogs(olderThan: PrefsShared.AutoDeleteLogsDays)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +39,21 @@ extension SQLiteDatabase {
|
||||
/// `INSERT INTO cache (dns, opt) VALUES (?, ?);`
|
||||
func logWrite(_ domain: String, blocked: Bool = false) throws {
|
||||
try self.run(sql: "INSERT INTO cache (dns, opt) VALUES (?, ?);",
|
||||
bind: [BindText(domain), BindInt32(blocked ? 1 : 0)])
|
||||
bind: [BindText(domain), BindInt32(blocked ? 1 : 0)])
|
||||
{ try ifStep($0, SQLITE_DONE) }
|
||||
}
|
||||
|
||||
/// `DELETE FROM cache WHERE ts < (now - ? days);`
|
||||
/// - Parameter days: if `0` or negative, this function does nothing.
|
||||
/// - Returns: `true` if at least one row was deleted.
|
||||
@discardableResult func dnsLogsDeleteOlderThan(days: Int) throws -> Bool {
|
||||
guard days > 0 else { return false }
|
||||
return try self.run(sql: "DELETE FROM cache WHERE ts < strftime('%s', 'now', ?);",
|
||||
bind: [BindText("-\(days) days")]) {
|
||||
try ifStep($0, SQLITE_DONE)
|
||||
return numberOfChanges > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ struct TheGreatDestroyer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fired when user taps on Settings -> Delete All Logs
|
||||
/// Fired when user taps on Settings -> "Delete All Logs"
|
||||
static func deleteAllLogs() {
|
||||
sync.pause()
|
||||
DispatchQueue.global().async {
|
||||
@@ -26,4 +26,18 @@ struct TheGreatDestroyer {
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fired when user changes Settings -> "Auto-delete logs" and every time the App enters foreground
|
||||
static func deleteLogs(olderThan days: Int) {
|
||||
guard days > 0 else { return }
|
||||
sync.pause()
|
||||
DispatchQueue.global().async {
|
||||
defer { sync.continue() }
|
||||
QLog.Info("Auto-delete logs")
|
||||
guard let success = try? AppDB?.dnsLogsDeleteOlderThan(days: days), success else {
|
||||
return // nothing changed
|
||||
}
|
||||
sync.needsReloadDB()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,19 @@ final class GlassVPNManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Notify VPN extension about changes
|
||||
/// - Returns: `true` on success, `false` if VPN is off or message could not be converted to `.utf8`
|
||||
@discardableResult func send(_ message: VPNAppMessage) -> Bool {
|
||||
if let session = self.managerVPN?.connection as? NETunnelProviderSession,
|
||||
session.status == .connected, let data = message.raw {
|
||||
do {
|
||||
try session.sendProviderMessage(data, responseHandler: nil)
|
||||
return true
|
||||
} catch {}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Notify callback
|
||||
|
||||
@@ -50,12 +63,8 @@ final class GlassVPNManager {
|
||||
postRawVPNState((notification.object as? NETunnelProviderSession)?.status ?? .invalid)
|
||||
}
|
||||
|
||||
@objc private func didChangeDomainFilter() {
|
||||
// Notify VPN extension about changes
|
||||
if let session = self.managerVPN?.connection as? NETunnelProviderSession,
|
||||
session.status == .connected {
|
||||
try? session.sendProviderMessage("filter-update".data(using: .ascii)!, responseHandler: nil)
|
||||
}
|
||||
@objc private func didChangeDomainFilter(_ notification: Notification) {
|
||||
send(.filterUpdate(domain: notification.object as? String))
|
||||
}
|
||||
|
||||
|
||||
@@ -109,3 +118,22 @@ final class GlassVPNManager {
|
||||
NotifyVPNStateChanged.post()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// |
|
||||
// | MARK: - VPN message
|
||||
// |
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
struct VPNAppMessage {
|
||||
let raw: Data?
|
||||
init(_ string: String) { raw = string.data(using: .utf8) }
|
||||
|
||||
static func filterUpdate(domain: String? = nil) -> Self {
|
||||
.init("filter-update:\(domain ?? "")")
|
||||
}
|
||||
static func autoDelete(after interval: Int) -> Self {
|
||||
.init("auto-delete:\(interval)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,14 +77,19 @@ class TVCSettings: UITableViewController {
|
||||
|
||||
let picker = DurationPickerAlert(
|
||||
title: "Auto-delete logs",
|
||||
detail: "Logs will be deleted on app launch or periodically as long as the VPN is running.",
|
||||
detail: "Warning: Logs older than the selected interval are deleted immediately! " +
|
||||
"Logs are also deleted on each app launch, and periodically in the background as long as the VPN is running.",
|
||||
options: [(0...30).map{"\($0)"}, ["Days", "Weeks", "Months"]],
|
||||
widths: [0.4, 0.6])
|
||||
picker.pickerView.setSelection([min(30, one), two])
|
||||
picker.present(in: self) {
|
||||
PrefsShared.AutoDeleteLogsDays = $1[0] * multiplier[$1[1]]
|
||||
cell.detailTextLabel?.text = autoDeleteString($1[0], unit: $1[1])
|
||||
// TODO: notify VPN and local delete timer
|
||||
picker.present(in: self) { _, idx in
|
||||
cell.detailTextLabel?.text = autoDeleteString(idx[0], unit: idx[1])
|
||||
let asDays = idx[0] * multiplier[idx[1]]
|
||||
PrefsShared.AutoDeleteLogsDays = asDays
|
||||
if !GlassVPN.send(.autoDelete(after: asDays)) {
|
||||
// if VPN isn't active, fallback to immediate local delete
|
||||
TheGreatDestroyer.deleteLogs(olderThan: asDays)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user