145 lines
5.0 KiB
Swift
145 lines
5.0 KiB
Swift
//
|
|
// Copyright 2023 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import LibSignalClient
|
|
import SignalCoreKit
|
|
|
|
/// It is possible that a user does not know their own PNI. However, in order to
|
|
/// be PNP-compatible, a user's PNI and PNI identity key must be synced between
|
|
/// the client and service. This manager handles syncing the PNI and related
|
|
/// keys as necessary.
|
|
public protocol LearnMyOwnPniManager {
|
|
func learnMyOwnPniIfNecessary() -> Promise<Void>
|
|
}
|
|
|
|
final class LearnMyOwnPniManagerImpl: LearnMyOwnPniManager {
|
|
fileprivate static let logger = PrefixedLogger(prefix: "LMOPMI")
|
|
private var logger: PrefixedLogger { Self.logger }
|
|
|
|
private let accountServiceClient: Shims.AccountServiceClient
|
|
private let registrationStateChangeManager: RegistrationStateChangeManager
|
|
private let tsAccountManager: TSAccountManager
|
|
|
|
private let db: DB
|
|
private let schedulers: Schedulers
|
|
|
|
init(
|
|
accountServiceClient: Shims.AccountServiceClient,
|
|
db: DB,
|
|
registrationStateChangeManager: RegistrationStateChangeManager,
|
|
schedulers: Schedulers,
|
|
tsAccountManager: TSAccountManager
|
|
) {
|
|
self.accountServiceClient = accountServiceClient
|
|
self.registrationStateChangeManager = registrationStateChangeManager
|
|
self.tsAccountManager = tsAccountManager
|
|
|
|
self.db = db
|
|
self.schedulers = schedulers
|
|
}
|
|
|
|
/// Wrap everything in a chained promise so we don't issue requests
|
|
/// concurrently and end up with broken state.
|
|
///
|
|
/// Before making requests (but after getting dequeued) each call will
|
|
/// check local state to see if things corrected while enqueued, so
|
|
/// enqueuing is cheap.
|
|
///
|
|
/// - Note
|
|
/// This only ever gets called in one place, on app launch, so this safety
|
|
/// is overkill. But it doesn't hurt to have.
|
|
private lazy var learnMyOwnPniChainedPromise = ChainedPromise(scheduler: schedulers.main)
|
|
|
|
func learnMyOwnPniIfNecessary() -> Promise<Void> {
|
|
return learnMyOwnPniChainedPromise.enqueue {
|
|
return self.db.read { tx in
|
|
self._learnMyOwnPniIfNecessary(tx: tx)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func _learnMyOwnPniIfNecessary(tx: DBReadTransaction) -> Promise<Void> {
|
|
guard tsAccountManager.registrationState(tx: tx).isPrimaryDevice ?? true else {
|
|
return .value(())
|
|
}
|
|
|
|
guard let localIdentifiers = tsAccountManager.localIdentifiers(tx: tx) else {
|
|
logger.warn("Skipping PNI learning, no local identifiers!")
|
|
return .value(())
|
|
}
|
|
|
|
return firstly(on: schedulers.sync) { () -> Promise<Void> in
|
|
return self.fetchMyPniIfNecessary(localIdentifiers: localIdentifiers)
|
|
}.recover(on: schedulers.sync) { error in
|
|
self.logger.error("Error learning local PNI! \(error)")
|
|
throw error
|
|
}
|
|
}
|
|
|
|
private func fetchMyPniIfNecessary(localIdentifiers: LocalIdentifiers) -> Promise<Void> {
|
|
if localIdentifiers.pni != nil {
|
|
return .value(())
|
|
}
|
|
|
|
return firstly(on: self.schedulers.sync) { () -> Promise<WhoAmIRequestFactory.Responses.WhoAmI> in
|
|
self.accountServiceClient.getAccountWhoAmI()
|
|
}.map(on: schedulers.global()) { whoAmI in
|
|
let remoteE164 = whoAmI.e164
|
|
let remoteAci = whoAmI.aci
|
|
let remotePni = whoAmI.pni
|
|
|
|
self.logger.info("Successfully fetched PNI: \(remotePni)")
|
|
|
|
guard
|
|
localIdentifiers.aci == remoteAci,
|
|
localIdentifiers.contains(phoneNumber: remoteE164)
|
|
else {
|
|
throw OWSGenericError(
|
|
"Remote ACI \(remoteAci) and e164 \(remoteE164) were not present in local identifiers, skipping PNI save."
|
|
)
|
|
}
|
|
|
|
self.db.write { tx in
|
|
self.registrationStateChangeManager.didUpdateLocalPhoneNumber(
|
|
remoteE164,
|
|
aci: remoteAci,
|
|
pni: remotePni,
|
|
tx: tx
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Dependencies
|
|
|
|
extension LearnMyOwnPniManagerImpl {
|
|
enum Shims {
|
|
typealias AccountServiceClient = _LearnMyOwnPniManagerImpl_AccountServiceClient_Shim
|
|
}
|
|
|
|
enum Wrappers {
|
|
typealias AccountServiceClient = _LearnMyOwnPniManagerImpl_AccountServiceClient_Wrapper
|
|
}
|
|
}
|
|
|
|
// MARK: AccountServiceClient
|
|
|
|
protocol _LearnMyOwnPniManagerImpl_AccountServiceClient_Shim {
|
|
func getAccountWhoAmI() -> Promise<WhoAmIRequestFactory.Responses.WhoAmI>
|
|
}
|
|
|
|
class _LearnMyOwnPniManagerImpl_AccountServiceClient_Wrapper: _LearnMyOwnPniManagerImpl_AccountServiceClient_Shim {
|
|
private let accountServiceClient: AccountServiceClient
|
|
|
|
init(_ accountServiceClient: AccountServiceClient) {
|
|
self.accountServiceClient = accountServiceClient
|
|
}
|
|
|
|
public func getAccountWhoAmI() -> Promise<WhoAmIRequestFactory.Responses.WhoAmI> {
|
|
accountServiceClient.getAccountWhoAmI()
|
|
}
|
|
}
|