From d0056c0275519af44ce965574248fc5d465a5966 Mon Sep 17 00:00:00 2001 From: relikd Date: Wed, 8 Apr 2020 18:53:00 +0200 Subject: [PATCH] Recording details duplicate and display --- .../Socket/AdapterSocket/SOCKS5Adapter.swift | 2 +- main/DB/DBWrapper.swift | 9 +- main/DB/SQDB.swift | 127 ++++++++++++++---- main/Extensions/DBExtensions.swift | 6 +- main/Recordings/TVCPreviousRecords.swift | 7 +- main/Recordings/TVCRecordingDetails.swift | 7 +- main/Recordings/VCEditRecording.swift | 8 +- main/Recordings/VCRecordings.swift | 5 +- 8 files changed, 120 insertions(+), 51 deletions(-) diff --git a/GlassVPN/zhuhaow-NEKit/Socket/AdapterSocket/SOCKS5Adapter.swift b/GlassVPN/zhuhaow-NEKit/Socket/AdapterSocket/SOCKS5Adapter.swift index 3333db2..b692069 100755 --- a/GlassVPN/zhuhaow-NEKit/Socket/AdapterSocket/SOCKS5Adapter.swift +++ b/GlassVPN/zhuhaow-NEKit/Socket/AdapterSocket/SOCKS5Adapter.swift @@ -14,7 +14,7 @@ public class SOCKS5Adapter: AdapterSocket { var internalStatus: SOCKS5AdapterStatus = .invalid - let helloData = Data(bytes: UnsafePointer(([0x05, 0x01, 0x00] as [UInt8])), count: 3) + let helloData = Data([0x05, 0x01, 0x00]) public enum ReadTag: Int { case methodResponse = -20000, connectResponseFirstPart, connectResponseSecondPart diff --git a/main/DB/DBWrapper.swift b/main/DB/DBWrapper.swift index 7ad52c5..16a63f1 100644 --- a/main/DB/DBWrapper.swift +++ b/main/DB/DBWrapper.swift @@ -48,6 +48,7 @@ class DBWrapper { // MARK: - Init func initContentOfDB() { + QLog.Debug("SQLite path: \(URL.internalDB())") DispatchQueue.global().async { #if IOS_SIMULATOR self.generateTestData() @@ -226,7 +227,9 @@ class DBWrapper { func listOfRecordings() -> [Recording] { AppDB?.allRecordings() ?? [] } func recordingGetCurrent() -> Recording? { AppDB?.ongoingRecording() } func recordingStartNew() -> Recording? { try? AppDB?.startNewRecording() } - func recordingStopAll() { AppDB?.stopRecordings() } + + func recordingStop(_ r: inout Recording) { AppDB?.stopRecording(&r) } + func recordingPersist(_ r: Recording) { AppDB?.persistRecordingLogs(r) } func recordingUpdate(_ r: Recording) { AppDB?.updateRecording(r) @@ -239,6 +242,10 @@ class DBWrapper { } } + func recordingDetails(_ r: Recording) -> [(domain: String?, count: Int32)]? { + AppDB?.getRecordingsLogs(r) + } + // MARK: - Helper methods diff --git a/main/DB/SQDB.swift b/main/DB/SQDB.swift index a463b4d..558cbf0 100644 --- a/main/DB/SQDB.swift +++ b/main/DB/SQDB.swift @@ -9,7 +9,7 @@ struct GroupedDomain { struct FilterOptions: OptionSet { let rawValue: Int32 - static let none = FilterOptions(rawValue: 0) + static let none = FilterOptions([]) static let blocked = FilterOptions(rawValue: 1 << 0) static let ignored = FilterOptions(rawValue: 1 << 1) static let any = FilterOptions(rawValue: 0b11) @@ -28,7 +28,6 @@ enum SQLiteError: Error { class SQLiteDatabase { private let dbPointer: OpaquePointer? private init(dbPointer: OpaquePointer?) { -// print("SQLite path: \(URL.internalDB())") self.dbPointer = dbPointer } @@ -158,6 +157,7 @@ extension SQLiteDatabase { try? self.createTable(table: DNSQueryT.self) try? self.createTable(table: DNSFilterT.self) try? self.createTable(table: Recording.self) + try? self.createTable(table: RecordingLog.self) } } @@ -172,9 +172,9 @@ private struct DNSQueryT: SQLTable { static var createStatement: String { return """ CREATE TABLE IF NOT EXISTS req( - ts BIGINT DEFAULT (strftime('%s','now')), - domain VARCHAR(255) NOT NULL, - logOpt INT DEFAULT 0 + ts INTEGER DEFAULT (strftime('%s','now')), + domain TEXT NOT NULL, + logOpt INTEGER DEFAULT 0 ); """ } @@ -254,8 +254,8 @@ private struct DNSFilterT: SQLTable { static var createStatement: String { return """ CREATE TABLE IF NOT EXISTS filter( - domain VARCHAR(255) UNIQUE NOT NULL, - opt INT DEFAULT 0 + domain TEXT UNIQUE NOT NULL, + opt INTEGER DEFAULT 0 ); """ } @@ -310,6 +310,7 @@ extension SQLiteDatabase { // MARK: - Recordings struct Recording: SQLTable { + let id: sqlite3_int64 let start: Timestamp let stop: Timestamp? var appId: String? = nil @@ -318,10 +319,11 @@ struct Recording: SQLTable { static var createStatement: String { return """ CREATE TABLE IF NOT EXISTS rec( - start BIGINT DEFAULT (strftime('%s','now')), - stop BIGINT, - appid VARCHAR(255), - title VARCHAR(255), + id INTEGER PRIMARY KEY, + start INTEGER DEFAULT (strftime('%s','now')), + stop INTEGER, + appid TEXT, + title TEXT, notes TEXT ); """ @@ -332,33 +334,37 @@ extension SQLiteDatabase { // MARK: write - func startNewRecording(_ title: String? = nil, appBundle: String? = nil) throws -> Recording { - try run(sql: "INSERT INTO rec (title, appid) VALUES (?, ?);", bind: { - self.bindTextOrNil($0, 1, title) && self.bindTextOrNil($0, 2, appBundle) - }) { stmt -> Recording in + func startNewRecording() throws -> Recording { + try run(sql: "INSERT INTO rec (stop) VALUES (NULL);", bind: nil) { stmt -> Recording in try ifStep(stmt, SQLITE_DONE) - return ongoingRecording()! + return try getRecording(withID: sqlite3_last_insert_rowid(dbPointer)) } } - func stopRecordings() { - try? run(sql: "UPDATE rec SET stop = (strftime('%s','now')) WHERE stop IS NULL;", bind: nil) { stmt -> Void in - sqlite3_step(stmt) + func stopRecording(_ r: inout Recording) { + guard r.stop == nil else { return } + let theID = r.id + try? run(sql: "UPDATE rec SET stop = (strftime('%s','now')) WHERE id = ? LIMIT 1;", bind: { + self.bindInt64($0, 1, theID) + }) { stmt -> Void in + try ifStep(stmt, SQLITE_DONE) + r = try getRecording(withID: theID) } } func updateRecording(_ r: Recording) { - try? run(sql: "UPDATE rec SET title = ?, appid = ?, notes = ? WHERE start = ? LIMIT 1;", bind: { + try? run(sql: "UPDATE rec SET title = ?, appid = ?, notes = ? WHERE id = ? LIMIT 1;", bind: { self.bindTextOrNil($0, 1, r.title) && self.bindTextOrNil($0, 2, r.appId) - && self.bindTextOrNil($0, 3, r.notes) && self.bindInt64($0, 4, r.start) + && self.bindTextOrNil($0, 3, r.notes) && self.bindInt64($0, 4, r.id) }) { stmt -> Void in sqlite3_step(stmt) } } func deleteRecording(_ r: Recording) throws -> Bool { - try run(sql: "DELETE FROM rec WHERE start = ? LIMIT 1;", bind: { - self.bindInt64($0, 1, r.start) + _ = try? deleteRecordingLogs(r.id) + return try run(sql: "DELETE FROM rec WHERE id = ? LIMIT 1;", bind: { + self.bindInt64($0, 1, r.id) }) { try ifStep($0, SQLITE_DONE) return sqlite3_changes(dbPointer) > 0 @@ -368,12 +374,13 @@ extension SQLiteDatabase { // MARK: read func readRecording(_ stmt: OpaquePointer) -> Recording { - let end = sqlite3_column_int64(stmt, 1) - return Recording(start: sqlite3_column_int64(stmt, 0), + let end = sqlite3_column_int64(stmt, 2) + return Recording(id: sqlite3_column_int64(stmt, 0), + start: sqlite3_column_int64(stmt, 1), stop: end == 0 ? nil : end, - appId: readText(stmt, 2), - title: readText(stmt, 3), - notes: readText(stmt, 4)) + appId: readText(stmt, 3), + title: readText(stmt, 4), + notes: readText(stmt, 5)) } func ongoingRecording() -> Recording? { @@ -388,4 +395,68 @@ extension SQLiteDatabase { allRows($0) { readRecording($0) } } } + + func getRecording(withID: sqlite3_int64) throws -> Recording { + try run(sql: "SELECT * FROM rec WHERE id = ? LIMIT 1;", bind: { + self.bindInt64($0, 1, withID) + }) { + try ifStep($0, SQLITE_ROW) + return readRecording($0) + } + } +} + +// MARK: + +private struct RecordingLog: SQLTable { + let rID: Int32 + let ts: Timestamp + let domain: String + static var createStatement: String { + return """ + CREATE TABLE IF NOT EXISTS recLog( + rid INTEGER REFERENCES rec(id) ON DELETE CASCADE, + ts INTEGER, + domain TEXT + ); + """ + } +} + +extension SQLiteDatabase { + + // MARK: write + + func persistRecordingLogs(_ r: Recording) { + guard let end = r.stop else { + return + } + try? run(sql: """ + INSERT INTO recLog (rid, ts, domain) SELECT ?, ts, domain FROM req + WHERE req.ts >= ? AND req.ts <= ? + """, bind: { + self.bindInt64($0, 1, r.id) && self.bindInt64($0, 2, r.start) && self.bindInt64($0, 3, end) + }) { + try ifStep($0, SQLITE_DONE) + } + } + + private func deleteRecordingLogs(_ recId: sqlite3_int64) throws -> Int32 { + try run(sql: "DELETE FROM recLog WHERE rid = ?;", bind: { + self.bindInt64($0, 1, recId) + }) { + try ifStep($0, SQLITE_DONE) + return sqlite3_changes(dbPointer) + } + } + + // MARK: read + + func getRecordingsLogs(_ r: Recording) -> [(domain: String?, count: Int32)]? { + try? run(sql: "SELECT domain, COUNT() FROM recLog WHERE rid = ? GROUP BY domain;", bind: { + self.bindInt64($0, 1, r.id) + }) { + allRows($0) { (readText($0, 0), sqlite3_column_int($0, 1)) } + } + } } diff --git a/main/Extensions/DBExtensions.swift b/main/Extensions/DBExtensions.swift index 04d743f..167c086 100644 --- a/main/Extensions/DBExtensions.swift +++ b/main/Extensions/DBExtensions.swift @@ -20,11 +20,7 @@ extension Array where Element == GroupedDomain { } extension Recording { - func stoppedCopy() -> Recording { - stop != nil ? self : Recording(start: start, stop: Timestamp(Date().timeIntervalSince1970), - appId: appId, title: title, notes: notes) - } - var fallbackTitle: String { get { "Unnamed #\(start)" } } + var fallbackTitle: String { get { "Unnamed Recording #\(id)" } } var duration: Timestamp? { get { stop == nil ? nil : stop! - start } } var durationString: String? { get { stop == nil ? nil : TimeFormat.from(duration!) } } } diff --git a/main/Recordings/TVCPreviousRecords.swift b/main/Recordings/TVCPreviousRecords.swift index c03e5ec..dfb6685 100644 --- a/main/Recordings/TVCPreviousRecords.swift +++ b/main/Recordings/TVCPreviousRecords.swift @@ -8,17 +8,14 @@ class TVCPreviousRecords: UITableViewController { NotifyRecordingChanged.observe(call: #selector(recordingDidChange(_:)), on: self) } - func stopRecording(_ record: Recording?) { - guard let r = record?.stoppedCopy() else { - return - } + func insertAndEditRecording(_ r: Recording) { insertNewRecord(r) editRecord(r, isNewRecording: true) } @objc private func recordingDidChange(_ notification: Notification) { let (new, deleted) = notification.object as! (Recording, Bool) - if let i = dataSource.firstIndex(where: { $0.start == new.start }) { + if let i = dataSource.firstIndex(where: { $0.id == new.id }) { if deleted { dataSource.remove(at: i) tableView.deleteRows(at: [IndexPath(row: i)], with: .automatic) diff --git a/main/Recordings/TVCRecordingDetails.swift b/main/Recordings/TVCRecordingDetails.swift index cc3bfe9..45a94d5 100644 --- a/main/Recordings/TVCRecordingDetails.swift +++ b/main/Recordings/TVCRecordingDetails.swift @@ -2,14 +2,11 @@ import UIKit class TVCRecordingDetails: UITableViewController { var record: Recording! - private var dataSource: [(domain: String, count: Int)] = [ - ("apple.com", 3), - ("cdn.apple.com", 1) - ] + private var dataSource: [(domain: String?, count: Int32)]! override func viewDidLoad() { title = record.title ?? record.fallbackTitle - // TODO: load db entries + dataSource = DBWrp.recordingDetails(record) ?? [] } diff --git a/main/Recordings/VCEditRecording.swift b/main/Recordings/VCEditRecording.swift index 53b9aa4..c5ca010 100644 --- a/main/Recordings/VCEditRecording.swift +++ b/main/Recordings/VCEditRecording.swift @@ -35,24 +35,24 @@ class VCEditRecording: UIViewController, UITextFieldDelegate, UITextViewDelegate if deleteOnCancel { // aka newly created // if remains true, `viewDidDisappear` will delete the record deleteOnCancel = false - // TODO: copy db entries in new table for editing } - QLog.Debug("updating record \(record.start)") + QLog.Debug("updating record #\(record.id)") record.title = (inputTitle.text == "") ? nil : inputTitle.text record.notes = (inputNotes.text == "") ? nil : inputNotes.text dismiss(animated: true) { DBWrp.recordingUpdate(self.record) + DBWrp.recordingPersist(self.record) } } @IBAction func didTapCancel(_ sender: UIBarButtonItem) { - QLog.Debug("discard edit of record \(record.start)") + QLog.Debug("discard edit of record #\(record.id)") dismiss(animated: true) } override func viewDidDisappear(_ animated: Bool) { if deleteOnCancel { - QLog.Debug("deleting record \(record.start)") + QLog.Debug("deleting record #\(record.id)") DBWrp.recordingDelete(record) deleteOnCancel = false } diff --git a/main/Recordings/VCRecordings.swift b/main/Recordings/VCRecordings.swift index 3f0916a..8fb927d 100644 --- a/main/Recordings/VCRecordings.swift +++ b/main/Recordings/VCRecordings.swift @@ -45,9 +45,10 @@ class VCRecordings: UIViewController, UINavigationControllerDelegate { startTimer(animate: true) } else { stopTimer(animate: true) - DBWrp.recordingStopAll() + DBWrp.recordingStop(¤tRecording!) prevRecController.popToRootViewController(animated: true) - (prevRecController.topViewController as! TVCPreviousRecords).stopRecording(currentRecording!) + let editVC = (prevRecController.topViewController as! TVCPreviousRecords) + editVC.insertAndEditRecording(currentRecording!) currentRecording = nil // otherwise it will restart } }