@ -17,6 +17,14 @@ public final class SyncMessagesProtocol : NSObject {
internal static var storage : OWSPrimaryStorage { OWSPrimaryStorage . shared ( ) }
// MARK: - E r r o r
@objc ( LKSyncMessagesProtocolError )
public class SyncMessagesProtocolError : NSError { // N o t c a l l e d ` E r r o r ` f o r O b j - C i n t e r o p e r a b l i t y
@objc public static let privateKeyMissing = SyncMessagesProtocolError ( domain : " SyncMessagesProtocolErrorDomain " , code : 1 , userInfo : [ NSLocalizedDescriptionKey : " Couldn't get private key for SSK based closed group. " ] )
}
// MARK: - S e n d i n g
@objc public static func syncProfile ( ) {
@ -59,6 +67,51 @@ public final class SyncMessagesProtocol : NSObject {
return AnyPromise . from ( when ( fulfilled : promises ) )
}
@objc ( syncClosedGroup : transaction : )
public static func syncClosedGroup ( _ thread : TSGroupThread , using transaction : YapDatabaseReadWriteTransaction ) -> AnyPromise {
// P r e p a r e
let messageSenderJobQueue = SSKEnvironment . shared . messageSenderJobQueue
let group = thread . groupModel
let groupPublicKey = LKGroupUtilities . getDecodedGroupID ( group . groupId )
let name = group . groupName !
let members = group . groupMemberIds
let admins = group . groupAdminIds
guard let groupPrivateKey = Storage . getClosedGroupPrivateKey ( for : groupPublicKey ) else {
print ( " [Loki] Couldn't get private key for SSK based closed group. " )
return AnyPromise . from ( Promise < Void > ( error : SyncMessagesProtocolError . privateKeyMissing ) )
}
// G e n e r a t e r a t c h e t s f o r t h e u s e r ' s l i n k e d d e v i c e s
let userPublicKey = getUserHexEncodedPublicKey ( )
let masterPublicKey = UserDefaults . standard [ . masterHexEncodedPublicKey ] ? ? userPublicKey
let deviceLinks = storage . getDeviceLinks ( for : userPublicKey , in : transaction )
let linkedDevices = deviceLinks . flatMap { [ $0 . master . hexEncodedPublicKey , $0 . slave . hexEncodedPublicKey ] } . filter { $0 != userPublicKey }
let senderKeys : [ ClosedGroupSenderKey ] = linkedDevices . map { publicKey in
let ratchet = SharedSenderKeysImplementation . shared . generateRatchet ( for : groupPublicKey , senderPublicKey : publicKey , using : transaction )
return ClosedGroupSenderKey ( chainKey : Data ( hex : ratchet . chainKey ) , keyIndex : ratchet . keyIndex , publicKey : publicKey )
}
// S e n d a c l o s e d g r o u p u p d a t e m e s s a g e t o t h e e x i s t i n g m e m b e r s w i t h t h e l i n k e d d e v i c e s ' r a t c h e t s ( t h i s m e s s a g e i s a i m e d a t t h e g r o u p )
func sendMessageToGroup ( ) {
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage . Kind . info ( groupPublicKey : Data ( hex : groupPublicKey ) , name : name , senderKeys : senderKeys ,
members : members , admins : admins )
let closedGroupUpdateMessage = ClosedGroupUpdateMessage ( thread : thread , kind : closedGroupUpdateMessageKind )
messageSenderJobQueue . add ( message : closedGroupUpdateMessage , transaction : transaction )
}
sendMessageToGroup ( )
// S e n d c l o s e d g r o u p u p d a t e m e s s a g e s t o t h e l i n k e d d e v i c e s u s i n g e s t a b l i s h e d c h a n n e l s
func sendMessageToLinkedDevices ( ) {
let allSenderKeys = [ ClosedGroupSenderKey ] ( Storage . getAllClosedGroupSenderKeys ( for : groupPublicKey ) ) // T h i s i n c l u d e s t h e n e w l y g e n e r a t e d s e n d e r k e y s
let thread = TSContactThread . getOrCreateThread ( withContactId : masterPublicKey , transaction : transaction )
thread . save ( with : transaction )
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage . Kind . new ( groupPublicKey : Data ( hex : groupPublicKey ) , name : name ,
groupPrivateKey : Data ( hex : groupPrivateKey ) , senderKeys : allSenderKeys , members : members , admins : admins )
let closedGroupUpdateMessage = ClosedGroupUpdateMessage ( thread : thread , kind : closedGroupUpdateMessageKind )
messageSenderJobQueue . add ( message : closedGroupUpdateMessage , transaction : transaction ) // T h i s i n t e r n a l l y t a k e s c a r e o f m u l t i d e v i c e
}
sendMessageToLinkedDevices ( )
// R e t u r n a d u m m y p r o m i s e
return AnyPromise . from ( Promise < Void > { $0 . fulfill ( ( ) ) } )
}
@objc public static func syncAllClosedGroups ( ) -> AnyPromise {
var closedGroups : [ TSGroupThread ] = [ ]
TSGroupThread . enumerateCollectionObjects { object , _ in