diff --git a/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift b/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift index c38c7f444..ae24d2fa5 100644 --- a/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift +++ b/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift @@ -7,6 +7,7 @@ public final class ConfigurationMessage : ControlMessage { public var displayName: String? public var profilePictureURL: String? public var profileKey: Data? + public var contacts: Set = [] public override var ttl: UInt64 { 4 * 24 * 60 * 60 * 1000 } @@ -15,13 +16,14 @@ public final class ConfigurationMessage : ControlMessage { // MARK: Initialization public override init() { super.init() } - public init(displayName: String?, profilePictureURL: String?, profileKey: Data?, closedGroups: Set, openGroups: Set) { + public init(displayName: String?, profilePictureURL: String?, profileKey: Data?, closedGroups: Set, openGroups: Set, contacts: Set) { super.init() self.displayName = displayName self.profilePictureURL = profilePictureURL self.profileKey = profileKey self.closedGroups = closedGroups self.openGroups = openGroups + self.contacts = contacts } // MARK: Coding @@ -32,6 +34,7 @@ public final class ConfigurationMessage : ControlMessage { 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? { self.contacts = contacts } } public override func encode(with coder: NSCoder) { @@ -41,6 +44,7 @@ public final class ConfigurationMessage : ControlMessage { coder.encode(displayName, forKey: "displayName") coder.encode(profilePictureURL, forKey: "profilePictureURL") coder.encode(profileKey, forKey: "profileKey") + coder.encode(contacts, forKey: "contacts") } // MARK: Proto Conversion @@ -51,7 +55,9 @@ public final class ConfigurationMessage : ControlMessage { let profileKey = configurationProto.profileKey let closedGroups = Set(configurationProto.closedGroups.compactMap { ClosedGroup.fromProto($0) }) let openGroups = Set(configurationProto.openGroups) - return ConfigurationMessage(displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey, closedGroups: closedGroups, openGroups: 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? { @@ -61,6 +67,7 @@ public final class ConfigurationMessage : ControlMessage { 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()) @@ -77,6 +84,10 @@ public final class ConfigurationMessage : ControlMessage { ConfigurationMessage( closedGroups: \([ClosedGroup](closedGroups).prettifiedDescription) openGroups: \([String](openGroups).prettifiedDescription) + displayName: \(displayName ?? "null") + profilePictureURL: \(profilePictureURL ?? "null") + profileKey: \(profileKey?.toHexString() ?? "null") + contacts: \([Contact](contacts).prettifiedDescription) ) """ } @@ -137,10 +148,13 @@ extension ConfigurationMessage { } let members = Set(proto.members.map { $0.toHexString() }) let admins = Set(proto.admins.map { $0.toHexString() }) - return ClosedGroup(publicKey: publicKey, name: name, encryptionKeyPair: encryptionKeyPair, members: members, admins: admins) + let result = ClosedGroup(publicKey: publicKey, name: name, encryptionKeyPair: encryptionKeyPair, members: members, admins: admins) + 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) @@ -164,3 +178,66 @@ extension ConfigurationMessage { 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 isValid: Bool { publicKey != nil && displayName != nil } + + public init(publicKey: String, displayName: String, profilePictureURL: String?, profileKey: Data?) { + self.publicKey = publicKey + self.displayName = displayName + self.profilePictureURL = profilePictureURL + self.profileKey = profileKey + } + + 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? + } + + 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") + } + + public static func fromProto(_ proto: SNProtoConfigurationMessageContact) -> Contact? { + let publicKey = proto.publicKey.toHexString() + let displayName = proto.name + let profilePictureURL = proto.profilePicture + let profileKey = proto.profileKey + let result = Contact(publicKey: publicKey, displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey) + 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) } + do { + return try result.build() + } catch { + SNLog("Couldn't construct contact proto from: \(self).") + return nil + } + } + + public override var description: String { displayName! } + } +} diff --git a/SessionMessagingKit/Protos/Generated/SNProto.swift b/SessionMessagingKit/Protos/Generated/SNProto.swift index f10af3ebf..d8ee914a0 100644 --- a/SessionMessagingKit/Protos/Generated/SNProto.swift +++ b/SessionMessagingKit/Protos/Generated/SNProto.swift @@ -3109,19 +3109,13 @@ extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClos // MARK: - SNProtoConfigurationMessageContactBuilder - @objc public class func builder() -> SNProtoConfigurationMessageContactBuilder { - return SNProtoConfigurationMessageContactBuilder() + @objc public class func builder(publicKey: Data, name: String) -> SNProtoConfigurationMessageContactBuilder { + return SNProtoConfigurationMessageContactBuilder(publicKey: publicKey, name: name) } // asBuilder() constructs a builder that reflects the proto's contents. @objc public func asBuilder() -> SNProtoConfigurationMessageContactBuilder { - let builder = SNProtoConfigurationMessageContactBuilder() - if let _value = publicKey { - builder.setPublicKey(_value) - } - if let _value = name { - builder.setName(_value) - } + let builder = SNProtoConfigurationMessageContactBuilder(publicKey: publicKey, name: name) if let _value = profilePicture { builder.setProfilePicture(_value) } @@ -3137,6 +3131,13 @@ extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClos @objc fileprivate override init() {} + @objc fileprivate init(publicKey: Data, name: String) { + super.init() + + setPublicKey(publicKey) + setName(name) + } + @objc public func setPublicKey(_ valueParam: Data) { proto.publicKey = valueParam } @@ -3164,25 +3165,9 @@ extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClos fileprivate let proto: SessionProtos_ConfigurationMessage.Contact - @objc public var publicKey: Data? { - guard proto.hasPublicKey else { - return nil - } - return proto.publicKey - } - @objc public var hasPublicKey: Bool { - return proto.hasPublicKey - } + @objc public let publicKey: Data - @objc public var name: String? { - guard proto.hasName else { - return nil - } - return proto.name - } - @objc public var hasName: Bool { - return proto.hasName - } + @objc public let name: String @objc public var profilePicture: String? { guard proto.hasProfilePicture else { @@ -3204,8 +3189,12 @@ extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClos return proto.hasProfileKey } - private init(proto: SessionProtos_ConfigurationMessage.Contact) { + private init(proto: SessionProtos_ConfigurationMessage.Contact, + publicKey: Data, + name: String) { self.proto = proto + self.publicKey = publicKey + self.name = name } @objc @@ -3219,11 +3208,23 @@ extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClos } fileprivate class func parseProto(_ proto: SessionProtos_ConfigurationMessage.Contact) throws -> SNProtoConfigurationMessageContact { + guard proto.hasPublicKey else { + throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: publicKey") + } + let publicKey = proto.publicKey + + guard proto.hasName else { + throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: name") + } + let name = proto.name + // MARK: - Begin Validation Logic for SNProtoConfigurationMessageContact - // MARK: - End Validation Logic for SNProtoConfigurationMessageContact - - let result = SNProtoConfigurationMessageContact(proto: proto) + let result = SNProtoConfigurationMessageContact(proto: proto, + publicKey: publicKey, + name: name) return result } diff --git a/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift b/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift index 1c9485312..fad2efaf4 100644 --- a/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift +++ b/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift @@ -1335,6 +1335,7 @@ struct SessionProtos_ConfigurationMessage { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. + /// @required var publicKey: Data { get {return _publicKey ?? SwiftProtobuf.Internal.emptyData} set {_publicKey = newValue} @@ -1344,6 +1345,7 @@ struct SessionProtos_ConfigurationMessage { /// Clears the value of `publicKey`. Subsequent reads from it will return its default value. mutating func clearPublicKey() {self._publicKey = nil} + /// @required var name: String { get {return _name ?? String()} set {_name = newValue} @@ -2843,6 +2845,7 @@ extension SessionProtos_ConfigurationMessage: SwiftProtobuf.Message, SwiftProtob public var isInitialized: Bool { if !SwiftProtobuf.Internal.areAllInitialized(self.closedGroups) {return false} + if !SwiftProtobuf.Internal.areAllInitialized(self.contacts) {return false} return true } @@ -2961,6 +2964,12 @@ extension SessionProtos_ConfigurationMessage.Contact: SwiftProtobuf.Message, Swi 4: .same(proto: "profileKey"), ] + public var isInitialized: Bool { + if self._publicKey == nil {return false} + if self._name == nil {return false} + return true + } + mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { switch fieldNumber { diff --git a/SessionMessagingKit/Protos/SessionProtos.proto b/SessionMessagingKit/Protos/SessionProtos.proto index 1c3944bd4..2ce67ec2c 100644 --- a/SessionMessagingKit/Protos/SessionProtos.proto +++ b/SessionMessagingKit/Protos/SessionProtos.proto @@ -222,8 +222,10 @@ message ConfigurationMessage { } message Contact { - optional bytes publicKey = 1; - optional string name = 2; + // @required + required bytes publicKey = 1; + // @required + required string name = 2; optional string profilePicture = 3; optional bytes profileKey = 4; } diff --git a/SignalUtilitiesKit/Messaging/ConfigurationMessage+Convenience.swift b/SignalUtilitiesKit/Messaging/ConfigurationMessage+Convenience.swift index 38fb3a3a0..d85ea6712 100644 --- a/SignalUtilitiesKit/Messaging/ConfigurationMessage+Convenience.swift +++ b/SignalUtilitiesKit/Messaging/ConfigurationMessage+Convenience.swift @@ -8,6 +8,7 @@ extension ConfigurationMessage { let profileKey = storage.getUserProfileKey() var closedGroups: Set = [] var openGroups: Set = [] + var contacts: Set = [] Storage.read { transaction in TSGroupThread.enumerateCollectionObjects(with: transaction) { object, _ in guard let thread = object as? TSGroupThread else { return } @@ -28,6 +29,7 @@ extension ConfigurationMessage { } } } - return ConfigurationMessage(displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey, closedGroups: closedGroups, openGroups: openGroups) + return ConfigurationMessage(displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey, + closedGroups: closedGroups, openGroups: openGroups, contacts: contacts) } }