This commit is contained in:
Niels Andriesse 2020-11-23 16:35:49 +11:00
parent bbd3ecd54a
commit aec182f36c
12 changed files with 20 additions and 384 deletions

View File

@ -3248,12 +3248,6 @@ typedef enum : NSUInteger {
// }];
[self messageWasSent:message];
if ([self.thread isKindOfClass:TSContactThread.class]) {
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKSessionManagementProtocol sendSessionRequestIfNeededToPublicKey:self.thread.contactIdentifier transaction:transaction];
}];
}
});
}

View File

@ -950,7 +950,7 @@ static CGRect oldframe;
if (gThread.usesSharedSenderKeys) {
NSString *groupPublicKey = [LKGroupUtilities getDecodedGroupID:gThread.groupModel.groupId];
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[[LKClosedGroupsProtocol leaveGroupWithPublicKey:groupPublicKey transaction:transaction] retainUntilComplete];
[[SNMessageSenderDelegate leaveGroupWithPublicKey:groupPublicKey transaction:transaction] retainUntilComplete];
}];
}
@ -1197,21 +1197,7 @@ static CGRect oldframe;
- (void)resetSecureSession
{
if (![self.thread isKindOfClass:TSContactThread.class]) { return; }
TSContactThread *thread = (TSContactThread *)self.thread;
__weak OWSConversationSettingsViewController *weakSelf = self;
NSString *message = @"This may help if you're having encryption problems in this conversation. Your messages will be kept.";
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Reset Secure Session?" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") style:UIAlertActionStyleDefault handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"Reset" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
dispatch_async(dispatch_get_main_queue(), ^{
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKSessionManagementProtocol startSessionResetInThread:thread transaction:transaction];
}];
[weakSelf.navigationController popViewControllerAnimated:YES];
});
}]];
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark - Notifications

View File

@ -209,7 +209,7 @@ final class EditClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelega
guard !name.isEmpty else {
return showError(title: NSLocalizedString("vc_create_closed_group_group_name_missing_error", comment: ""))
}
guard name.count < ClosedGroupsProtocol.maxNameSize else {
guard name.count < 64 else {
return showError(title: NSLocalizedString("vc_create_closed_group_group_name_too_long_error", comment: ""))
}
isEditingGroupName = false
@ -253,7 +253,7 @@ final class EditClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelega
}
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
Storage.writeSync { [weak self] transaction in
ClosedGroupsProtocol.update(groupPublicKey, with: members, name: name, transaction: transaction).done(on: DispatchQueue.main) {
MessageSenderDelegate.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)

View File

@ -384,7 +384,7 @@ 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 _ = ClosedGroupsProtocol.leave(groupPublicKey, using: transaction).ensure {
let _ = MessageSenderDelegate.leave(groupPublicKey, using: transaction).ensure {
Storage.writeSync { transaction in
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)

View File

@ -157,20 +157,20 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
guard let name = nameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), name.count > 0 else {
return showError(title: NSLocalizedString("vc_create_closed_group_group_name_missing_error", comment: ""))
}
guard name.count < ClosedGroupsProtocol.maxNameSize else {
guard name.count < 64 else {
return showError(title: NSLocalizedString("vc_create_closed_group_group_name_too_long_error", comment: ""))
}
guard selectedContacts.count >= 1 else {
return showError(title: "Please pick at least 1 group member")
}
guard selectedContacts.count < ClosedGroupsProtocol.groupSizeLimit else { // Minus one because we're going to include self later
guard selectedContacts.count < 20 else { // Minus one because we're going to include self later
return showError(title: NSLocalizedString("vc_create_closed_group_too_many_group_members_error", comment: ""))
}
let selectedContacts = self.selectedContacts
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
var promise: Promise<TSGroupThread>!
Storage.writeSync { transaction in
promise = ClosedGroupsProtocol.createClosedGroup(name: name, members: selectedContacts, transaction: transaction)
promise = MessageSenderDelegate.createClosedGroup(name: name, members: selectedContacts, transaction: transaction)
}
let _ = promise.done(on: DispatchQueue.main) { thread in
self?.presentingViewController?.dismiss(animated: true, completion: nil)

View File

@ -403,7 +403,6 @@
C33FDCAE255A582000E217F9 /* SSKEnvironment.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF4255A580600E217F9 /* SSKEnvironment.m */; };
C33FDCB1255A582000E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF7255A580600E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.m */; };
C33FDCB3255A582000E217F9 /* TSContactThread.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF9255A580600E217F9 /* TSContactThread.m */; };
C33FDCB5255A582000E217F9 /* SessionMetaProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAFB255A580600E217F9 /* SessionMetaProtocol.swift */; };
C33FDCB7255A582000E217F9 /* LRUCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAFD255A580600E217F9 /* LRUCache.swift */; };
C33FDCB8255A582000E217F9 /* OWSStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAFE255A580600E217F9 /* OWSStorage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDCB9255A582000E217F9 /* DisplayNameUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAFF255A580600E217F9 /* DisplayNameUtilities.swift */; };
@ -485,7 +484,6 @@
C33FDD56255A582000E217F9 /* TSIncomingMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB9C255A581300E217F9 /* TSIncomingMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD5A255A582000E217F9 /* TSStorageHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBA0255A581400E217F9 /* TSStorageHeaders.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD5B255A582000E217F9 /* OWSOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBA1255A581400E217F9 /* OWSOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD5D255A582000E217F9 /* SessionManagementProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBA3255A581400E217F9 /* SessionManagementProtocol.swift */; };
C33FDD5E255A582000E217F9 /* OWSDisappearingMessagesConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBA4255A581400E217F9 /* OWSDisappearingMessagesConfiguration.m */; };
C33FDD60255A582000E217F9 /* TSInvalidIdentityKeySendingErrorMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBA6255A581400E217F9 /* TSInvalidIdentityKeySendingErrorMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD62255A582000E217F9 /* OWSLinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBA8255A581500E217F9 /* OWSLinkPreview.swift */; };
@ -1511,7 +1509,6 @@
C33FDAF4255A580600E217F9 /* SSKEnvironment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKEnvironment.m; sourceTree = "<group>"; };
C33FDAF7255A580600E217F9 /* OWSPrimaryStorage+SignedPreKeyStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+SignedPreKeyStore.m"; sourceTree = "<group>"; };
C33FDAF9255A580600E217F9 /* TSContactThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSContactThread.m; sourceTree = "<group>"; };
C33FDAFB255A580600E217F9 /* SessionMetaProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionMetaProtocol.swift; sourceTree = "<group>"; };
C33FDAFC255A580600E217F9 /* MIMETypeUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIMETypeUtil.h; sourceTree = "<group>"; };
C33FDAFD255A580600E217F9 /* LRUCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LRUCache.swift; sourceTree = "<group>"; };
C33FDAFE255A580600E217F9 /* OWSStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSStorage.h; sourceTree = "<group>"; };
@ -1605,7 +1602,6 @@
C33FDB9E255A581400E217F9 /* TSAttachmentPointer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachmentPointer.m; sourceTree = "<group>"; };
C33FDBA0255A581400E217F9 /* TSStorageHeaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSStorageHeaders.h; sourceTree = "<group>"; };
C33FDBA1255A581400E217F9 /* OWSOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOperation.h; sourceTree = "<group>"; };
C33FDBA3255A581400E217F9 /* SessionManagementProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionManagementProtocol.swift; sourceTree = "<group>"; };
C33FDBA4255A581400E217F9 /* OWSDisappearingMessagesConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisappearingMessagesConfiguration.m; sourceTree = "<group>"; };
C33FDBA6255A581400E217F9 /* TSInvalidIdentityKeySendingErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSInvalidIdentityKeySendingErrorMessage.h; sourceTree = "<group>"; };
C33FDBA8255A581500E217F9 /* OWSLinkPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSLinkPreview.swift; sourceTree = "<group>"; };
@ -3272,8 +3268,6 @@
C33FDAFF255A580600E217F9 /* DisplayNameUtilities.swift */,
C33FDBF4255A581B00E217F9 /* DisplayNameUtilities2.swift */,
C33FDBB9255A581600E217F9 /* ProfileManagerProtocol.h */,
C33FDBA3255A581400E217F9 /* SessionManagementProtocol.swift */,
C33FDAFB255A580600E217F9 /* SessionMetaProtocol.swift */,
C33FDADF255A580400E217F9 /* PublicChatManager.swift */,
C38EF3E5255B6DF4007E1867 /* ContactCellView.h */,
C38EF3D6255B6DEF007E1867 /* ContactCellView.m */,
@ -5075,7 +5069,6 @@
C38EF218255B6D3B007E1867 /* Theme.m in Sources */,
C38EF3BA255B6DE7007E1867 /* ImageEditorItem.swift in Sources */,
B8D8F1382566120F0092EF10 /* Storage+ClosedGroups.swift in Sources */,
C33FDD5D255A582000E217F9 /* SessionManagementProtocol.swift in Sources */,
C38EF3F7255B6DF7007E1867 /* OWSNavigationBar.swift in Sources */,
C33FDD76255A582000E217F9 /* SSKKeychainStorage.swift in Sources */,
C33FDDA6255A582000E217F9 /* OWSRecipientIdentity.m in Sources */,
@ -5097,7 +5090,6 @@
C33FDD13255A582000E217F9 /* OWSFailedAttachmentDownloadsJob.m in Sources */,
C38EF24D255B6D67007E1867 /* UIView+OWS.swift in Sources */,
C3CA3B13255CF18200F4C6D4 /* Destination+Conversion.swift in Sources */,
C33FDCB5255A582000E217F9 /* SessionMetaProtocol.swift in Sources */,
C33FDD4D255A582000E217F9 /* PreKeyBundle+jsonDict.m in Sources */,
C38EF24B255B6D67007E1867 /* UIView+OWS.m in Sources */,
C33FDC51255A582000E217F9 /* TSIncomingMessage.m in Sources */,

View File

@ -165,10 +165,12 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa
TSThread *thread = [TSContactThread getOrCreateThreadWithContactId:recipientId];
if (![LKSessionMetaProtocol shouldSendReceiptInThread:thread]) {
if (thread.isGroupThread) { // Don't send receipts in group threads
continue;
}
// TODO TODO TODO
/*
OWSReceiptsForSenderMessage *message;
NSString *receiptName;

View File

@ -289,7 +289,6 @@ public final class MessageReceiverDelegate : SessionMessagingKit.MessageReceiver
}
// Respond to the request
SNLog("Responding to sender key request from: \(message.sender!).")
SessionManagementProtocol.sendSessionRequestIfNeeded(to: message.sender!, using: transaction)
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))

View File

@ -1,7 +1,9 @@
import PromiseKit
public final class MessageSenderDelegate : SessionMessagingKit.MessageSenderDelegate, SharedSenderKeysDelegate {
@objc(SNMessageSenderDelegate)
public final class MessageSenderDelegate : NSObject, SessionMessagingKit.MessageSenderDelegate, SharedSenderKeysDelegate {
// MARK: Error
public enum Error : LocalizedError {
case noThread
case noPrivateKey
@ -16,8 +18,12 @@ public final class MessageSenderDelegate : SessionMessagingKit.MessageSenderDele
}
}
// MARK: Initialization
public static let shared = MessageSenderDelegate()
private override init() { }
// MARK: Sending
public func handleSuccessfulMessageSend(_ message: Message, using transaction: Any) {
guard let tsMessage = TSOutgoingMessage.find(withTimestamp: message.sentTimestamp!) else { return }
if let openGroupServerMessageID = message.openGroupServerMessageID {
@ -30,6 +36,7 @@ public final class MessageSenderDelegate : SessionMessagingKit.MessageSenderDele
// TODO: Implement
}
// MARK: Closed Groups
public static func createClosedGroup(name: String, members: Set<String>, transaction: YapDatabaseReadWriteTransaction) -> Promise<TSGroupThread> {
// Prepare
var members = members

View File

@ -309,7 +309,7 @@ public class TypingIndicatorsImpl: NSObject, TypingIndicators {
return
}
if !SessionMetaProtocol.shouldSendTypingIndicator(in: thread) { return }
if thread.isGroupThread() { return } // Don't send typing indicators in group threads
let typingIndicator = TypingIndicator()
typingIndicator.kind = action

View File

@ -1,225 +0,0 @@
import PromiseKit
// A few notes about making changes in this file:
//
// Don't use a database transaction if you can avoid it.
// If you do need to use a database transaction, use a read transaction if possible.
// For write transactions, consider making it the caller's responsibility to manage the database transaction (this helps avoid unnecessary transactions).
// Think carefully about adding a function; there might already be one for what you need.
// Document the expected cases in which a function will be used
// Express those cases in tests.
@objc(LKSessionManagementProtocol)
public final class SessionManagementProtocol : NSObject {
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
// MARK: - General
@objc(createPreKeys)
public static func createPreKeys() {
// We don't generate new pre keys here like Signal does.
// This is because we need the records to be linked to a contact since we don't have a central server.
// It's done automatically when we generate a pre key bundle to send to a contact (generatePreKeyBundleForContact:).
// You can use getOrCreatePreKeyForContact: to generate one if needed.
let signedPreKeyRecord = storage.generateRandomSignedRecord()
signedPreKeyRecord.markAsAcceptedByService()
storage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
storage.setCurrentSignedPrekeyId(signedPreKeyRecord.id)
SNLog("Pre keys created successfully.")
}
@objc(refreshSignedPreKey)
public static func refreshSignedPreKey() {
// We don't generate new pre keys here like Signal does.
// This is because we need the records to be linked to a contact since we don't have a central server.
// It's done automatically when we generate a pre key bundle to send to a contact (generatePreKeyBundleForContact:).
// You can use getOrCreatePreKeyForContact: to generate one if needed.
guard storage.currentSignedPrekeyId() == nil else { return }
let signedPreKeyRecord = storage.generateRandomSignedRecord()
signedPreKeyRecord.markAsAcceptedByService()
storage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
storage.setCurrentSignedPrekeyId(signedPreKeyRecord.id)
TSPreKeyManager.clearPreKeyUpdateFailureCount()
TSPreKeyManager.clearSignedPreKeyRecords()
SNLog("Signed pre key refreshed successfully.")
}
@objc(rotateSignedPreKey)
public static func rotateSignedPreKey() {
// This is identical to what Signal does, except that it doesn't upload the signed pre key
// to a server.
let signedPreKeyRecord = storage.generateRandomSignedRecord()
signedPreKeyRecord.markAsAcceptedByService()
storage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
storage.setCurrentSignedPrekeyId(signedPreKeyRecord.id)
TSPreKeyManager.clearPreKeyUpdateFailureCount()
TSPreKeyManager.clearSignedPreKeyRecords()
SNLog("Signed pre key rotated successfully.")
}
// MARK: - Sending
@objc(isSessionRequiredForMessage:recipientID:transaction:)
public static func isSessionRequired(for message: TSOutgoingMessage, recipientID: String, transaction: YapDatabaseReadWriteTransaction) -> Bool {
return false
// if SharedSenderKeysImplementation.shared.isClosedGroup(recipientID) {
// return false
// } else {
// return !shouldUseFallbackEncryption(for: message, recipientID: recipientID, transaction: transaction)
// }
}
@objc(shouldUseFallbackEncryptionForMessage:recipientID:transaction:)
public static func shouldUseFallbackEncryption(for message: TSOutgoingMessage, recipientID: String, transaction: YapDatabaseReadWriteTransaction) -> Bool {
return true
// if SharedSenderKeysImplementation.shared.isClosedGroup(recipientID) { return false }
// else if message is SessionRequestMessage { return true }
// else if message is EndSessionMessage { return true }
// else if let message = message as? DeviceLinkMessage, message.kind == .request { return true }
// else if message is OWSOutgoingNullMessage { return false }
// return !storage.containsSession(recipientID, deviceId: Int32(OWSDevicePrimaryDeviceId), protocolContext: transaction)
}
private static func hasSentSessionRequestExpired(for publicKey: String) -> Bool {
return false
// let timestamp = Storage.getSessionRequestSentTimestamp(for: publicKey)
// let expiration = timestamp + TTLUtilities.getTTL(for: .sessionRequest)
// return NSDate.ows_millisecondTimeStamp() > expiration
}
@objc(sendSessionRequestIfNeededToPublicKey:transaction:)
public static func sendSessionRequestIfNeeded(to publicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
// It's never necessary to establish a session with self
guard publicKey != getUserHexEncodedPublicKey() else { return }
// Check that we don't already have a session
let hasSession = storage.containsSession(publicKey, deviceId: Int32(1), protocolContext: transaction)
guard !hasSession else { return }
// Check that we didn't already send a session request
let hasSentSessionRequest = (Storage.shared.getSessionRequestSentTimestamp(for: publicKey) > 0)
let hasSentSessionRequestExpired = SessionManagementProtocol.hasSentSessionRequestExpired(for: publicKey)
if hasSentSessionRequestExpired {
Storage.shared.setSessionRequestSentTimestamp(for: publicKey, to: 0, using: transaction)
}
guard !hasSentSessionRequest || hasSentSessionRequestExpired else { return }
// Create the thread if needed
let thread = TSContactThread.getOrCreateThread(withContactId: publicKey, transaction: transaction)
thread.save(with: transaction)
// Send the session request
SNLog("Sending session request to: \(publicKey).")
Storage.shared.setSessionRequestSentTimestamp(for: publicKey, to: NSDate.ows_millisecondTimeStamp(), using: transaction)
let sessionRequest = SessionRequest()
sessionRequest.preKeyBundle = storage.generatePreKeyBundle(forContact: publicKey)
MessageSender.send(sessionRequest, in: thread, using: transaction)
}
@objc(sendNullMessageToPublicKey:transaction:)
public static func sendNullMessage(to publicKey: String, in transaction: YapDatabaseReadWriteTransaction) {
let thread = TSContactThread.getOrCreateThread(withContactId: publicKey, transaction: transaction)
thread.save(with: transaction)
let nullMessage = NullMessage()
MessageSender.send(nullMessage, in: thread, using: transaction)
}
/// - Note: Deprecated.
///
/// Only relevant for closed groups that don't use shared sender keys.
@objc(shouldIgnoreMissingPreKeyBundleExceptionForMessage:to:)
public static func shouldIgnoreMissingPreKeyBundleException(for message: TSOutgoingMessage, to hexEncodedPublicKey: String) -> Bool {
// When a closed group is created, members try to establish sessions with eachother in the background through
// session requests. Until ALL users those session requests were sent to have come online, stored the pre key
// bundles contained in the session requests and replied with background messages to finalize the session
// creation, a given user won't be able to successfully send a message to all members of a group. This check
// is so that until we can do better on this front the user at least won't see this as an error in the UI.
guard let groupThread = message.thread as? TSGroupThread else { return false }
return groupThread.groupModel.groupType == .closedGroup && !groupThread.usesSharedSenderKeys
}
@objc(startSessionResetInThread:transaction:)
public static func startSessionReset(in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) {
// // Check preconditions
// guard let thread = thread as? TSContactThread else {
// return SNLog("Can't restore session for non contact thread.")
// }
// // Send end session messages to the devices requiring session restoration
// let devices = thread.sessionRestoreDevices // TODO: Rename this to something that reads better
// for device in devices {
// guard ECKeyPair.isValidHexEncodedPublicKey(candidate: device) else { continue }
// let thread = TSContactThread.getOrCreateThread(withContactId: device, transaction: transaction)
// thread.save(with: transaction)
// /*
// let endSessionMessage = EndSessionMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread)
// let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
// messageSenderJobQueue.add(message: endSessionMessage, transaction: transaction)
// */
// }
// thread.removeAllSessionRestoreDevices(with: transaction)
// // Notify the user
// let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeLokiSessionResetInProgress)
// infoMessage.save(with: transaction)
// // Update the session reset status
// thread.sessionResetStatus = .initiated
// thread.save(with: transaction)
}
// MARK: - Receiving
@objc(handleDecryptionError:forPublicKey:transaction:)
public static func handleDecryptionError(_ errorMessage: TSErrorMessage, for publicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
// let type = errorMessage.errorType
// let masterPublicKey = storage.getMasterHexEncodedPublicKey(for: publicKey, in: transaction) ?? publicKey
// let thread = TSContactThread.getOrCreateThread(withContactId: masterPublicKey, transaction: transaction)
// let restorationTimeInMs = UInt64(storage.getRestorationTime() * 1000)
// // Show the session reset prompt upon certain errors
// switch type {
// case .noSession, .invalidMessage, .invalidKeyException:
// if restorationTimeInMs > errorMessage.timestamp {
// // Automatically rebuild session after restoration
// sendSessionRequestIfNeeded(to: publicKey, using: transaction)
// } else {
// // Store the source device's public key in case it was a secondary device
// thread.addSessionRestoreDevice(publicKey, transaction: transaction)
// }
// default: break
// }
}
private static func shouldProcessSessionRequest(from publicKey: String, at timestamp: UInt64) -> Bool {
let sentTimestamp = Storage.shared.getSessionRequestSentTimestamp(for: publicKey)
let processedTimestamp = Storage.shared.getSessionRequestProcessedTimestamp(for: publicKey)
let restorationTimestamp = UInt64(storage.getRestorationTime() * 1000)
return timestamp > sentTimestamp && timestamp > processedTimestamp && timestamp > restorationTimestamp
}
@objc(handlePreKeyBundleMessageIfNeeded:wrappedIn:transaction:)
public static func handlePreKeyBundleMessageIfNeeded(_ protoContent: SNProtoContent, wrappedIn envelope: SNProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) {
// let publicKey = envelope.source! // Set during UD decryption
// guard let preKeyBundleMessage = protoContent.prekeyBundleMessage else { return }
// SNLog("Received a pre key bundle message from: \(publicKey).")
// guard let preKeyBundle = preKeyBundleMessage.getPreKeyBundle(with: transaction) else {
// return SNLog("Couldn't parse pre key bundle received from: \(publicKey).")
// }
// if !shouldProcessSessionRequest(from: publicKey, at: envelope.timestamp) {
// return SNLog("Ignoring session request from: \(publicKey).")
// }
// storage.setPreKeyBundle(preKeyBundle, forContact: publicKey, transaction: transaction)
// Storage.setSessionRequestProcessedTimestamp(for: publicKey, to: NSDate.ows_millisecondTimeStamp(), using: transaction)
// sendNullMessage(to: publicKey, in: transaction)
}
@objc(handleEndSessionMessageReceivedInThread:using:)
public static func handleEndSessionMessageReceived(in thread: TSContactThread, using transaction: YapDatabaseReadWriteTransaction) {
// let publicKey = thread.contactIdentifier()
// SNLog("End session message received from: \(publicKey).")
// // Notify the user
// let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeLokiSessionResetInProgress)
// infoMessage.save(with: transaction)
// // Archive all sessions
// storage.archiveAllSessions(forContact: publicKey, protocolContext: transaction)
// // Update the session reset status
// thread.sessionResetStatus = .requestReceived
// thread.save(with: transaction)
// // Send a null message
// sendNullMessage(to: publicKey, in: transaction)
}
}

View File

@ -1,119 +0,0 @@
import PromiseKit
// A few notes about making changes in this file:
//
// Don't use a database transaction if you can avoid it.
// If you do need to use a database transaction, use a read transaction if possible.
// For write transactions, consider making it the caller's responsibility to manage the database transaction (this helps avoid unnecessary transactions).
// Think carefully about adding a function; there might already be one for what you need.
// Document the expected cases in which a function will be used
// Express those cases in tests.
/// See [Receipts, Transcripts & Typing Indicators](https://github.com/loki-project/session-protocol-docs/wiki/Receipts,-Transcripts-&-Typing-Indicators) for more information.
@objc(LKSessionMetaProtocol)
public final class SessionMetaProtocol : NSObject {
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
// MARK: - Sending
// MARK: Message Destination(s)
@objc public static func getDestinationsForOutgoingSyncMessage() -> NSMutableSet {
return NSMutableSet(set: [ getUserHexEncodedPublicKey() ]) // return NSMutableSet(set: MultiDeviceProtocol.getUserLinkedDevices())
}
@objc(getDestinationsForOutgoingGroupMessage:inThread:)
public static func getDestinations(for outgoingGroupMessage: TSOutgoingMessage, in thread: TSThread) -> NSMutableSet {
guard let thread = thread as? TSGroupThread else { preconditionFailure("Can't get destinations for group message in non-group thread.") }
var result: Set<String> = []
if thread.isOpenGroup {
if let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!) {
result = [ openGroup.server ] // Aim the message at the open group server
} else {
// Should never occur
}
} else {
if let groupThread = thread as? TSGroupThread, groupThread.usesSharedSenderKeys {
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupThread.groupModel.groupId)
result = [ groupPublicKey ]
} else {
result = Set(outgoingGroupMessage.sendingRecipientIds())
.intersection(thread.groupModel.groupMemberIds)
.subtracting([ getUserHexEncodedPublicKey() ]) // .subtracting(MultiDeviceProtocol.getUserLinkedDevices())
}
}
return NSMutableSet(set: result)
}
// MARK: Note to Self
@objc(isThreadNoteToSelf:)
public static func isThreadNoteToSelf(_ thread: TSThread) -> Bool {
guard let thread = thread as? TSContactThread else { return false }
return thread.contactIdentifier() == getUserHexEncodedPublicKey()
/*
var isNoteToSelf = false
storage.dbReadConnection.read { transaction in
isNoteToSelf = LokiDatabaseUtilities.isUserLinkedDevice(thread.contactIdentifier(), transaction: transaction)
}
return isNoteToSelf
*/
}
// MARK: Transcripts
@objc(shouldSendTranscriptForMessage:inThread:)
public static func shouldSendTranscript(for message: TSOutgoingMessage, in thread: TSThread) -> Bool {
guard message.shouldSyncTranscript() else { return false }
let isOpenGroupMessage = (thread as? TSGroupThread)?.isOpenGroup == true
let wouldSignalRequireTranscript = (AreRecipientUpdatesEnabled() || !message.hasSyncedTranscript)
guard wouldSignalRequireTranscript && !isOpenGroupMessage else { return false }
return false
/*
var usesMultiDevice = false
storage.dbReadConnection.read { transaction in
usesMultiDevice = !storage.getDeviceLinks(for: getUserHexEncodedPublicKey(), in: transaction).isEmpty
|| UserDefaults.standard[.masterHexEncodedPublicKey] != nil
}
return usesMultiDevice
*/
}
// MARK: Typing Indicators
/// Invoked only if typing indicators are enabled in the settings. Provides an opportunity
/// to avoid sending them if certain conditions are met.
@objc(shouldSendTypingIndicatorInThread:)
public static func shouldSendTypingIndicator(in thread: TSThread) -> Bool {
return !thread.isGroupThread() && thread.numberOfInteractions() > 0
}
// MARK: Receipts
@objc(shouldSendReceiptInThread:)
public static func shouldSendReceipt(in thread: TSThread) -> Bool {
return !thread.isGroupThread()
}
// MARK: - Receiving
@objc(isErrorMessageFromBeforeRestoration:)
public static func isErrorMessageFromBeforeRestoration(_ errorMessage: TSErrorMessage) -> Bool {
let restorationTimeInMs = UInt64(storage.getRestorationTime() * 1000)
return errorMessage.timestamp < restorationTimeInMs
}
@objc(updateDisplayNameIfNeededForPublicKey:using:transaction:)
public static func updateDisplayNameIfNeeded(for publicKey: String, using dataMessage: SNProtoDataMessage, in transaction: YapDatabaseReadWriteTransaction) {
guard let profile = dataMessage.profile, let displayName = profile.displayName, !displayName.isEmpty else { return }
let profileManager = SSKEnvironment.shared.profileManager
profileManager.updateProfileForContact(withID: publicKey, displayName: displayName, with: transaction)
}
@objc(updateProfileKeyIfNeededForPublicKey:using:)
public static func updateProfileKeyIfNeeded(for publicKey: String, using dataMessage: SNProtoDataMessage) {
guard dataMessage.hasProfileKey, let profileKey = dataMessage.profileKey else { return }
guard profileKey.count == kAES256_KeyByteLength else {
return SNLog("Unexpected profile key size: \(profileKey.count).")
}
let profilePictureURL = dataMessage.profile?.profilePicture
let profileManager = SSKEnvironment.shared.profileManager
profileManager.setProfileKeyData(profileKey, forRecipientId: publicKey, avatarURL: profilePictureURL)
}
}