mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-18 23:48:29 +02:00
convert DecryptionFailureTracker to swift + tests
This commit is contained in:
138
Riot/Modules/Analytics/DecryptionFailureTracker.swift
Normal file
138
Riot/Modules/Analytics/DecryptionFailureTracker.swift
Normal file
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
// Protocol to get the current time. Used for easy testing
|
||||
protocol TimeProvider {
|
||||
func nowTs() -> TimeInterval
|
||||
}
|
||||
|
||||
class DefaultTimeProvider: TimeProvider {
|
||||
|
||||
func nowTs() -> TimeInterval {
|
||||
return Date.now.timeIntervalSince1970
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@objc
|
||||
class DecryptionFailureTracker: NSObject {
|
||||
|
||||
let GRACE_PERIOD: TimeInterval = 4
|
||||
// Call `checkFailures` every `CHECK_INTERVAL`
|
||||
let CHECK_INTERVAL: TimeInterval = 2
|
||||
|
||||
@objc weak var delegate: E2EAnalytics?
|
||||
|
||||
// Reported failures
|
||||
var reportedFailures = [String /* eventId */: DecryptionFailure]()
|
||||
|
||||
// Event ids of failures that were tracked previously
|
||||
var trackedEvents = Set<String>()
|
||||
|
||||
var checkFailuresTimer: Timer?
|
||||
|
||||
@objc static let sharedInstance = DecryptionFailureTracker()
|
||||
|
||||
var timeProvider: TimeProvider = DefaultTimeProvider()
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(eventDidDecrypt(_:)),
|
||||
name: .mxEventDidDecrypt,
|
||||
object: nil)
|
||||
|
||||
Timer.scheduledTimer(withTimeInterval: CHECK_INTERVAL, repeats: true) { [weak self] _ in
|
||||
self?.checkFailures()
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func reportUnableToDecryptError(forEvent event: MXEvent, withRoomState roomState: MXRoomState, myUser userId: String) {
|
||||
if reportedFailures[event.eventId] != nil || trackedEvents.contains(event.eventId) {
|
||||
return
|
||||
}
|
||||
|
||||
// Filter out "expected" UTDs
|
||||
// We cannot decrypt messages sent before the user joined the room
|
||||
guard let myUser = roomState.members.member(withUserId: userId) else { return }
|
||||
if myUser.membership != MXMembership.join {
|
||||
return
|
||||
}
|
||||
|
||||
guard let failedEventId = event.eventId else { return }
|
||||
|
||||
guard let error = event.decryptionError as? NSError else { return }
|
||||
|
||||
var reason = DecryptionFailureReason.unspecified
|
||||
|
||||
if error.code == MXDecryptingErrorUnknownInboundSessionIdCode.rawValue {
|
||||
reason = DecryptionFailureReason.olmKeysNotSent
|
||||
} else if error.code == MXDecryptingErrorOlmCode.rawValue {
|
||||
reason = DecryptionFailureReason.olmIndexError
|
||||
}
|
||||
|
||||
let context = String(format: "code: %ld, description: %@", error.code, event.decryptionError.localizedDescription)
|
||||
|
||||
reportedFailures[failedEventId] = DecryptionFailure(failedEventId: failedEventId, reason: reason, context: context, ts: self.timeProvider.nowTs())
|
||||
}
|
||||
|
||||
@objc
|
||||
func dispatch() {
|
||||
self.checkFailures()
|
||||
}
|
||||
|
||||
@objc
|
||||
func eventDidDecrypt(_ notification: Notification) {
|
||||
guard let event = notification.object as? MXEvent else { return }
|
||||
|
||||
// Could be an event in the reportedFailures, remove it
|
||||
reportedFailures.removeValue(forKey: event.eventId)
|
||||
}
|
||||
|
||||
/**
|
||||
Mark reported failures that occured before tsNow - GRACE_PERIOD as failures that should be
|
||||
tracked.
|
||||
*/
|
||||
@objc
|
||||
func checkFailures() {
|
||||
guard let delegate = self.delegate else {return}
|
||||
|
||||
|
||||
let tsNow = self.timeProvider.nowTs()
|
||||
var failuresToCheck = [DecryptionFailure]()
|
||||
|
||||
for reportedFailure in self.reportedFailures.values {
|
||||
let ellapsed = tsNow - reportedFailure.ts
|
||||
if ellapsed > GRACE_PERIOD {
|
||||
failuresToCheck.append(reportedFailure)
|
||||
reportedFailures.removeValue(forKey: reportedFailure.failedEventId)
|
||||
trackedEvents.insert(reportedFailure.failedEventId)
|
||||
}
|
||||
}
|
||||
|
||||
for failure in failuresToCheck {
|
||||
delegate.trackE2EEError(failure.reason, context: failure.context)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user