113 lines
3.4 KiB
Swift
113 lines
3.4 KiB
Swift
//
|
|
// Copyright 2016 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import SignalServiceKit
|
|
|
|
class SyncPushTokensJob: NSObject {
|
|
enum Mode {
|
|
case normal
|
|
case forceRotation
|
|
case rotateIfEligible
|
|
}
|
|
|
|
private let mode: Mode
|
|
|
|
init(mode: Mode) {
|
|
self.mode = mode
|
|
}
|
|
|
|
private static let hasUploadedTokensOnce = AtomicBool(false, lock: .sharedGlobal)
|
|
|
|
func run() async throws {
|
|
switch mode {
|
|
case .normal:
|
|
// Don't rotate.
|
|
return try await run(shouldRotateAPNSToken: false)
|
|
case .forceRotation:
|
|
// Always rotate
|
|
return try await run(shouldRotateAPNSToken: true)
|
|
case .rotateIfEligible:
|
|
let shouldRotate = SSKEnvironment.shared.databaseStorageRef.read { tx -> Bool in
|
|
return APNSRotationStore.canRotateAPNSToken(transaction: tx)
|
|
}
|
|
guard shouldRotate else {
|
|
// If we aren't rotating, no-op.
|
|
return
|
|
}
|
|
return try await run(shouldRotateAPNSToken: true)
|
|
}
|
|
}
|
|
|
|
typealias ApnRegistrationId = RegistrationRequestFactory.ApnRegistrationId
|
|
|
|
private func run(shouldRotateAPNSToken: Bool) async throws {
|
|
let regResult = try await AppEnvironment.shared.pushRegistrationManagerRef.requestPushTokens(forceRotation: shouldRotateAPNSToken)
|
|
|
|
await SSKEnvironment.shared.databaseStorageRef.awaitableWrite { tx in
|
|
if shouldRotateAPNSToken {
|
|
APNSRotationStore.didRotateAPNSToken(transaction: tx)
|
|
}
|
|
}
|
|
|
|
let pushToken = regResult.apnsToken
|
|
|
|
let reason: String
|
|
|
|
if SSKEnvironment.shared.preferencesRef.pushToken != pushToken {
|
|
reason = "changed"
|
|
} else if !Self.hasUploadedTokensOnce.get() {
|
|
reason = "launched"
|
|
} else {
|
|
return
|
|
}
|
|
|
|
Logger.info("uploading push token; reason: \(reason), pushToken: \(redact(pushToken))")
|
|
try await self.updatePushTokens(pushToken: pushToken)
|
|
|
|
await recordPushTokensLocally(pushToken: pushToken)
|
|
|
|
Self.hasUploadedTokensOnce.set(true)
|
|
}
|
|
|
|
class func run(mode: Mode = .normal) {
|
|
Task {
|
|
do {
|
|
try await SyncPushTokensJob(mode: mode).run()
|
|
} catch {
|
|
Logger.error("Error: \(error).")
|
|
}
|
|
}
|
|
}
|
|
|
|
private func recordPushTokensLocally(pushToken: String) async {
|
|
assert(!Thread.isMainThread)
|
|
|
|
await SSKEnvironment.shared.databaseStorageRef.awaitableWrite { tx in
|
|
if pushToken != SSKEnvironment.shared.preferencesRef.getPushToken(tx: tx) {
|
|
Logger.info("saved new push token: \(redact(pushToken))")
|
|
SSKEnvironment.shared.preferencesRef.setPushToken(pushToken, tx: tx)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Requests
|
|
|
|
private func updatePushTokens(pushToken: String) async throws {
|
|
return try await Retry.performWithBackoff(maxAttempts: 3) {
|
|
let request = OWSRequestFactory.registerForPushRequest(apnsToken: pushToken)
|
|
_ = try await SSKEnvironment.shared.networkManagerRef.asyncRequest(request)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func redact(_ string: String?) -> String {
|
|
guard let string else { return "nil" }
|
|
#if DEBUG
|
|
return string
|
|
#else
|
|
return "\(string.prefix(2))…\(string.suffix(2))"
|
|
#endif
|
|
}
|