// // Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Foundation public import GRDB public import LibSignalClient extension Aci { /// Parses an ACI from its string representation. /// /// - Note: Call this only if you **expect** an `Aci` (or nil). If the /// result could be a `Pni`, you shouldn't call this method. public static func parseFrom(aciString: String?) -> Aci? { guard let aciString else { return nil } guard let serviceId = try? ServiceId.parseFrom(serviceIdString: aciString) else { return nil } guard let aci = serviceId as? Aci else { return nil } return aci } } extension Pni { public static func parseFrom(pniString: String?) -> Pni? { guard let pniString else { return nil } guard let pniUuid = UUID(uuidString: pniString) else { return nil } return Pni(fromUUID: pniUuid) } public static func parseFrom(ambiguousString: String?) -> Pni? { guard let ambiguousString else { return nil } // Give LibSignal the first pass at parsing a "PNI:"-prefixed value. return (try? Pni.parseFrom(serviceIdString: ambiguousString)) ?? parseFrom(pniString: ambiguousString) } } extension ServiceId { public enum ConcreteType { case aci(Aci) case pni(Pni) } public var concreteType: ConcreteType { switch kind { case .aci: return .aci(self as! Aci) case .pni: return .pni(self as! Pni) } } } extension ServiceId { public static func parseFrom(serviceIdBinary: Data?, serviceIdString: String?) -> Self? { if let serviceIdBinary { return try? Self.parseFrom(serviceIdBinary: serviceIdBinary) } if let serviceIdString { return try? Self.parseFrom(serviceIdString: serviceIdString) } return nil } } extension ProtocolAddress { public convenience init(_ serviceId: ServiceId, deviceId: DeviceId) { self.init(serviceId, deviceId: deviceId.uint32Value) } public var deviceIdObj: DeviceId { // LibSignal also enforces [1, 127], so this can't fail. return DeviceId(validating: self.deviceId)! } } public struct AtLeastOneServiceId { /// Non-Optional because we must have at least an ACI or a PNI. public let aciOrElsePni: ServiceId public let aci: Aci? public let pni: Pni? public init?(aci: Aci?, pni: Pni?) { guard let aciOrElsePni = aci ?? pni else { return nil } self.aciOrElsePni = aciOrElsePni self.aci = aci self.pni = pni } } /// An "address" that's written to disk in a DB record. /// /// This is the exact value that exists in the database, meaning that /// /// `self == self.normalizedValue?.persistableValue` /// /// may not always be true. public struct PersistableDatabaseRecordAddress: Equatable { public let serviceId: ServiceId? public let phoneNumber: String? public init(serviceId: ServiceId?, phoneNumber: String?) { self.serviceId = serviceId self.phoneNumber = phoneNumber } } /// A "normalized address" that's written to various DB records. /// /// New DB record types should generally store a foreign key to /// `SignalRecipient`, but existing types may store a ServiceId/E164 pair. /// /// For these older types, we often want to avoid storing phone numbers in /// cases where we already know the ACI. This type does that. public struct NormalizedDatabaseRecordAddress { public let serviceId: ServiceId? public let phoneNumber: String? public init(aci: Aci) { self.serviceId = aci self.phoneNumber = nil } private init(phoneNumber: String, pni: Pni?) { self.serviceId = pni self.phoneNumber = phoneNumber } private init(phoneNumber: String?, pni: Pni) { self.serviceId = pni self.phoneNumber = phoneNumber } public init?(aci: Aci?, phoneNumber: String?, pni: Pni?) { if let aci { self.init(aci: aci) } else if let pni { self.init(phoneNumber: phoneNumber, pni: pni) } else if let phoneNumber { self.init(phoneNumber: phoneNumber, pni: pni) } else { return nil } } public init?(serviceId: ServiceId?, phoneNumber: String?) { self.init(aci: serviceId as? Aci, phoneNumber: phoneNumber, pni: serviceId as? Pni) } public init?(serviceIdString: String?, phoneNumber: String?) { let serviceId = serviceIdString.flatMap { try? ServiceId.parseFrom(serviceIdString: $0) } self.init(serviceId: serviceId, phoneNumber: phoneNumber) } public init?(address: SignalServiceAddress?) { self.init(serviceId: address?.serviceId, phoneNumber: address?.phoneNumber) } public var persistableValue: PersistableDatabaseRecordAddress { return PersistableDatabaseRecordAddress(serviceId: serviceId, phoneNumber: phoneNumber) } } @objc public class ServiceIdObjC: NSObject, NSCopying { public var wrappedValue: ServiceId { owsFail("Subclasses must implement.") } override fileprivate init() { super.init() } public static func wrapValue(_ wrappedValue: ServiceId) -> ServiceIdObjC { switch wrappedValue.kind { case .aci: return AciObjC(wrappedValue as! Aci) case .pni: return PniObjC(wrappedValue as! Pni) } } @objc public static func parseFrom(serviceIdString: String?) -> ServiceIdObjC? { guard let serviceIdString, let wrappedValue = try? ServiceId.parseFrom(serviceIdString: serviceIdString) else { return nil } return wrapValue(wrappedValue) } @objc public var serviceIdString: String { wrappedValue.serviceIdString } @objc public var serviceIdBinary: Data { wrappedValue.serviceIdBinary } @objc public var serviceIdUppercaseString: String { wrappedValue.serviceIdUppercaseString } @objc public var rawUUID: UUID { wrappedValue.rawUUID } @objc override public var hash: Int { wrappedValue.hashValue } @objc override public func isEqual(_ object: Any?) -> Bool { wrappedValue == (object as? ServiceIdObjC)?.wrappedValue } @objc public func copy(with zone: NSZone? = nil) -> Any { self } @objc override public var description: String { wrappedValue.debugDescription } } @objc public final class AciObjC: ServiceIdObjC { public let wrappedAciValue: Aci override public var wrappedValue: ServiceId { wrappedAciValue } public init(_ wrappedValue: Aci) { self.wrappedAciValue = wrappedValue } @objc public init(uuidValue: UUID) { self.wrappedAciValue = Aci(fromUUID: uuidValue) } @objc public init?(aciString: String?) { guard let aciValue = Aci.parseFrom(aciString: aciString) else { return nil } self.wrappedAciValue = aciValue } } @objc public final class PniObjC: ServiceIdObjC { public let wrappedPniValue: Pni override public var wrappedValue: ServiceId { wrappedPniValue } public init(_ wrappedValue: Pni) { self.wrappedPniValue = wrappedValue } @objc public init(uuidValue: UUID) { self.wrappedPniValue = Pni(fromUUID: uuidValue) } } // MARK: - Codable @propertyWrapper public struct AciUuid: Codable, Equatable, Hashable, DatabaseValueConvertible { public let wrappedValue: Aci public init(wrappedValue: Aci) { self.wrappedValue = wrappedValue } public init(from decoder: Decoder) throws { self.wrappedValue = Aci(fromUUID: try decoder.singleValueContainer().decode(UUID.self)) } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self.wrappedValue.rawUUID) } public var databaseValue: DatabaseValue { wrappedValue.rawUUID.databaseValue } public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Self? { UUID.fromDatabaseValue(dbValue).map { Self(wrappedValue: Aci(fromUUID: $0)) } } } extension Aci { public var codableUuid: AciUuid { .init(wrappedValue: self) } } @propertyWrapper public struct PniUuid: Codable, Equatable, Hashable, DatabaseValueConvertible { public let wrappedValue: Pni public init(wrappedValue: Pni) { self.wrappedValue = wrappedValue } public init(from decoder: Decoder) throws { self.wrappedValue = Pni(fromUUID: try decoder.singleValueContainer().decode(UUID.self)) } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self.wrappedValue.rawUUID) } public var databaseValue: DatabaseValue { wrappedValue.rawUUID.databaseValue } public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Self? { UUID.fromDatabaseValue(dbValue).map { Self(wrappedValue: Pni(fromUUID: $0)) } } } extension Pni { public var codableUuid: PniUuid { .init(wrappedValue: self) } } @propertyWrapper public struct ServiceIdString: Codable, Hashable { public let wrappedValue: ServiceId public init(wrappedValue: ServiceId) { self.wrappedValue = wrappedValue } public init(from decoder: Decoder) throws { self.wrappedValue = try ServiceId.parseFrom( serviceIdString: try decoder.singleValueContainer().decode(String.self), ) } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self.wrappedValue.serviceIdString) } } @propertyWrapper public struct ServiceIdUppercaseString: Codable, Hashable { public let wrappedValue: T public init(wrappedValue: T) { self.wrappedValue = wrappedValue } public init(from decoder: Decoder) throws { self.wrappedValue = try T.parseFrom( serviceIdString: try decoder.singleValueContainer().decode(String.self), ) } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self.wrappedValue.serviceIdUppercaseString) } } extension ServiceId { public var codableUppercaseString: ServiceIdUppercaseString { .init(wrappedValue: self) } } // MARK: - Unit Tests #if TESTABLE_BUILD extension Aci { public static func randomForTesting() -> Aci { Aci(fromUUID: UUID()) } public static func constantForTesting(_ uuidString: String) -> Aci { try! ServiceId.parseFrom(serviceIdString: uuidString) as! Aci } } extension Pni { public static func randomForTesting() -> Pni { Pni(fromUUID: UUID()) } public static func constantForTesting(_ serviceIdString: String) -> Pni { try! ServiceId.parseFrom(serviceIdString: serviceIdString) as! Pni } } #endif