mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
47314bd639
Fixed a bug where the notification count could be increased for message requests Fixed a bug where an approved contact could be 'unapproved' due to an order of execution issue when generating the config sync message Fixed a check to avoid registering for push notifications when on the simulator (old check didn't cater for M1 Macs) Moved the 'hasHiddenMessageRequests' into the group user defaults so it can be accessed within the notification extension Added code to handle an edge case where an old client could incorrectly un-approve a contact via a legacy configuration message
298 lines
14 KiB
Swift
298 lines
14 KiB
Swift
import SessionUtilitiesKit
|
|
|
|
@objc(SNConfigurationMessage)
|
|
public final class ConfigurationMessage : ControlMessage {
|
|
public var closedGroups: Set<ClosedGroup> = []
|
|
public var openGroups: Set<String> = []
|
|
public var displayName: String?
|
|
public var profilePictureURL: String?
|
|
public var profileKey: Data?
|
|
public var contacts: Set<Contact> = []
|
|
|
|
public override var isSelfSendValid: Bool { true }
|
|
|
|
// MARK: Initialization
|
|
public override init() { super.init() }
|
|
|
|
public init(displayName: String?, profilePictureURL: String?, profileKey: Data?, closedGroups: Set<ClosedGroup>, openGroups: Set<String>, contacts: Set<Contact>) {
|
|
super.init()
|
|
self.displayName = displayName
|
|
self.profilePictureURL = profilePictureURL
|
|
self.profileKey = profileKey
|
|
self.closedGroups = closedGroups
|
|
self.openGroups = openGroups
|
|
self.contacts = contacts
|
|
}
|
|
|
|
// MARK: Coding
|
|
public required init?(coder: NSCoder) {
|
|
super.init(coder: coder)
|
|
if let closedGroups = coder.decodeObject(forKey: "closedGroups") as! Set<ClosedGroup>? { self.closedGroups = closedGroups }
|
|
if let openGroups = coder.decodeObject(forKey: "openGroups") as! Set<String>? { self.openGroups = openGroups }
|
|
if let displayName = coder.decodeObject(forKey: "displayName") as! String? { self.displayName = displayName }
|
|
if let profilePictureURL = coder.decodeObject(forKey: "profilePictureURL") as! String? { self.profilePictureURL = profilePictureURL }
|
|
if let profileKey = coder.decodeObject(forKey: "profileKey") as! Data? { self.profileKey = profileKey }
|
|
if let contacts = coder.decodeObject(forKey: "contacts") as! Set<Contact>? { self.contacts = contacts }
|
|
}
|
|
|
|
public override func encode(with coder: NSCoder) {
|
|
super.encode(with: coder)
|
|
coder.encode(closedGroups, forKey: "closedGroups")
|
|
coder.encode(openGroups, forKey: "openGroups")
|
|
coder.encode(displayName, forKey: "displayName")
|
|
coder.encode(profilePictureURL, forKey: "profilePictureURL")
|
|
coder.encode(profileKey, forKey: "profileKey")
|
|
coder.encode(contacts, forKey: "contacts")
|
|
}
|
|
|
|
// MARK: Proto Conversion
|
|
public override class func fromProto(_ proto: SNProtoContent) -> ConfigurationMessage? {
|
|
guard let configurationProto = proto.configurationMessage else { return nil }
|
|
let displayName = configurationProto.displayName
|
|
let profilePictureURL = configurationProto.profilePicture
|
|
let profileKey = configurationProto.profileKey
|
|
let closedGroups = Set(configurationProto.closedGroups.compactMap { ClosedGroup.fromProto($0) })
|
|
let openGroups = Set(configurationProto.openGroups)
|
|
let contacts = Set(configurationProto.contacts.compactMap { Contact.fromProto($0) })
|
|
return ConfigurationMessage(displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey,
|
|
closedGroups: closedGroups, openGroups: openGroups, contacts: contacts)
|
|
}
|
|
|
|
public override func toProto(using transaction: YapDatabaseReadWriteTransaction) -> SNProtoContent? {
|
|
let configurationProto = SNProtoConfigurationMessage.builder()
|
|
if let displayName = displayName { configurationProto.setDisplayName(displayName) }
|
|
if let profilePictureURL = profilePictureURL { configurationProto.setProfilePicture(profilePictureURL) }
|
|
if let profileKey = profileKey { configurationProto.setProfileKey(profileKey) }
|
|
configurationProto.setClosedGroups(closedGroups.compactMap { $0.toProto() })
|
|
configurationProto.setOpenGroups([String](openGroups))
|
|
configurationProto.setContacts(contacts.compactMap { $0.toProto() })
|
|
let contentProto = SNProtoContent.builder()
|
|
do {
|
|
contentProto.setConfigurationMessage(try configurationProto.build())
|
|
return try contentProto.build()
|
|
} catch {
|
|
SNLog("Couldn't construct configuration proto from: \(self).")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// MARK: Description
|
|
public override var description: String {
|
|
"""
|
|
ConfigurationMessage(
|
|
closedGroups: \([ClosedGroup](closedGroups).prettifiedDescription),
|
|
openGroups: \([String](openGroups).prettifiedDescription),
|
|
displayName: \(displayName ?? "null"),
|
|
profilePictureURL: \(profilePictureURL ?? "null"),
|
|
profileKey: \(profileKey?.toHexString() ?? "null"),
|
|
contacts: \([Contact](contacts).prettifiedDescription)
|
|
)
|
|
"""
|
|
}
|
|
}
|
|
|
|
// MARK: Closed Group
|
|
extension ConfigurationMessage {
|
|
|
|
@objc(SNClosedGroup)
|
|
public final class ClosedGroup : NSObject, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
|
|
public let publicKey: String
|
|
public let name: String
|
|
public let encryptionKeyPair: ECKeyPair
|
|
public let members: Set<String>
|
|
public let admins: Set<String>
|
|
public let expirationTimer: UInt32
|
|
|
|
public var isValid: Bool { !members.isEmpty && !admins.isEmpty }
|
|
|
|
public init(publicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: Set<String>, admins: Set<String>, expirationTimer: UInt32) {
|
|
self.publicKey = publicKey
|
|
self.name = name
|
|
self.encryptionKeyPair = encryptionKeyPair
|
|
self.members = members
|
|
self.admins = admins
|
|
self.expirationTimer = expirationTimer
|
|
}
|
|
|
|
public required init?(coder: NSCoder) {
|
|
guard let publicKey = coder.decodeObject(forKey: "publicKey") as! String?,
|
|
let name = coder.decodeObject(forKey: "name") as! String?,
|
|
let encryptionKeyPair = coder.decodeObject(forKey: "encryptionKeyPair") as! ECKeyPair?,
|
|
let members = coder.decodeObject(forKey: "members") as! Set<String>?,
|
|
let admins = coder.decodeObject(forKey: "admins") as! Set<String>? else { return nil }
|
|
let expirationTimer = coder.decodeObject(forKey: "expirationTimer") as? UInt32 ?? 0
|
|
self.publicKey = publicKey
|
|
self.name = name
|
|
self.encryptionKeyPair = encryptionKeyPair
|
|
self.members = members
|
|
self.admins = admins
|
|
self.expirationTimer = expirationTimer
|
|
}
|
|
|
|
public func encode(with coder: NSCoder) {
|
|
coder.encode(publicKey, forKey: "publicKey")
|
|
coder.encode(name, forKey: "name")
|
|
coder.encode(encryptionKeyPair, forKey: "encryptionKeyPair")
|
|
coder.encode(members, forKey: "members")
|
|
coder.encode(admins, forKey: "admins")
|
|
coder.encode(expirationTimer, forKey: "expirationTimer")
|
|
}
|
|
|
|
public static func fromProto(_ proto: SNProtoConfigurationMessageClosedGroup) -> ClosedGroup? {
|
|
guard let publicKey = proto.publicKey?.toHexString(),
|
|
let name = proto.name,
|
|
let encryptionKeyPairAsProto = proto.encryptionKeyPair else { return nil }
|
|
let encryptionKeyPair: ECKeyPair
|
|
do {
|
|
encryptionKeyPair = try ECKeyPair(publicKeyData: encryptionKeyPairAsProto.publicKey, privateKeyData: encryptionKeyPairAsProto.privateKey)
|
|
} catch {
|
|
SNLog("Couldn't construct closed group from proto: \(self).")
|
|
return nil
|
|
}
|
|
let members = Set(proto.members.map { $0.toHexString() })
|
|
let admins = Set(proto.admins.map { $0.toHexString() })
|
|
let expirationTimer = proto.expirationTimer
|
|
let result = ClosedGroup(publicKey: publicKey, name: name, encryptionKeyPair: encryptionKeyPair, members: members, admins: admins, expirationTimer: expirationTimer)
|
|
guard result.isValid else { return nil }
|
|
return result
|
|
}
|
|
|
|
public func toProto() -> SNProtoConfigurationMessageClosedGroup? {
|
|
guard isValid else { return nil }
|
|
let result = SNProtoConfigurationMessageClosedGroup.builder()
|
|
result.setPublicKey(Data(hex: publicKey))
|
|
result.setName(name)
|
|
do {
|
|
let encryptionKeyPairAsProto = try SNProtoKeyPair.builder(publicKey: encryptionKeyPair.publicKey, privateKey: encryptionKeyPair.privateKey).build()
|
|
result.setEncryptionKeyPair(encryptionKeyPairAsProto)
|
|
} catch {
|
|
SNLog("Couldn't construct closed group proto from: \(self).")
|
|
return nil
|
|
}
|
|
result.setMembers(members.map { Data(hex: $0) })
|
|
result.setAdmins(admins.map { Data(hex: $0) })
|
|
result.setExpirationTimer(expirationTimer)
|
|
do {
|
|
return try result.build()
|
|
} catch {
|
|
SNLog("Couldn't construct closed group proto from: \(self).")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public override var description: String { name }
|
|
}
|
|
}
|
|
|
|
// MARK: Contact
|
|
extension ConfigurationMessage {
|
|
|
|
@objc(SNConfigurationMessageContact)
|
|
public final class Contact : NSObject, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
|
|
public var publicKey: String?
|
|
public var displayName: String?
|
|
public var profilePictureURL: String?
|
|
public var profileKey: Data?
|
|
|
|
public var hasIsApproved: Bool
|
|
public var isApproved: Bool
|
|
public var hasIsBlocked: Bool
|
|
public var isBlocked: Bool
|
|
public var hasDidApproveMe: Bool
|
|
public var didApproveMe: Bool
|
|
|
|
public var isValid: Bool { publicKey != nil && displayName != nil }
|
|
|
|
public init(
|
|
publicKey: String,
|
|
displayName: String,
|
|
profilePictureURL: String?,
|
|
profileKey: Data?,
|
|
hasIsApproved: Bool,
|
|
isApproved: Bool,
|
|
hasIsBlocked: Bool,
|
|
isBlocked: Bool,
|
|
hasDidApproveMe: Bool,
|
|
didApproveMe: Bool
|
|
) {
|
|
self.publicKey = publicKey
|
|
self.displayName = displayName
|
|
self.profilePictureURL = profilePictureURL
|
|
self.profileKey = profileKey
|
|
self.hasIsApproved = hasIsApproved
|
|
self.isApproved = isApproved
|
|
self.hasIsBlocked = hasIsBlocked
|
|
self.isBlocked = isBlocked
|
|
self.hasDidApproveMe = hasDidApproveMe
|
|
self.didApproveMe = didApproveMe
|
|
}
|
|
|
|
public required init?(coder: NSCoder) {
|
|
guard let publicKey = coder.decodeObject(forKey: "publicKey") as! String?,
|
|
let displayName = coder.decodeObject(forKey: "displayName") as! String? else { return nil }
|
|
self.publicKey = publicKey
|
|
self.displayName = displayName
|
|
self.profilePictureURL = coder.decodeObject(forKey: "profilePictureURL") as! String?
|
|
self.profileKey = coder.decodeObject(forKey: "profileKey") as! Data?
|
|
self.hasIsApproved = (coder.decodeObject(forKey: "hasIsApproved") as? Bool ?? false)
|
|
self.isApproved = (coder.decodeObject(forKey: "isApproved") as? Bool ?? false)
|
|
self.hasIsBlocked = (coder.decodeObject(forKey: "hasIsBlocked") as? Bool ?? false)
|
|
self.isBlocked = (coder.decodeObject(forKey: "isBlocked") as? Bool ?? false)
|
|
self.hasDidApproveMe = (coder.decodeObject(forKey: "hasDidApproveMe") as? Bool ?? false)
|
|
self.didApproveMe = (coder.decodeObject(forKey: "didApproveMe") as? Bool ?? false)
|
|
}
|
|
|
|
public func encode(with coder: NSCoder) {
|
|
coder.encode(publicKey, forKey: "publicKey")
|
|
coder.encode(displayName, forKey: "displayName")
|
|
coder.encode(profilePictureURL, forKey: "profilePictureURL")
|
|
coder.encode(profileKey, forKey: "profileKey")
|
|
coder.encode(hasIsApproved, forKey: "hasIsApproved")
|
|
coder.encode(isApproved, forKey: "isApproved")
|
|
coder.encode(hasIsBlocked, forKey: "hasIsBlocked")
|
|
coder.encode(isBlocked, forKey: "isBlocked")
|
|
coder.encode(hasDidApproveMe, forKey: "hasDidApproveMe")
|
|
coder.encode(didApproveMe, forKey: "didApproveMe")
|
|
}
|
|
|
|
public static func fromProto(_ proto: SNProtoConfigurationMessageContact) -> Contact? {
|
|
let result: Contact = Contact(
|
|
publicKey: proto.publicKey.toHexString(),
|
|
displayName: proto.name,
|
|
profilePictureURL: proto.profilePicture,
|
|
profileKey: proto.profileKey,
|
|
hasIsApproved: proto.hasIsApproved,
|
|
isApproved: proto.isApproved,
|
|
hasIsBlocked: proto.hasIsBlocked,
|
|
isBlocked: proto.isBlocked,
|
|
hasDidApproveMe: proto.hasDidApproveMe,
|
|
didApproveMe: proto.didApproveMe
|
|
)
|
|
|
|
guard result.isValid else { return nil }
|
|
return result
|
|
}
|
|
|
|
public func toProto() -> SNProtoConfigurationMessageContact? {
|
|
guard isValid else { return nil }
|
|
guard let publicKey = publicKey, let displayName = displayName else { return nil }
|
|
let result = SNProtoConfigurationMessageContact.builder(publicKey: Data(hex: publicKey), name: displayName)
|
|
if let profilePictureURL = profilePictureURL { result.setProfilePicture(profilePictureURL) }
|
|
if let profileKey = profileKey { result.setProfileKey(profileKey) }
|
|
|
|
if hasIsApproved { result.setIsApproved(isApproved) }
|
|
if hasIsBlocked { result.setIsBlocked(isBlocked) }
|
|
if hasDidApproveMe { result.setDidApproveMe(didApproveMe) }
|
|
|
|
do {
|
|
return try result.build()
|
|
} catch {
|
|
SNLog("Couldn't construct contact proto from: \(self).")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public override var description: String { displayName ?? "" }
|
|
}
|
|
}
|