You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-ios/SessionMessagingKit/SessionUtil/Config Handling/SessionUtil+SharedGroup.swift

210 lines
8.1 KiB
Swift

// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import GRDB
import SessionUIKit
import SessionSnodeKit
import SessionUtil
import SessionUtilitiesKit
// MARK: - Convenience
internal extension SessionUtil {
static func createGroup(
_ db: Database,
name: String,
displayPictureUrl: String?,
displayPictureFilename: String?,
displayPictureEncryptionKey: Data?,
members: [(id: String, profile: Profile?)],
admins: [(id: String, profile: Profile?)],
using dependencies: Dependencies
) throws -> (identityKeyPair: KeyPair, group: ClosedGroup, members: [GroupMember]) {
guard
let groupIdentityKeyPair: KeyPair = dependencies.crypto.generate(.ed25519KeyPair()),
let userED25519KeyPair: KeyPair = Identity.fetchUserEd25519KeyPair(db, using: dependencies)
else { throw MessageSenderError.noKeyPair }
// There will probably be custom init functions, will need a way to save the conf into
// the in-memory state after init though
var secretKey: [UInt8] = userED25519KeyPair.secretKey
var groupIdentityPublicKey: [UInt8] = groupIdentityKeyPair.publicKey
var groupIdentityPrivateKey: [UInt8] = groupIdentityKeyPair.secretKey
let groupId: SessionId = SessionId(.group, publicKey: groupIdentityKeyPair.publicKey)
let creationTimestamp: TimeInterval = TimeInterval(
SnodeAPI.currentOffsetTimestampMs(using: dependencies) / 1000
)
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
let currentUserProfile: Profile? = Profile.fetchOrCreateCurrentUser(db, using: dependencies)
// Create the new config objects
var groupKeysConf: UnsafeMutablePointer<config_group_keys>? = nil
var groupInfoConf: UnsafeMutablePointer<config_object>? = nil
var groupMembersConf: UnsafeMutablePointer<config_object>? = nil
var error: [CChar] = [CChar](repeating: 0, count: 256)
try groups_info_init(
&groupInfoConf,
&groupIdentityPublicKey,
&groupIdentityPrivateKey,
nil,
0,
&error
).orThrow(error: error)
try groups_members_init(
&groupMembersConf,
&groupIdentityPublicKey,
&groupIdentityPrivateKey,
nil,
0,
&error
).orThrow(error: error)
try groups_keys_init(
&groupKeysConf,
&secretKey,
&groupIdentityPublicKey,
&groupIdentityPrivateKey,
groupInfoConf,
groupMembersConf,
nil,
0,
&error
).orThrow(error: error)
guard
let keysConf: UnsafeMutablePointer<config_group_keys> = groupKeysConf,
let infoConf: UnsafeMutablePointer<config_object> = groupInfoConf,
let membersConf: UnsafeMutablePointer<config_object> = groupMembersConf
else {
SNLog("[SessionUtil Error] Group config objects were null")
throw SessionUtilError.unableToCreateConfigObject
}
// Set the initial values in the confs
var groupName: [CChar] = name.cArray.nullTerminated()
groups_info_set_name(groupInfoConf, &groupName)
groups_info_set_created(groupInfoConf, Int64(floor(creationTimestamp)))
if
let displayPictureUrl: String = displayPictureUrl,
let displayPictureEncryptionKey: Data = displayPictureEncryptionKey
{
var displayPic: user_profile_pic = user_profile_pic()
displayPic.url = displayPictureUrl.toLibSession()
displayPic.key = displayPictureEncryptionKey.toLibSession()
groups_info_set_pic(groupInfoConf, displayPic)
}
// Store the members/admins in the group (reduce to ensure we don't accidentally insert duplicates)
let finalMembers: [String: (profile: Profile?, isAdmin: Bool)] = members
.map { ($0.id, $0.profile, false) }
.appending(contentsOf: admins.map { ($0.id, $0.profile, true) })
.appending((currentUserPublicKey, currentUserProfile, true))
.reduce(into: [:]) { result, next in result[next.0] = (profile: next.1, isAdmin: next.2)}
try finalMembers.forEach { memberId, info in
var profilePic: user_profile_pic = user_profile_pic()
if
let picUrl: String = info.profile?.profilePictureUrl,
let picKey: Data = info.profile?.profileEncryptionKey
{
profilePic.url = picUrl.toLibSession()
profilePic.key = picKey.toLibSession()
}
try CExceptionHelper.performSafely {
var member: config_group_member = config_group_member(
session_id: memberId.toLibSession(),
name: (info.profile?.name ?? "").toLibSession(),
profile_pic: profilePic,
admin: info.isAdmin,
invited: 0,
promoted: 0
)
groups_members_set(membersConf, &member)
}
}
// Load them into memory
let groupState: [ConfigDump.Variant: Config] = [
.groupKeys: .groupKeys(keysConf, info: infoConf, members: membersConf),
.groupInfo: .object(infoConf),
.groupMembers: .object(membersConf),
]
dependencies.caches.mutate(cache: .sessionUtil) { cache in
groupState.forEach { variant, config in
cache.setConfig(for: variant, publicKey: groupId.hexString, to: config)
}
}
// Create and save dumps for the configs
try groupState.forEach { variant, config in
try SessionUtil.createDump(
config: config,
for: variant,
publicKey: groupId.hexString,
timestampMs: Int64(floor(creationTimestamp * 1000))
)?.save(db)
}
// Add the new group to the USER_GROUPS config message
try SessionUtil.add(
db,
groupIdentityPublicKey: groupId.hexString,
groupIdentityPrivateKey: Data(groupIdentityPrivateKey),
name: name,
tag: nil,
subkey: nil,
joinedAt: Int64(floor(creationTimestamp)),
using: dependencies
)
return (
groupIdentityKeyPair,
ClosedGroup(
threadId: groupId.hexString,
name: name,
formationTimestamp: creationTimestamp,
displayPictureUrl: displayPictureUrl,
displayPictureFilename: displayPictureFilename,
displayPictureEncryptionKey: displayPictureEncryptionKey,
lastDisplayPictureUpdate: creationTimestamp,
groupIdentityPrivateKey: Data(groupIdentityPrivateKey),
approved: true
),
finalMembers.map { memberId, info -> GroupMember in
GroupMember(
groupId: groupId.hexString,
profileId: memberId,
role: (info.isAdmin ? .admin : .standard),
isHidden: false
)
}
)
}
@discardableResult static func addGroup(
_ db: Database,
groupIdentityPublicKey: [UInt8],
groupIdentityPrivateKey: Data?,
name: String,
tag: Data?,
subkey: Data?,
joinedAt: Int64,
approved: Bool,
using dependencies: Dependencies
) throws -> (group: ClosedGroup, members: [GroupMember]) {
// TODO: This!!!
preconditionFailure()
}
}
private extension Int32 {
func orThrow(error: [CChar]) throws {
guard self != 0 else { return }
SNLog("[SessionUtil Error] Unable to create group config objects: \(String(cString: error))")
throw SessionUtilError.unableToCreateConfigObject
}
}