From 1f9cd92bce312717baeb00528c2a2672b71ff049 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Thu, 10 Jun 2021 11:51:38 +1000 Subject: [PATCH] set expiration timer for new group members --- .../ClosedGroupControlMessage.swift | 18 +++++++++++------- .../MessageReceiver+Handling.swift | 15 +++++++++++---- .../MessageSender+ClosedGroups.swift | 6 ++++-- .../NotificationServiceExtension.swift | 2 +- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/SessionMessagingKit/Messages/Control Messages/ClosedGroupControlMessage.swift b/SessionMessagingKit/Messages/Control Messages/ClosedGroupControlMessage.swift index 8d87666e2..61fe80290 100644 --- a/SessionMessagingKit/Messages/Control Messages/ClosedGroupControlMessage.swift +++ b/SessionMessagingKit/Messages/Control Messages/ClosedGroupControlMessage.swift @@ -14,7 +14,7 @@ public final class ClosedGroupControlMessage : ControlMessage { // MARK: Kind public enum Kind : CustomStringConvertible { - case new(publicKey: Data, name: String, encryptionKeyPair: ECKeyPair, members: [Data], admins: [Data]) + case new(publicKey: Data, name: String, encryptionKeyPair: ECKeyPair, members: [Data], admins: [Data], expireTimer: UInt32) /// An encryption key pair encrypted for each member individually. /// /// - Note: `publicKey` is only set when an encryption key pair is sent in a one-to-one context (i.e. not in a group). @@ -89,9 +89,9 @@ public final class ClosedGroupControlMessage : ControlMessage { public override var isValid: Bool { guard super.isValid, let kind = kind else { return false } switch kind { - case .new(let publicKey, let name, let encryptionKeyPair, let members, let admins): + case .new(let publicKey, let name, let encryptionKeyPair, let members, let admins, let expireTimer): return !publicKey.isEmpty && !name.isEmpty && !encryptionKeyPair.publicKey.isEmpty - && !encryptionKeyPair.privateKey.isEmpty && !members.isEmpty && !admins.isEmpty + && !encryptionKeyPair.privateKey.isEmpty && !members.isEmpty && !admins.isEmpty && expireTimer >= 0 case .encryptionKeyPair: return true case .nameChange(let name): return !name.isEmpty case .membersAdded(let members): return !members.isEmpty @@ -112,7 +112,8 @@ public final class ClosedGroupControlMessage : ControlMessage { let encryptionKeyPair = coder.decodeObject(forKey: "encryptionKeyPair") as? ECKeyPair, let members = coder.decodeObject(forKey: "members") as? [Data], let admins = coder.decodeObject(forKey: "admins") as? [Data] else { return nil } - self.kind = .new(publicKey: publicKey, name: name, encryptionKeyPair: encryptionKeyPair, members: members, admins: admins) + let expireTimer = coder.decodeObject(forKey: "expireTimer") as! UInt32 + self.kind = .new(publicKey: publicKey, name: name, encryptionKeyPair: encryptionKeyPair, members: members, admins: admins, expireTimer: expireTimer) case "encryptionKeyPair": let publicKey = coder.decodeObject(forKey: "publicKey") as? Data guard let wrappers = coder.decodeObject(forKey: "wrappers") as? [KeyPairWrapper] else { return nil } @@ -138,13 +139,14 @@ public final class ClosedGroupControlMessage : ControlMessage { super.encode(with: coder) guard let kind = kind else { return } switch kind { - case .new(let publicKey, let name, let encryptionKeyPair, let members, let admins): + case .new(let publicKey, let name, let encryptionKeyPair, let members, let admins, let expireTimer): coder.encode("new", forKey: "kind") 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(expireTimer, forKey: "expireTimer") case .encryptionKeyPair(let publicKey, let wrappers): coder.encode("encryptionKeyPair", forKey: "kind") coder.encode(publicKey, forKey: "publicKey") @@ -174,9 +176,10 @@ public final class ClosedGroupControlMessage : ControlMessage { guard let publicKey = closedGroupControlMessageProto.publicKey, let name = closedGroupControlMessageProto.name, let encryptionKeyPairAsProto = closedGroupControlMessageProto.encryptionKeyPair else { return nil } do { + let expireTimer = closedGroupControlMessageProto.expireTimer let encryptionKeyPair = try ECKeyPair(publicKeyData: encryptionKeyPairAsProto.publicKey.removing05PrefixIfNeeded(), privateKeyData: encryptionKeyPairAsProto.privateKey) kind = .new(publicKey: publicKey, name: name, encryptionKeyPair: encryptionKeyPair, - members: closedGroupControlMessageProto.members, admins: closedGroupControlMessageProto.admins) + members: closedGroupControlMessageProto.members, admins: closedGroupControlMessageProto.admins, expireTimer: expireTimer) } catch { SNLog("Couldn't parse key pair.") return nil @@ -208,7 +211,7 @@ public final class ClosedGroupControlMessage : ControlMessage { do { let closedGroupControlMessage: SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGroupControlMessageBuilder switch kind { - case .new(let publicKey, let name, let encryptionKeyPair, let members, let admins): + case .new(let publicKey, let name, let encryptionKeyPair, let members, let admins, let expireTimer): closedGroupControlMessage = SNProtoDataMessageClosedGroupControlMessage.builder(type: .new) closedGroupControlMessage.setPublicKey(publicKey) closedGroupControlMessage.setName(name) @@ -221,6 +224,7 @@ public final class ClosedGroupControlMessage : ControlMessage { } closedGroupControlMessage.setMembers(members) closedGroupControlMessage.setAdmins(admins) + closedGroupControlMessage.setExpireTimer(expireTimer) case .encryptionKeyPair(let publicKey, let wrappers): closedGroupControlMessage = SNProtoDataMessageClosedGroupControlMessage.builder(type: .encryptionKeyPair) if let publicKey = publicKey { diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift index 80b632dd7..8b8c79935 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift @@ -208,7 +208,7 @@ extension MessageReceiver { for closedGroup in message.closedGroups { guard !allClosedGroupPublicKeys.contains(closedGroup.publicKey) else { continue } handleNewClosedGroup(groupPublicKey: closedGroup.publicKey, name: closedGroup.name, encryptionKeyPair: closedGroup.encryptionKeyPair, - members: [String](closedGroup.members), admins: [String](closedGroup.admins), messageSentTimestamp: message.sentTimestamp!, using: transaction) + members: [String](closedGroup.members), admins: [String](closedGroup.admins), expireTimer: 0, messageSentTimestamp: message.sentTimestamp!, using: transaction) } // Open groups for openGroupURL in message.openGroups { @@ -368,15 +368,15 @@ extension MessageReceiver { } private static func handleNewClosedGroup(_ message: ClosedGroupControlMessage, using transaction: Any) { - guard case let .new(publicKeyAsData, name, encryptionKeyPair, membersAsData, adminsAsData) = message.kind else { return } + guard case let .new(publicKeyAsData, name, encryptionKeyPair, membersAsData, adminsAsData, expireTimer) = message.kind else { return } let groupPublicKey = publicKeyAsData.toHexString() let members = membersAsData.map { $0.toHexString() } let admins = adminsAsData.map { $0.toHexString() } handleNewClosedGroup(groupPublicKey: groupPublicKey, name: name, encryptionKeyPair: encryptionKeyPair, - members: members, admins: admins, messageSentTimestamp: message.sentTimestamp!, using: transaction) + members: members, admins: admins, expireTimer: expireTimer, messageSentTimestamp: message.sentTimestamp!, using: transaction) } - private static func handleNewClosedGroup(groupPublicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: [String], admins: [String], messageSentTimestamp: UInt64, using transaction: Any) { + private static func handleNewClosedGroup(groupPublicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: [String], admins: [String], expireTimer: UInt32, messageSentTimestamp: UInt64, using transaction: Any) { let transaction = transaction as! YapDatabaseReadWriteTransaction // Create the group let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey) @@ -397,6 +397,13 @@ extension MessageReceiver { let infoMessage = TSInfoMessage(timestamp: messageSentTimestamp, in: thread, messageType: .groupCreated) infoMessage.save(with: transaction) } + var configuration: OWSDisappearingMessagesConfiguration + if (expireTimer > 0) { + configuration = OWSDisappearingMessagesConfiguration(threadId: thread.uniqueId!, enabled: true, durationSeconds: expireTimer) + } else { + configuration = OWSDisappearingMessagesConfiguration(threadId: thread.uniqueId!, enabled: false, durationSeconds: 24 * 60 * 60) + } + configuration.save(with: transaction) // Add the group to the user's set of public keys to poll for Storage.shared.addClosedGroupPublicKey(groupPublicKey, using: transaction) // Store the key pair diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift b/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift index c570b7278..acc9a0ce9 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift @@ -27,7 +27,7 @@ extension MessageSender { let thread = TSContactThread.getOrCreateThread(withContactSessionID: member, transaction: transaction) thread.save(with: transaction) let closedGroupControlMessageKind = ClosedGroupControlMessage.Kind.new(publicKey: Data(hex: groupPublicKey), name: name, - encryptionKeyPair: encryptionKeyPair, members: membersAsData, admins: adminsAsData) + encryptionKeyPair: encryptionKeyPair, members: membersAsData, admins: adminsAsData, expireTimer: 0) let closedGroupControlMessage = ClosedGroupControlMessage(kind: closedGroupControlMessageKind) // Sending this non-durably is okay because we show a loader to the user. If they close the app while the // loader is still showing, it's within expectation that the group creation might be incomplete. @@ -163,6 +163,8 @@ extension MessageSender { let members = [String](Set(group.groupMemberIds).union(newMembers)) let membersAsData = members.map { Data(hex: $0) } let adminsAsData = group.groupAdminIds.map { Data(hex: $0) } + // Get expiration timer value for the group + let expireTimer = thread.disappearingMessagesDuration(with: transaction) guard let encryptionKeyPair = Storage.shared.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey) else { SNLog("Couldn't find encryption key pair for closed group: \(groupPublicKey).") return Promise(error: Error.noKeyPair) @@ -175,7 +177,7 @@ extension MessageSender { let thread = TSContactThread.getOrCreateThread(withContactSessionID: member, transaction: transaction) thread.save(with: transaction) let closedGroupControlMessageKind = ClosedGroupControlMessage.Kind.new(publicKey: Data(hex: groupPublicKey), name: group.groupName!, - encryptionKeyPair: encryptionKeyPair, members: membersAsData, admins: adminsAsData) + encryptionKeyPair: encryptionKeyPair, members: membersAsData, admins: adminsAsData, expireTimer: expireTimer) let closedGroupControlMessage = ClosedGroupControlMessage(kind: closedGroupControlMessageKind) MessageSender.send(closedGroupControlMessage, in: thread, using: transaction) } diff --git a/SessionNotificationServiceExtension/NotificationServiceExtension.swift b/SessionNotificationServiceExtension/NotificationServiceExtension.swift index 0fb635576..0097eb3b7 100644 --- a/SessionNotificationServiceExtension/NotificationServiceExtension.swift +++ b/SessionNotificationServiceExtension/NotificationServiceExtension.swift @@ -61,7 +61,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension // TODO: We could consider actually handling the update here. Not sure if there's enough time though, seeing as though // in some cases we need to send messages (e.g. our sender key) to a number of other users. switch closedGroupControlMessage.kind { - case .new(_, let name, _, _, _): snippet = "\(senderDisplayName) added you to \(name)" + case .new(_, let name, _, _, _, _): snippet = "\(senderDisplayName) added you to \(name)" default: return self.completeSilenty() } default: return self.completeSilenty()