Allow admins to leave & fix remaining issues

This commit is contained in:
nielsandriesse 2021-01-08 14:54:27 +11:00
parent c7d3f3e32d
commit d5e1237b0c
16 changed files with 78 additions and 472 deletions

View File

@ -931,7 +931,7 @@ static CGRect oldframe;
if (gThread.usesSharedSenderKeys) {
NSString *groupPublicKey = [LKGroupUtilities getDecodedGroupID:gThread.groupModel.groupId];
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[[SNMessageSender leaveGroupWithPublicKey:groupPublicKey transaction:transaction] retainUntilComplete];
[SNMessageSender leaveClosedGroupWithPublicKey:groupPublicKey using:transaction error:nil];
}];
}

View File

@ -252,28 +252,22 @@ final class EditClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelega
guard members != Set(thread.groupModel.groupMemberIds) || name != thread.groupModel.groupName else {
return popToConversationVC(self)
}
/*
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
Storage.writeSync { [weak self] transaction in
MessageSender.update(groupPublicKey, with: members, name: name, transaction: transaction).done(on: DispatchQueue.main) {
guard let self = self else { return }
self.dismiss(animated: true, completion: nil) // Dismiss the loader
popToConversationVC(self)
}.catch(on: DispatchQueue.main) { error in
guard let self = self else { return }
self.dismiss(animated: true, completion: nil) // Dismiss the loader
self.showError(title: "Couldn't Update Group", message: "Please check your internet connection and try again.")
}
if !members.contains(getUserHexEncodedPublicKey()) {
guard members.intersection(Set(thread.groupModel.groupMemberIds).subtracting([ getUserHexEncodedPublicKey() ])) == members else {
return showError(title: "Couldn't Update Group", message: "Can't leave while adding or removing other members.")
}
}
*/
Storage.write(with: { [weak self] transaction in
do {
try MessageSender.updateV2(groupPublicKey, with: members, name: name, transaction: transaction)
if !members.contains(getUserHexEncodedPublicKey()) {
try MessageSender.leaveV2(groupPublicKey, using: transaction)
} else {
try MessageSender.updateV2(groupPublicKey, with: members, name: name, transaction: transaction)
}
} catch {
self?.showError(title: "Couldn't Update Group", message: "Please check your internet connection and try again.")
DispatchQueue.main.async {
self?.showError(title: "Couldn't Update Group", message: "Please check your internet connection and try again.")
}
}
}, completion: { [weak self] in
guard let self = self else { return }
@ -289,7 +283,7 @@ final class EditClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelega
}
private func canBeRemoved(_ publicKey: String) -> Bool {
return !isAdmin(publicKey) && !isCurrentUser(publicKey)
return !isAdmin(publicKey) || isCurrentUser(publicKey)
}
private func isAdmin(_ publicKey: String) -> Bool {

View File

@ -402,7 +402,11 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
guard let thread = self.thread(at: indexPath.row) else { return [] }
let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!)
let delete = UITableViewRowAction(style: .destructive, title: NSLocalizedString("TXT_DELETE_TITLE", comment: "")) { [weak self] _, _ in
let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: ""), preferredStyle: .alert)
var message = NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: "")
if let thread = thread as? TSGroupThread, thread.usesSharedSenderKeys, thread.groupModel.groupAdminIds.contains(getUserHexEncodedPublicKey()) {
message = "Because you are the creator of this group it will be deleted for everyone. This cannot be undone."
}
let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { _ in
Storage.write { transaction in
Storage.shared.cancelPendingMessageSendJobs(for: thread.uniqueId!, using: transaction)
@ -420,11 +424,14 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
} else if let thread = thread as? TSGroupThread, thread.usesSharedSenderKeys == true {
let groupID = thread.groupModel.groupId
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
let _ = MessageSender.leave(groupPublicKey, using: transaction).ensure {
do {
try MessageSender.leaveV2(groupPublicKey, using: transaction)
Storage.write { transaction in
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
}
} catch {
// TODO: Handle
}
} else {
thread.removeAllThreadInteractions(with: transaction)

View File

@ -170,11 +170,6 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
var promise: Promise<TSGroupThread>!
Storage.writeSync { transaction in
/*
promise = MessageSender.createClosedGroup(name: name, members: selectedContacts, transaction: transaction)
*/
promise = MessageSender.createV2ClosedGroup(name: name, members: selectedContacts, transaction: transaction)
}
let _ = promise.done(on: DispatchQueue.main) { thread in

View File

@ -122,7 +122,6 @@ extension Storage {
var result: Set<String> = []
Storage.read { transaction in
result = result.union(Set(transaction.allKeys(inCollection: Storage.closedGroupPublicKeyCollection)))
result = result.union(Set(transaction.allKeys(inCollection: Storage.closedGroupPrivateKeyCollection)))
}
return result
}

View File

@ -80,8 +80,8 @@ public final class ClosedGroupUpdateV2 : ControlMessage {
case .new(let publicKey, let name, let encryptionKeyPair, let members, let admins):
return !publicKey.isEmpty && !name.isEmpty && !encryptionKeyPair.publicKey.isEmpty
&& !encryptionKeyPair.privateKey.isEmpty && !members.isEmpty && !admins.isEmpty
case .update(let name, let members):
return !name.isEmpty && !members.isEmpty
case .update(let name, _):
return !name.isEmpty
case .encryptionKeyPair: return true
}
}

View File

@ -6,6 +6,7 @@ FOUNDATION_EXPORT const unsigned char SessionMessagingKitVersionString[];
#import <SessionMessagingKit/AppReadiness.h>
#import <SessionMessagingKit/Environment.h>
#import <SessionMessagingKit/NotificationsProtocol.h>
#import <SessionMessagingKit/NSData+messagePadding.h>
#import <SessionMessagingKit/OWSAudioPlayer.h>
#import <SessionMessagingKit/OWSBackgroundTask.h>
#import <SessionMessagingKit/OWSBackupFragment.h>

View File

@ -12,7 +12,6 @@ extension MessageReceiver {
case let message as ReadReceipt: handleReadReceipt(message, using: transaction)
case let message as TypingIndicator: handleTypingIndicator(message, using: transaction)
case let message as ClosedGroupUpdateV2: handleClosedGroupUpdateV2(message, using: transaction)
case let message as ClosedGroupUpdate: handleClosedGroupUpdate(message, using: transaction)
case let message as ExpirationTimerUpdate: handleExpirationTimerUpdate(message, using: transaction)
case let message as VisibleMessage: try handleVisibleMessage(message, associatedWithProto: proto, openGroupID: openGroupID, isBackgroundPoll: isBackgroundPoll, using: transaction)
default: fatalError()
@ -224,17 +223,6 @@ extension MessageReceiver {
case .encryptionKeyPair: handleGroupEncryptionKeyPair(message, using: transaction)
}
}
private static func handleClosedGroupUpdate(_ message: ClosedGroupUpdate, using transaction: Any) {
switch message.kind! {
case .new: handleNewGroup(message, using: transaction)
case .info: handleGroupUpdate(message, using: transaction)
case .senderKeyRequest: handleSenderKeyRequest(message, using: transaction)
case .senderKey: handleSenderKey(message, using: transaction)
}
}
// MARK: - V2
private static func handleNewGroupV2(_ message: ClosedGroupUpdateV2, using transaction: Any) {
// Prepare
@ -286,8 +274,8 @@ extension MessageReceiver {
guard Set(group.groupMemberIds).contains(message.sender!) else {
return SNLog("Ignoring closed group update message from non-member.")
}
// Check that the admin wasn't removed
guard members.contains(group.groupAdminIds.first!) else {
// Check that the admin wasn't removed unless the group was destroyed entirely
if !members.contains(group.groupAdminIds.first!) && !members.isEmpty {
return SNLog("Ignoring invalid closed group update message.")
}
// Remove the group from the user's set of public keys to poll for if the current user was removed
@ -363,164 +351,4 @@ extension MessageReceiver {
Storage.shared.addClosedGroupEncryptionKeyPair(keyPair, for: groupPublicKey, using: transaction)
SNLog("Received a new closed group encryption key pair.")
}
// MARK: - V1
private static func handleNewGroup(_ message: ClosedGroupUpdate, using transaction: Any) {
guard case let .new(groupPublicKeyAsData, name, groupPrivateKey, senderKeys, membersAsData, adminsAsData) = message.kind else { return }
let transaction = transaction as! YapDatabaseReadWriteTransaction
let groupPublicKey = groupPublicKeyAsData.toHexString()
let members = membersAsData.map { $0.toHexString() }
let admins = adminsAsData.map { $0.toHexString() }
// Persist the ratchets
senderKeys.forEach { senderKey in
guard members.contains(senderKey.publicKey.toHexString()) else { return }
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.publicKey.toHexString(), ratchet: ratchet, using: transaction)
}
// Sort out any discrepancies between the provided sender keys and what's required
let missingSenderKeys = Set(members).subtracting(senderKeys.map { $0.publicKey.toHexString() })
let userPublicKey = getUserHexEncodedPublicKey()
if missingSenderKeys.contains(userPublicKey) {
let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
members.forEach { member in
guard member != userPublicKey else { return }
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
}
missingSenderKeys.subtracting([ userPublicKey ]).forEach { publicKey in
MessageSender.shared.requestSenderKey(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
}
// Create the group
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
let group = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
let thread: TSGroupThread
if let t = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) {
thread = t
thread.setGroupModel(group, with: transaction)
} else {
thread = TSGroupThread.getOrCreateThread(with: group, transaction: transaction)
thread.usesSharedSenderKeys = true
thread.save(with: transaction)
}
// Add the group to the user's set of public keys to poll for
Storage.shared.setClosedGroupPrivateKey(groupPrivateKey.toHexString(), for: groupPublicKey, using: transaction)
// Notify the PN server
let _ = PushNotificationAPI.performOperation(.subscribe, for: groupPublicKey, publicKey: getUserHexEncodedPublicKey())
// Notify the user
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
infoMessage.save(with: transaction)
}
private static func handleGroupUpdate(_ message: ClosedGroupUpdate, using transaction: Any) {
guard case let .info(groupPublicKeyAsData, name, senderKeys, membersAsData, adminsAsData) = message.kind else { return }
let transaction = transaction as! YapDatabaseReadWriteTransaction
let groupPublicKey = groupPublicKeyAsData.toHexString()
let members = membersAsData.map { $0.toHexString() }
let admins = adminsAsData.map { $0.toHexString() }
// Get the group
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
guard let thread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
return SNLog("Ignoring closed group info message for nonexistent group.")
}
let group = thread.groupModel
// Check that the sender is a member of the group (before the update)
guard Set(group.groupMemberIds).contains(message.sender!) else {
return SNLog("Ignoring closed group info message from non-member.")
}
// Store the ratchets for any new members (it's important that this happens before the code below)
senderKeys.forEach { senderKey in
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.publicKey.toHexString(), ratchet: ratchet, using: transaction)
}
// Delete all ratchets and either:
// Send out the user's new ratchet using established channels if other members of the group left or were removed
// Remove the group from the user's set of public keys to poll for if the current user was among the members that were removed
let oldMembers = group.groupMemberIds
let userPublicKey = getUserHexEncodedPublicKey()
let wasUserRemoved = !members.contains(userPublicKey)
if Set(members).intersection(oldMembers) != Set(oldMembers) {
let allOldRatchets = Storage.shared.getAllClosedGroupRatchets(for: groupPublicKey)
for (senderPublicKey, oldRatchet) in allOldRatchets {
let collection = ClosedGroupRatchetCollectionType.old
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: oldRatchet, in: collection, using: transaction)
}
Storage.shared.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
if wasUserRemoved {
Storage.shared.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
// Notify the PN server
let _ = PushNotificationAPI.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
} else {
let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
members.forEach { member in
guard member != userPublicKey else { return }
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
}
}
// Update the group
let newGroupModel = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
thread.setGroupModel(newGroupModel, with: transaction)
// Notify the user if needed
if Set(members) != Set(oldMembers) || Set(admins) != Set(group.groupAdminIds) || name != group.groupName {
let infoMessageType: TSInfoMessageType = wasUserRemoved ? .typeGroupQuit : .typeGroupUpdate
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: infoMessageType, customMessage: updateInfo)
infoMessage.save(with: transaction)
}
}
private static func handleSenderKeyRequest(_ message: ClosedGroupUpdate, using transaction: Any) {
guard case let .senderKeyRequest(groupPublicKeyAsData) = message.kind else { return }
let transaction = transaction as! YapDatabaseReadWriteTransaction
let userPublicKey = getUserHexEncodedPublicKey()
let groupPublicKey = groupPublicKeyAsData.toHexString()
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
guard let groupThread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
return SNLog("Ignoring closed group sender key request for nonexistent group.")
}
let group = groupThread.groupModel
// Check that the requesting user is a member of the group
let members = Set(group.groupMemberIds)
guard members.contains(message.sender!) else {
return SNLog("Ignoring closed group sender key request from non-member.")
}
// Respond to the request
SNLog("Responding to sender key request from: \(message.sender!).")
let userRatchet = Storage.shared.getClosedGroupRatchet(for: groupPublicKey, senderPublicKey: userPublicKey)
?? SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
let thread = TSContactThread.getOrCreateThread(withContactId: message.sender!, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
private static func handleSenderKey(_ message: ClosedGroupUpdate, using transaction: Any) {
guard case let .senderKey(groupPublicKeyAsData, senderKey) = message.kind else { return }
let groupPublicKey = groupPublicKeyAsData.toHexString()
guard senderKey.publicKey.toHexString() == message.sender! else {
return SNLog("Ignoring invalid closed group sender key.")
}
// Store the sender key
SNLog("Received a sender key from: \(message.sender!).")
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: message.sender!, ratchet: ratchet, using: transaction)
}
}

View File

@ -71,31 +71,24 @@ public enum MessageReceiver {
(plaintext, sender) = try decryptWithSessionProtocol(ciphertext: ciphertext, using: userX25519KeyPair)
case .closedGroupCiphertext:
guard let hexEncodedGroupPublicKey = envelope.source, SNMessagingKitConfiguration.shared.storage.isClosedGroup(hexEncodedGroupPublicKey) else { throw Error.invalidGroupPublicKey }
do {
var encryptionKeyPairs = Storage.shared.getClosedGroupEncryptionKeyPairs(for: hexEncodedGroupPublicKey)
guard !encryptionKeyPairs.isEmpty else { throw Error.noGroupKeyPair }
// Loop through all known group key pairs in reverse order (i.e. try the latest key pair first (which'll more than
// likely be the one we want) but try older ones in case that didn't work)
var encryptionKeyPair = encryptionKeyPairs.removeLast()
func decrypt() throws {
do {
(plaintext, sender) = try decryptWithSessionProtocol(ciphertext: ciphertext, using: encryptionKeyPair)
} catch {
if !encryptionKeyPairs.isEmpty {
encryptionKeyPair = encryptionKeyPairs.removeLast()
try decrypt()
} else {
throw error
}
var encryptionKeyPairs = Storage.shared.getClosedGroupEncryptionKeyPairs(for: hexEncodedGroupPublicKey)
guard !encryptionKeyPairs.isEmpty else { throw Error.noGroupKeyPair }
// Loop through all known group key pairs in reverse order (i.e. try the latest key pair first (which'll more than
// likely be the one we want) but try older ones in case that didn't work)
var encryptionKeyPair = encryptionKeyPairs.removeLast()
func decrypt() throws {
do {
(plaintext, sender) = try decryptWithSessionProtocol(ciphertext: ciphertext, using: encryptionKeyPair)
} catch {
if !encryptionKeyPairs.isEmpty {
encryptionKeyPair = encryptionKeyPairs.removeLast()
try decrypt()
} else {
throw error
}
}
try decrypt()
} catch {
// Fall back on the V1 method
guard let privateKey = SNMessagingKitConfiguration.shared.storage.getClosedGroupPrivateKey(for: hexEncodedGroupPublicKey) else { throw Error.noGroupKeyPair }
let keyPair = try ECKeyPair(publicKeyData: Data(hex: hexEncodedGroupPublicKey.removing05PrefixIfNeeded()), privateKeyData: Data(hex: privateKey))
(plaintext, sender) = try decryptWithSessionProtocol(ciphertext: ciphertext, using: keyPair)
}
try decrypt()
groupPublicKey = envelope.source
default: throw Error.unknownEnvelopeType
}
@ -117,7 +110,6 @@ public enum MessageReceiver {
if let readReceipt = ReadReceipt.fromProto(proto) { return readReceipt }
if let typingIndicator = TypingIndicator.fromProto(proto) { return typingIndicator }
if let closedGroupUpdate = ClosedGroupUpdateV2.fromProto(proto) { return closedGroupUpdate }
if let closedGroupUpdate = ClosedGroupUpdate.fromProto(proto) { return closedGroupUpdate }
if let expirationTimerUpdate = ExpirationTimerUpdate.fromProto(proto) { return expirationTimerUpdate }
if let visibleMessage = VisibleMessage.fromProto(proto) { return visibleMessage }
return nil

View File

@ -1,9 +1,7 @@
import PromiseKit
import SessionProtocolKit
extension MessageSender : SharedSenderKeysDelegate {
// MARK: - V2
extension MessageSender {
public static func createV2ClosedGroup(name: String, members: Set<String>, transaction: YapDatabaseReadWriteTransaction) -> Promise<TSGroupThread> {
// Prepare
@ -62,28 +60,33 @@ extension MessageSender : SharedSenderKeysDelegate {
let oldMembers = Set(group.groupMemberIds)
let newMembers = members.subtracting(oldMembers)
let membersAsData = members.map { Data(hex: $0) }
let removedMembers = oldMembers.subtracting(members)
let admins = group.groupAdminIds
let adminsAsData = admins.map { Data(hex: $0) }
let isUserLeaving = !members.contains(userPublicKey)
let wasAnyUserRemoved = !removedMembers.isEmpty
let isCurrentUserAdmin = group.groupAdminIds.contains(getUserHexEncodedPublicKey())
// Check preconditions
guard let encryptionKeyPair = Storage.shared.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey) else {
SNLog("Couldn't get key pair for closed group.")
throw Error.noKeyPair
}
let removedMembers = oldMembers.subtracting(members)
guard !removedMembers.contains(admins.first!) else {
SNLog("Can't remove admin from closed group.")
if removedMembers.contains(admins.first!) && !members.isEmpty {
SNLog("Can't remove admin from closed group without removing everyone.")
throw Error.invalidClosedGroupUpdate
}
let isUserLeaving = removedMembers.contains(userPublicKey)
if isUserLeaving && (removedMembers.count != 1 || !newMembers.isEmpty) {
SNLog("Can't remove self and add or remove others simultaneously.")
throw Error.invalidClosedGroupUpdate
if isUserLeaving && !members.isEmpty {
guard removedMembers.count == 1 && newMembers.isEmpty else {
SNLog("Can't remove self and add or remove others simultaneously.")
throw Error.invalidClosedGroupUpdate
}
}
// Send the update to the group
let mainClosedGroupUpdate = ClosedGroupUpdateV2(kind: .update(name: name, members: membersAsData))
if isUserLeaving {
let _ = MessageSender.sendNonDurably(mainClosedGroupUpdate, in: thread, using: transaction).done {
SNMessagingKitConfiguration.shared.storage.write { transaction in
// Remove the group private key and unsubscribe from PNs
// Remove the group from the database and unsubscribe from PNs
Storage.shared.removeAllClosedGroupEncryptionKeyPairs(for: groupPublicKey, using: transaction)
Storage.shared.removeClosedGroupPublicKey(groupPublicKey, using: transaction)
let _ = PushNotificationAPI.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
@ -92,8 +95,6 @@ extension MessageSender : SharedSenderKeysDelegate {
} else {
MessageSender.send(mainClosedGroupUpdate, in: thread, using: transaction)
// Generate and distribute a new encryption key pair if needed
let wasAnyUserRemoved = !removedMembers.isEmpty
let isCurrentUserAdmin = group.groupAdminIds.contains(getUserHexEncodedPublicKey())
if wasAnyUserRemoved && isCurrentUserAdmin {
try generateAndSendNewEncryptionKeyPair(for: groupPublicKey, to: members.subtracting(newMembers), using: transaction)
}
@ -115,7 +116,8 @@ extension MessageSender : SharedSenderKeysDelegate {
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate, customMessage: updateInfo)
infoMessage.save(with: transaction)
}
@objc(leaveClosedGroupWithPublicKey:using:error:)
public static func leaveV2(_ groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) throws {
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
let threadID = TSGroupThread.threadId(fromGroupId: groupID)
@ -124,8 +126,14 @@ extension MessageSender : SharedSenderKeysDelegate {
throw Error.noThread
}
let group = thread.groupModel
var newMembers = Set(group.groupMemberIds)
newMembers.remove(getUserHexEncodedPublicKey())
let isCurrentUserAdmin = group.groupAdminIds.contains(getUserHexEncodedPublicKey())
var newMembers: Set<String>
if !isCurrentUserAdmin {
newMembers = Set(group.groupMemberIds)
newMembers.remove(getUserHexEncodedPublicKey())
} else {
newMembers = [] // If the admin leaves the group is destroyed
}
return try updateV2(groupPublicKey, with: newMembers, name: group.groupName!, transaction: transaction)
}
@ -160,213 +168,4 @@ extension MessageSender : SharedSenderKeysDelegate {
}
}
}
// MARK: - V1
public static func createClosedGroup(name: String, members: Set<String>, transaction: YapDatabaseReadWriteTransaction) -> Promise<TSGroupThread> {
// Prepare
var members = members
let userPublicKey = getUserHexEncodedPublicKey()
// Generate a key pair for the group
let groupKeyPair = Curve25519.generateKeyPair()
let groupPublicKey = groupKeyPair.hexEncodedPublicKey // Includes the "05" prefix
// Ensure the current user is included in the member list
members.insert(userPublicKey)
let membersAsData = members.map { Data(hex: $0) }
// Create ratchets for all members
let senderKeys: [ClosedGroupSenderKey] = members.map { publicKey in
let ratchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: publicKey))
}
// Create the group
let admins = [ userPublicKey ]
let adminsAsData = admins.map { Data(hex: $0) }
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
let group = TSGroupModel(title: name, memberIds: [String](members), image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
let thread = TSGroupThread.getOrCreateThread(with: group, transaction: transaction)
thread.usesSharedSenderKeys = true
thread.save(with: transaction)
// Send a closed group update message to all members using established channels
var promises: [Promise<Void>] = []
for member in members {
guard member != userPublicKey else { continue }
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
groupPrivateKey: groupKeyPair.privateKey, senderKeys: senderKeys, members: membersAsData, admins: adminsAsData)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
let promise = MessageSender.sendNonDurably(closedGroupUpdate, in: thread, using: transaction)
promises.append(promise)
}
// Add the group to the user's set of public keys to poll for
Storage.shared.setClosedGroupPrivateKey(groupKeyPair.privateKey.toHexString(), for: groupPublicKey, using: transaction)
// Notify the PN server
promises.append(PushNotificationAPI.performOperation(.subscribe, for: groupPublicKey, publicKey: userPublicKey))
// Notify the user
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
infoMessage.save(with: transaction)
// Return
return when(fulfilled: promises).map2 { thread }
}
/// - Note: The returned promise is only relevant for group leaving.
public static func update(_ groupPublicKey: String, with members: Set<String>, name: String, transaction: YapDatabaseReadWriteTransaction) -> Promise<Void> {
let (promise, seal) = Promise<Void>.pending()
let userPublicKey = getUserHexEncodedPublicKey()
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
guard let thread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
SNLog("Can't update nonexistent closed group.")
return Promise(error: Error.noThread)
}
let group = thread.groupModel
let oldMembers = Set(group.groupMemberIds)
let newMembers = members.subtracting(oldMembers)
let membersAsData = members.map { Data(hex: $0) }
let admins = group.groupAdminIds
let adminsAsData = admins.map { Data(hex: $0) }
guard let groupPrivateKey = Storage.shared.getClosedGroupPrivateKey(for: groupPublicKey) else {
SNLog("Couldn't get private key for closed group.")
return Promise(error: Error.noKeyPair)
}
let wasAnyUserRemoved = Set(members).intersection(oldMembers) != oldMembers
let removedMembers = oldMembers.subtracting(members)
let isUserLeaving = removedMembers.contains(userPublicKey)
var newSenderKeys: [ClosedGroupSenderKey] = []
if wasAnyUserRemoved {
if isUserLeaving && removedMembers.count != 1 {
SNLog("Can't remove self and others simultaneously.")
return Promise(error: Error.invalidClosedGroupUpdate)
}
// Send the update to the existing members using established channels (don't include new ratchets as everyone should regenerate new ratchets individually)
let promises: [Promise<Void>] = oldMembers.map { member in
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: [],
members: membersAsData, admins: adminsAsData)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
return MessageSender.sendNonDurably(closedGroupUpdate, in: thread, using: transaction)
}
when(resolved: promises).done2 { _ in seal.fulfill(()) }.catch2 { seal.reject($0) }
let _ = promise.done {
SNMessagingKitConfiguration.shared.storage.writeSync { transaction in
let allOldRatchets = Storage.shared.getAllClosedGroupRatchets(for: groupPublicKey)
for (senderPublicKey, oldRatchet) in allOldRatchets {
let collection = ClosedGroupRatchetCollectionType.old
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: oldRatchet, in: collection, using: transaction)
}
// Delete all ratchets (it's important that this happens * after * sending out the update)
Storage.shared.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
// Remove the group from the user's set of public keys to poll for if the user is leaving. Otherwise generate a new ratchet and
// send it out to all members (minus the removed ones) using established channels.
if isUserLeaving {
Storage.shared.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
// Notify the PN server
let _ = PushNotificationAPI.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
} else {
// Send closed group update messages to any new members using established channels
for member in newMembers {
let transaction = transaction as! YapDatabaseReadWriteTransaction
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
groupPrivateKey: Data(hex: groupPrivateKey), senderKeys: [], members: membersAsData, admins: adminsAsData)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
// Send out the user's new ratchet to all members (minus the removed ones) using established channels
let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
for member in members {
let transaction = transaction as! YapDatabaseReadWriteTransaction
guard member != userPublicKey else { continue }
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
}
}
}
} else if !newMembers.isEmpty {
seal.fulfill(())
// Generate ratchets for any new members
newSenderKeys = newMembers.map { publicKey in
let ratchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: publicKey))
}
// Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: newSenderKeys,
members: membersAsData, admins: adminsAsData)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
// Send closed group update messages to the new members using established channels
var allSenderKeys = Storage.shared.getAllClosedGroupSenderKeys(for: groupPublicKey)
allSenderKeys.formUnion(newSenderKeys)
for member in newMembers {
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
groupPrivateKey: Data(hex: groupPrivateKey), senderKeys: [ClosedGroupSenderKey](allSenderKeys), members: membersAsData, admins: adminsAsData)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
} else {
seal.fulfill(())
let allSenderKeys = Storage.shared.getAllClosedGroupSenderKeys(for: groupPublicKey)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name,
senderKeys: [ClosedGroupSenderKey](allSenderKeys), members: membersAsData, admins: adminsAsData)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
// Update the group
let newGroupModel = TSGroupModel(title: name, memberIds: [String](members), image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
thread.setGroupModel(newGroupModel, with: transaction)
// Notify the user
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate, customMessage: updateInfo)
infoMessage.save(with: transaction)
// Return
return promise
}
/// The returned promise is fulfilled when the group update message has been sent. It doesn't wait for the user's new ratchet to be distributed.
@objc(leaveGroupWithPublicKey:transaction:)
public static func objc_leave(_ groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) -> AnyPromise {
return AnyPromise.from(leave(groupPublicKey, using: transaction))
}
/// The returned promise is fulfilled when the group update message has been sent. It doesn't wait for the user's new ratchet to be distributed.
public static func leave(_ groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) -> Promise<Void> {
let userPublicKey = getUserHexEncodedPublicKey()
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
guard let thread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
SNLog("Can't leave nonexistent closed group.")
return Promise(error: Error.noThread)
}
let group = thread.groupModel
var newMembers = Set(group.groupMemberIds)
newMembers.remove(userPublicKey)
return update(groupPublicKey, with: newMembers, name: group.groupName!, transaction: transaction)
}
public func requestSenderKey(for groupPublicKey: String, senderPublicKey: String, using transaction: Any) { // FIXME: This should be static
SNLog("Requesting sender key for group public key: \(groupPublicKey), sender public key: \(senderPublicKey).")
let transaction = transaction as! YapDatabaseReadWriteTransaction
let thread = TSContactThread.getOrCreateThread(withContactId: senderPublicKey, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKeyRequest(groupPublicKey: Data(hex: groupPublicKey))
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
}

View File

@ -176,11 +176,6 @@ public final class MessageSender : NSObject {
switch destination {
case .contact(let publicKey): ciphertext = try encryptWithSessionProtocol(plaintext, for: publicKey)
case .closedGroup(let groupPublicKey):
/*
ciphertext = try encryptWithSessionProtocol(plaintext, for: groupPublicKey)
*/
guard let encryptionKeyPair = Storage.shared.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey) else { throw Error.noKeyPair }
ciphertext = try encryptWithSessionProtocol(plaintext, for: encryptionKeyPair.hexEncodedPublicKey)
case .openGroup(_, _): preconditionFailure()

View File

@ -2,5 +2,3 @@
FOUNDATION_EXPORT double SessionProtocolKitVersionNumber;
FOUNDATION_EXPORT const unsigned char SessionProtocolKitVersionString[];
#import <SessionProtocolKit/NSData+messagePadding.h>

View File

@ -317,6 +317,11 @@
C31FFE57254A5FFE00F19441 /* KeyPairUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */; };
C329FEEC24F7277900B1C64C /* LightModeSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEEB24F7277900B1C64C /* LightModeSheet.swift */; };
C32A025A25A7FC55000ED5D4 /* ClosedGroupsV2Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32A025925A7FC55000ED5D4 /* ClosedGroupsV2Migration.swift */; };
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4825589FF20043A11F /* NSData+messagePadding.m */; };
C32A026C25A801AF000ED5D4 /* NSData+messagePadding.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */; settings = {ATTRIBUTES = (Public, ); }; };
C32A027D25A80423000ED5D4 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
C32A027E25A80428000ED5D4 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
C32A027F25A80432000ED5D4 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */; };
C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */; };
C32C59C0256DB41F003C73A2 /* TSThread.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAD3255A580300E217F9 /* TSThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -453,7 +458,6 @@
C33FD9AF255A548A00E217F9 /* SignalUtilitiesKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FD9AD255A548A00E217F9 /* SignalUtilitiesKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FD9B3255A548A00E217F9 /* SignalUtilitiesKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
C33FD9C2255A54EF00E217F9 /* SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; };
C33FD9C3255A54EF00E217F9 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
C33FD9C4255A54EF00E217F9 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; };
C33FD9C5255A54EF00E217F9 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; };
C33FDC27255A581F00E217F9 /* YapDatabase+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6D255A57FA00E217F9 /* YapDatabase+Promise.swift */; };
@ -545,7 +549,6 @@
C37F5385255B94F6002AEA92 /* SelectRecipientViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF34E255B6DC8007E1867 /* SelectRecipientViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
C37F5396255B95BD002AEA92 /* OWSAnyTouchGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */; settings = {ATTRIBUTES = (Public, ); }; };
C37F5414255BAFA7002AEA92 /* SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; };
C37F54CB255BB53F002AEA92 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
C37F54DC255BB84A002AEA92 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; };
C38D5E8D2575011E00B6A65C /* MessageSender+ClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38D5E8C2575011E00B6A65C /* MessageSender+ClosedGroups.swift */; };
C38EF00C255B61CC007E1867 /* SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; };
@ -705,8 +708,6 @@
C3A71D0B2558989C0043A11F /* MessageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D0A2558989C0043A11F /* MessageWrapper.swift */; };
C3A71D1E25589AC30043A11F /* WebSocketProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D1C25589AC30043A11F /* WebSocketProto.swift */; };
C3A71D1F25589AC30043A11F /* WebSocketResources.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D1D25589AC30043A11F /* WebSocketResources.pb.swift */; };
C3A71D5425589FF30043A11F /* NSData+messagePadding.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4825589FF20043A11F /* NSData+messagePadding.m */; };
C3A71D5A25589FF30043A11F /* NSData+messagePadding.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3A71F892558BA9F0043A11F /* Mnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71F882558BA9F0043A11F /* Mnemonic.swift */; };
C3A7211A2558BCA10043A11F /* DiffieHellman.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D662558A0170043A11F /* DiffieHellman.swift */; };
C3A721382558BDFA0043A11F /* OpenGroupMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A721342558BDF90043A11F /* OpenGroupMessage.swift */; };
@ -758,7 +759,6 @@
C3C2A7852553AAF300C340D1 /* SessionProtos.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A7832553AAF300C340D1 /* SessionProtos.pb.swift */; };
C3C2A8662553B41A00C340D1 /* SessionProtocolKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C3C2A8642553B41A00C340D1 /* SessionProtocolKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3C2A86A2553B41A00C340D1 /* SessionProtocolKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
C3C2AAC82553C25300C340D1 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
C3C2ABD22553C6C900C340D1 /* Data+SecureRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2ABD12553C6C900C340D1 /* Data+SecureRandom.swift */; };
C3C2AC2E2553CBEB00C340D1 /* String+Trimming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2AC2D2553CBEB00C340D1 /* String+Trimming.swift */; };
C3C2AC372553CCE600C340D1 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; };
@ -1877,9 +1877,9 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C32A027E25A80428000ED5D4 /* SessionProtocolKit.framework in Frameworks */,
C38EF48A255B7E3F007E1867 /* SessionUIKit.framework in Frameworks */,
C33FD9C2255A54EF00E217F9 /* SessionMessagingKit.framework in Frameworks */,
C33FD9C3255A54EF00E217F9 /* SessionProtocolKit.framework in Frameworks */,
C33FD9C4255A54EF00E217F9 /* SessionSnodeKit.framework in Frameworks */,
C33FD9C5255A54EF00E217F9 /* SessionUtilitiesKit.framework in Frameworks */,
B3E0C9C6F1633B1ABCE5AD0B /* Pods_SignalUtilitiesKit.framework in Frameworks */,
@ -1907,8 +1907,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C32A027D25A80423000ED5D4 /* SessionProtocolKit.framework in Frameworks */,
5DF9AB212C6DB1E8BE70EFF6 /* Pods_SessionMessagingKit.framework in Frameworks */,
C3C2AAC82553C25300C340D1 /* SessionProtocolKit.framework in Frameworks */,
C3C2A70B25539E1E00C340D1 /* SessionSnodeKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1926,8 +1926,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C32A027F25A80432000ED5D4 /* SessionProtocolKit.framework in Frameworks */,
C37F54DC255BB84A002AEA92 /* SessionSnodeKit.framework in Frameworks */,
C37F54CB255BB53F002AEA92 /* SessionProtocolKit.framework in Frameworks */,
C37F5414255BAFA7002AEA92 /* SignalUtilitiesKit.framework in Frameworks */,
455A16DD1F1FEA0000F86704 /* Metal.framework in Frameworks */,
455A16DE1F1FEA0000F86704 /* MetalKit.framework in Frameworks */,
@ -3162,6 +3162,8 @@
C33FDB7F255A581100E217F9 /* FullTextSearchFinder.swift */,
C33FDBC1255A581700E217F9 /* General.swift */,
C3A71D0A2558989C0043A11F /* MessageWrapper.swift */,
C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */,
C3A71D4825589FF20043A11F /* NSData+messagePadding.m */,
C38EF2F5255B6DBC007E1867 /* OWSAudioPlayer.h */,
C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */,
C38EF281255B6D84007E1867 /* OWSAudioSession.swift */,
@ -3324,8 +3326,6 @@
B8CA010A25A293530091AF73 /* ClosedGroupRatchet.swift */,
B8CA010025A293260091AF73 /* ClosedGroupSenderKey.swift */,
B8CA011425A293800091AF73 /* Configuration.swift */,
C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */,
C3A71D4825589FF20043A11F /* NSData+messagePadding.m */,
B8CA011E25A2939F0091AF73 /* SharedSenderKeys.swift */,
B8CA014025A293EE0091AF73 /* Storage.swift */,
);
@ -3669,6 +3669,7 @@
C32C5EF7256DF567003C73A2 /* TSDatabaseView.h in Headers */,
C3A3A0B3256E17F2004D228D /* SSKJobRecord.h in Headers */,
B8856ED7256F1EB4001CE70E /* OWSPreferences.h in Headers */,
C32A026C25A801AF000ED5D4 /* NSData+messagePadding.h in Headers */,
C32C5BE6256DC891003C73A2 /* OWSReadReceiptManager.h in Headers */,
C32C5EC3256DE133003C73A2 /* OWSQuotedReplyModel.h in Headers */,
C32C5BF8256DC8F6003C73A2 /* OWSDisappearingMessagesJob.h in Headers */,
@ -3702,7 +3703,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
C3A71D5A25589FF30043A11F /* NSData+messagePadding.h in Headers */,
C3C2A8662553B41A00C340D1 /* SessionProtocolKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -4740,6 +4740,7 @@
C3C2A74D2553A39700C340D1 /* VisibleMessage.swift in Sources */,
C32C5AAD256DBE8F003C73A2 /* TSInfoMessage.m in Sources */,
C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */,
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */,
C352A3932557883D00338F3E /* JobDelegate.swift in Sources */,
C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */,
C3A3A108256E1A5C004D228D /* OWSIncomingMessageFinder.m in Sources */,
@ -4868,7 +4869,6 @@
files = (
B8CA010125A293260091AF73 /* ClosedGroupSenderKey.swift in Sources */,
B8CA011525A293800091AF73 /* Configuration.swift in Sources */,
C3A71D5425589FF30043A11F /* NSData+messagePadding.m in Sources */,
B8CA011F25A2939F0091AF73 /* SharedSenderKeys.swift in Sources */,
B8CA010B25A293530091AF73 /* ClosedGroupRatchet.swift in Sources */,
B8CA014125A293EE0091AF73 /* Storage.swift in Sources */,

View File

@ -1,5 +1,4 @@
import SessionMessagingKit
import SessionProtocolKit
import SessionSnodeKit
extension OWSPrimaryStorage : OWSPrimaryStorageProtocol { }
@ -10,7 +9,6 @@ public final class Configuration : NSObject {
@objc public static func performMainSetup() {
SNMessagingKit.configure(storage: Storage.shared)
SNSnodeKit.configure(storage: Storage.shared)
SNProtocolKit.configure(storage: Storage.shared, sharedSenderKeysDelegate: MessageSender.shared)
SNUtilitiesKit.configure(owsPrimaryStorage: OWSPrimaryStorage.shared(), maxFileSize: UInt(Double(FileServerAPI.maxFileSize) / FileServerAPI.fileSizeORMultiplier))
}
}