conversations-classic-ios/ConversationsClassic/AppData/Client/Client+MartinOMEMO.swift
2024-08-27 17:28:29 +02:00

426 lines
14 KiB
Swift

import Foundation
import GRDB
import Martin
import MartinOMEMO
final class ClientMartinOMEMO {
let credentials: Credentials
init(_ credentials: Credentials) {
self.credentials = credentials
}
var signal: (SignalStorage, SignalContext) {
let signalStorage = SignalStorage(sessionStore: self, preKeyStore: self, signedPreKeyStore: self, identityKeyStore: self, senderKeyStore: self)
// swiftlint:disable:next force_unwrapping
let signalContext = SignalContext(withStorage: signalStorage)!
signalStorage.setup(withContext: signalContext)
return (signalStorage, signalContext)
}
}
// MARK: - Session
extension ClientMartinOMEMO: SignalSessionStoreProtocol {
func sessionRecord(forAddress address: MartinOMEMO.SignalAddress) -> Data? {
do {
let data = try Database.shared.dbQueue.read { db in
try Row.fetchOne(
db,
sql: "SELECT key FROM omemo_sessions WHERE account = :account AND name = :name AND device_id = :deviceId",
arguments: ["account": credentials.bareJid, "name": address.name, "deviceId": address.deviceId]
)
}
return data?["key"]
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return nil
}
}
func allDevices(for name: String, activeAndTrusted: Bool) -> [Int32] {
do {
let sql = activeAndTrusted ?
"""
SELECT s.device_id
FROM omemo_sessions s
LEFT JOIN omemo_identities i
ON s.account = i.account
AND s.name = i.name
AND s.device_id = i.device_id
WHERE s.account = :account
AND s.name = :name
AND ((i.status >= 0 AND i.status % 2 = 0) OR i.status IS NULL)
"""
:
"SELECT device_id FROM omemo_sessions WHERE account = :account AND name = :name"
let data = try Database.shared.dbQueue.read { db in
try Row.fetchAll(
db,
sql: sql,
arguments: ["account": credentials.bareJid, "name": name]
)
}
return data.map { $0["device_id"] }
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return []
}
}
func storeSessionRecord(_ data: Data, forAddress: MartinOMEMO.SignalAddress) -> Bool {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: "INSERT INTO omemo_sessions (account, name, device_id, key) VALUES (:account, :name, :deviceId, :key)",
arguments: ["account": credentials.bareJid, "name": forAddress.name, "deviceId": forAddress.deviceId, "key": data]
)
}
return true
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
func containsSessionRecord(forAddress: MartinOMEMO.SignalAddress) -> Bool {
do {
let rec = try Database.shared.dbQueue.read { db in
try Row.fetchOne(
db,
sql: "SELECT key FROM omemo_sessions WHERE account = :account AND name = :name AND device_id = :deviceId",
arguments: ["account": credentials.bareJid, "name": forAddress.name, "deviceId": forAddress.deviceId]
)
}
return rec != nil
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
func deleteSessionRecord(forAddress: MartinOMEMO.SignalAddress) -> Bool {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: "DELETE FROM omemo_sessions WHERE account = :account AND name = :name AND device_id = :deviceId",
arguments: ["account": credentials.bareJid, "name": forAddress.name, "deviceId": forAddress.deviceId]
)
}
return true
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
func deleteAllSessions(for name: String) -> Bool {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: "DELETE FROM omemo_sessions WHERE account = :account AND name = :name",
arguments: ["account": credentials.bareJid, "name": name]
)
}
return true
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
func sessionsWipe() {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: "DELETE FROM omemo_sessions WHERE account = :account",
arguments: ["account": credentials.bareJid]
)
}
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
}
}
}
// MARK: - PreKey
extension ClientMartinOMEMO: SignalPreKeyStoreProtocol {
func currentPreKeyId() -> UInt32 {
do {
let data = try Database.shared.dbQueue.read { db in
try Row.fetchOne(
db,
sql: "SELECT max(id) FROM omemo_pre_keys WHERE account = :account",
arguments: ["account": credentials.bareJid]
)
}
return data?["id"] ?? 0
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return 0
}
}
func loadPreKey(withId: UInt32) -> Data? {
do {
let data = try Database.shared.dbQueue.read { db in
try Row.fetchOne(
db,
sql: "SELECT key FROM omemo_pre_keys WHERE account = :account AND id = :id",
arguments: ["account": credentials.bareJid, "id": withId]
)
}
return data?["key"]
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return nil
}
}
func storePreKey(_ data: Data, withId: UInt32) -> Bool {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: "INSERT INTO omemo_pre_keys (account, id, key) VALUES (:account, :id, :key)",
arguments: ["account": credentials.bareJid, "id": withId, "key": data]
)
}
return true
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
func containsPreKey(withId: UInt32) -> Bool {
do {
let rec = try Database.shared.dbQueue.read { db in
try Row.fetchOne(
db,
sql: "SELECT key FROM omemo_pre_keys WHERE account = :account AND id = :id",
arguments: ["account": credentials.bareJid, "id": withId]
)
}
return rec != nil
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
func deletePreKey(withId: UInt32) -> Bool {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: "DELETE FROM omemo_pre_keys WHERE account = :account AND id = :id",
arguments: ["account": credentials.bareJid, "id": withId]
)
}
return true
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
// TODO: Check logic of this function carefully!!!
func flushDeletedPreKeys() -> Bool {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: """
DELETE FROM omemo_pre_keys
WHERE account = :account
AND id IN
(SELECT id
FROM omemo_pre_keys
WHERE account = :account
AND id NOT IN (
SELECT id
FROM omemo_pre_keys
WHERE account = :account
ORDER BY id DESC
LIMIT 100)
)
""",
arguments: ["account": credentials.bareJid]
)
}
return true
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
func preKeysWipe() {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: "DELETE FROM omemo_pre_keys WHERE account = :account",
arguments: ["account": credentials.bareJid]
)
}
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
}
}
}
// MARK: - SignedPreKey
extension ClientMartinOMEMO: SignalSignedPreKeyStoreProtocol {
func countSignedPreKeys() -> Int {
do {
let data = try Database.shared.dbQueue.read { db in
try Row.fetchOne(
db,
sql: "SELECT count(1) FROM omemo_signed_pre_keys WHERE account = :account",
arguments: ["account": credentials.bareJid]
)
}
return data?["count(1)"] ?? 0
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return 0
}
}
func loadSignedPreKey(withId: UInt32) -> Data? {
do {
let data = try Database.shared.dbQueue.read { db in
try Row.fetchOne(
db,
sql: "SELECT key FROM omemo_signed_pre_keys WHERE account = :account AND id = :id",
arguments: ["account": credentials.bareJid, "id": withId]
)
}
return data?["key"]
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return nil
}
}
func storeSignedPreKey(_ data: Data, withId: UInt32) -> Bool {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: "INSERT INTO omemo_signed_pre_keys (account, id, key) VALUES (:account, :id, :key)",
arguments: ["account": credentials.bareJid, "id": withId, "key": data]
)
}
return true
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
func containsSignedPreKey(withId: UInt32) -> Bool {
do {
let rec = try Database.shared.dbQueue.read { db in
try Row.fetchOne(
db,
sql: "SELECT key FROM omemo_signed_pre_keys WHERE account = :account AND id = :id",
arguments: ["account": credentials.bareJid, "id": withId]
)
}
return rec != nil
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
func deleteSignedPreKey(withId: UInt32) -> Bool {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: "DELETE FROM omemo_signed_pre_keys WHERE account = :account AND id = :id",
arguments: ["account": credentials.bareJid, "id": withId]
)
}
return true
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
return false
}
}
func wipeSignedPreKeys() {
do {
try Database.shared.dbQueue.write { db in
try db.execute(
sql: "DELETE FROM omemo_signed_pre_keys WHERE account = :account",
arguments: ["account": credentials.bareJid]
)
}
} catch {
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
}
}
}
// MARK: - Identity
extension ClientMartinOMEMO: SignalIdentityKeyStoreProtocol {
func keyPair() -> (any MartinOMEMO.SignalIdentityKeyPairProtocol)? {
nil
}
func localRegistrationId() -> UInt32 {
0
}
func save(identity: MartinOMEMO.SignalAddress, key: (any MartinOMEMO.SignalIdentityKeyProtocol)?) -> Bool {
print(identity, key)
return false
}
func isTrusted(identity: MartinOMEMO.SignalAddress, key: (any MartinOMEMO.SignalIdentityKeyProtocol)?) -> Bool {
print(identity, key)
return false
}
func save(identity: MartinOMEMO.SignalAddress, publicKeyData: Data?) -> Bool {
print(identity, publicKeyData)
return false
}
func isTrusted(identity: MartinOMEMO.SignalAddress, publicKeyData: Data?) -> Bool {
print(identity, publicKeyData)
return false
}
func setStatus(_ status: MartinOMEMO.IdentityStatus, forIdentity: MartinOMEMO.SignalAddress) -> Bool {
print(status, forIdentity)
return false
}
func setStatus(active: Bool, forIdentity: MartinOMEMO.SignalAddress) -> Bool {
print(active, forIdentity)
return false
}
func identities(forName: String) -> [MartinOMEMO.Identity] {
print(forName)
return []
}
func identityFingerprint(forAddress address: MartinOMEMO.SignalAddress) -> String? {
print(address)
return nil
}
}
// MARK: - SenderKey
extension ClientMartinOMEMO: SignalSenderKeyStoreProtocol {
func storeSenderKey(_ key: Data, address: MartinOMEMO.SignalAddress?, groupId: String?) -> Bool {
print(key, address, groupId)
return false
}
func loadSenderKey(forAddress: MartinOMEMO.SignalAddress?, groupId: String?) -> Data? {
print(forAddress, groupId)
return nil
}
}