Signal-iOS/SignalServiceKit/Storage/RecipientIdFinder.swift
2024-03-27 12:22:52 -05:00

93 lines
3.6 KiB
Swift

//
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
import LibSignalClient
import SignalCoreKit
public typealias AccountId = String
@objc
public extension SignalRecipient {
var accountId: AccountId! {
return uniqueId
}
}
public enum RecipientIdError: Error, IsRetryableProvider {
/// We can't use the Pni because it's been replaced by an Aci.
case mustNotUsePniBecauseAciExists
public var isRetryableProvider: Bool {
// Allow retries so that we send to the Aci instead of the Pni.
return true
}
}
public final class RecipientIdFinder {
private let recipientDatabaseTable: RecipientDatabaseTable
private let recipientFetcher: RecipientFetcher
public init(
recipientDatabaseTable: RecipientDatabaseTable,
recipientFetcher: RecipientFetcher
) {
self.recipientFetcher = recipientFetcher
self.recipientDatabaseTable = recipientDatabaseTable
}
public func recipientId(for serviceId: ServiceId, tx: DBReadTransaction) -> Result<AccountId, RecipientIdError>? {
guard let recipient = recipientDatabaseTable.fetchRecipient(serviceId: serviceId, transaction: tx) else {
return nil
}
return recipientIdResult(for: serviceId, recipient: recipient)
}
public func recipientId(for address: SignalServiceAddress, tx: DBReadTransaction) -> Result<AccountId, RecipientIdError>? {
guard let recipient = SignalRecipient.fetchRecipient(for: address, onlyIfRegistered: false, tx: SDSDB.shimOnlyBridge(tx)) else {
return nil
}
return recipientIdResult(for: address.serviceId, recipient: recipient)
}
public func ensureRecipientId(for serviceId: ServiceId, tx: DBWriteTransaction) -> Result<AccountId, RecipientIdError> {
let recipient = recipientFetcher.fetchOrCreate(serviceId: serviceId, tx: tx)
return recipientIdResult(for: serviceId, recipient: recipient)
}
private func recipientIdResult(for serviceId: ServiceId?, recipient: SignalRecipient) -> Result<AccountId, RecipientIdError> {
if serviceId is Pni, recipient.aciString != nil {
return .failure(.mustNotUsePniBecauseAciExists)
}
return .success(recipient.uniqueId)
}
}
public final class OWSAccountIdFinder {
public class func ensureRecipient(
forAddress address: SignalServiceAddress,
transaction: SDSAnyWriteTransaction
) -> SignalRecipient {
let recipientFetcher = DependenciesBridge.shared.recipientFetcher
let recipient: SignalRecipient
if let serviceId = address.serviceId {
recipient = recipientFetcher.fetchOrCreate(serviceId: serviceId, tx: transaction.asV2Write)
} else if let phoneNumber = address.e164 {
recipient = recipientFetcher.fetchOrCreate(phoneNumber: phoneNumber, tx: transaction.asV2Write)
} else {
// This can happen for historical reasons. It shouldn't happen, but it
// could. We could return [[NSUUID UUID] UUIDString] and avoid persisting
// anything to disk. However, it's possible that a caller may expect to be
// able to fetch the recipient based on the value we return, so we need to
// ensure that the return value can be fetched. In the future, we should
// update all callers to ensure they pass valid addresses.
owsFailDebug("Fetching accountId for invalid address.")
recipient = SignalRecipient(aci: nil, pni: nil, phoneNumber: nil)
recipient.anyInsert(transaction: transaction)
}
return recipient
}
}