session-ios/Session/Signal/SessionResetJob.swift

195 lines
7.2 KiB
Swift
Raw Normal View History

2017-04-07 02:13:38 +02:00
//
2018-01-30 21:49:36 +01:00
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
2017-04-07 02:13:38 +02:00
//
import Foundation
import PromiseKit
2020-11-11 07:45:50 +01:00
import SignalUtilitiesKit
@objc(OWSSessionResetJobQueue)
public class SessionResetJobQueue: NSObject, JobQueue {
@objc(addContactThread:transaction:)
public func add(contactThread: TSContactThread, transaction: YapDatabaseReadWriteTransaction) {
let jobRecord = OWSSessionResetJobRecord(contactThread: contactThread, label: self.jobRecordLabel)
self.add(jobRecord: jobRecord, transaction: transaction)
}
// MARK: JobQueue
public typealias DurableOperationType = SessionResetOperation
public let jobRecordLabel: String = "SessionReset"
public static let maxRetries: UInt = 10
2018-10-22 18:53:14 +02:00
public let requiresInternet: Bool = true
public var runningOperations: [SessionResetOperation] = []
@objc
public override init() {
super.init()
AppReadiness.runNowOrWhenAppWillBecomeReady {
self.setup()
}
}
@objc
public func setup() {
defaultSetup()
}
public var isSetup: Bool = false
public func didMarkAsReady(oldJobRecord: JobRecordType, transaction: YapDatabaseReadWriteTransaction) {
// no special handling
}
let operationQueue: OperationQueue = {
// no need to serialize the operation queuing, since sending will ultimately be serialized by MessageSender
let operationQueue = OperationQueue()
operationQueue.name = "SessionReset.OperationQueue"
return operationQueue
}()
public func operationQueue(jobRecord: OWSSessionResetJobRecord) -> OperationQueue {
return self.operationQueue
}
public func buildOperation(jobRecord: OWSSessionResetJobRecord, transaction: YapDatabaseReadTransaction) throws -> SessionResetOperation {
guard let contactThread = TSThread.fetch(uniqueId: jobRecord.contactThreadId, transaction: transaction) as? TSContactThread else {
throw JobError.obsolete(description: "thread for session reset no longer exists")
}
return SessionResetOperation(contactThread: contactThread, jobRecord: jobRecord)
}
}
public class SessionResetOperation: OWSOperation, DurableOperation {
// MARK: DurableOperation
public let jobRecord: OWSSessionResetJobRecord
weak public var durableOperationDelegate: SessionResetJobQueue?
2018-10-22 18:53:14 +02:00
public var operation: OWSOperation {
return self
}
// MARK:
let contactThread: TSContactThread
var recipientId: String {
return contactThread.contactIdentifier()
}
@objc public required init(contactThread: TSContactThread, jobRecord: OWSSessionResetJobRecord) {
self.contactThread = contactThread
self.jobRecord = jobRecord
}
// MARK: Dependencies
var dbConnection: YapDatabaseConnection {
return SSKEnvironment.shared.primaryStorage.dbReadWriteConnection
}
var primaryStorage: OWSPrimaryStorage {
return SSKEnvironment.shared.primaryStorage
}
// MARK:
var firstAttempt = true
override public func run() {
assert(self.durableOperationDelegate != nil)
2020-11-16 00:34:47 +01:00
/*
let endSessionMessage = EndSessionMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: self.contactThread)
firstly {
return self.messageSender.sendPromise(message: endSessionMessage)
}.done {
Logger.info("successfully sent EndSessionMessage.")
2020-11-04 01:46:30 +01:00
Storage.writeSync { transaction in
// Archive the just-created session since the recipient should delete their corresponding
// session upon receiving and decrypting our EndSession message.
// Otherwise if we send another message before them, they wont have the session to decrypt it.
self.primaryStorage.archiveAllSessions(forContact: self.recipientId, protocolContext: transaction)
2019-05-20 03:20:03 +02:00
/* Loki: Original code
* ================
let message = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(),
in: self.contactThread,
messageType: TSInfoMessageType.typeSessionDidEnd)
message.save(with: transaction)
2019-05-20 03:20:03 +02:00
* ================
*/
2020-02-17 01:24:16 +01:00
if (self.contactThread.sessionResetStatus != .requestReceived) {
2019-05-20 04:31:21 +02:00
let message = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: self.contactThread, messageType: .typeLokiSessionResetInProgress)
message.save(with: transaction)
2019-05-20 03:20:03 +02:00
// Loki: We have initiated a session reset
2020-11-20 00:14:35 +01:00
SNLog("Session reset initiated.")
2020-02-17 01:24:16 +01:00
self.contactThread.sessionResetStatus = .initiated
self.contactThread.save(with: transaction)
}
2018-02-02 18:56:55 +01:00
}
self.reportSuccess()
}.catch { error in
Logger.error("sending error: \(error.localizedDescription)")
self.reportError(error)
}.retainUntilComplete()
2020-11-16 00:34:47 +01:00
*/
}
override public func didSucceed() {
2020-11-04 01:46:30 +01:00
Storage.writeSync { transaction in
self.durableOperationDelegate?.durableOperationDidSucceed(self, transaction: transaction)
}
}
override public func didReportError(_ error: Error) {
Logger.debug("remainingRetries: \(self.remainingRetries)")
2020-11-04 01:46:30 +01:00
Storage.writeSync { transaction in
self.durableOperationDelegate?.durableOperation(self, didReportError: error, transaction: transaction)
}
}
2018-10-22 18:53:14 +02:00
override public func retryInterval() -> TimeInterval {
// Arbitrary backoff factor...
2018-10-23 17:12:16 +02:00
// With backOffFactor of 1.9
// try 1 delay: 0.00s
// try 2 delay: 0.19s
// ...
// try 5 delay: 1.30s
// ...
// try 11 delay: 61.31s
let backoffFactor = 1.9
let maxBackoff = kHourInterval
let seconds = 0.1 * min(maxBackoff, pow(backoffFactor, Double(self.jobRecord.failureCount)))
2018-10-22 18:53:14 +02:00
return seconds
}
override public func didFail(error: Error) {
Logger.error("failed to send EndSessionMessage with error: \(error.localizedDescription)")
2020-11-04 01:46:30 +01:00
Storage.writeSync { transaction in
self.durableOperationDelegate?.durableOperation(self, didFailWithError: error, transaction: transaction)
// Even though this is the failure handler - which means probably the recipient didn't receive the message
// there's a chance that our send did succeed and the server just timed out our repsonse or something.
// Since the cost of sending a future message using a session the recipient doesn't have is so high,
// we archive the session just in case.
//
// Archive the just-created session since the recipient should delete their corresponding
// session upon receiving and decrypting our EndSession message.
// Otherwise if we send another message before them, they wont have the session to decrypt it.
self.primaryStorage.archiveAllSessions(forContact: self.recipientId, protocolContext: transaction)
}
}
}