diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 11742491b..95257a5c4 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -807,23 +807,11 @@ FDC4380927B31D4E00C60D73 /* SOGSError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4380827B31D4E00C60D73 /* SOGSError.swift */; }; FDC4381527B329CE00C60D73 /* NonceGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381427B329CE00C60D73 /* NonceGenerator.swift */; }; FDC4381727B32EC700C60D73 /* Personalization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381627B32EC700C60D73 /* Personalization.swift */; }; - FDC4381A27B34EBA00C60D73 /* LegacyCompactPollBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381927B34EBA00C60D73 /* LegacyCompactPollBody.swift */; }; - FDC4381C27B354AC00C60D73 /* LegacyPublicKeyBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381B27B354AC00C60D73 /* LegacyPublicKeyBody.swift */; }; FDC4382027B36ADC00C60D73 /* SOGSEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381F27B36ADC00C60D73 /* SOGSEndpoint.swift */; }; - FDC4382627B37F6900C60D73 /* LegacyDeletedMessagesResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382527B37F6900C60D73 /* LegacyDeletedMessagesResponse.swift */; }; - FDC4382827B37FD300C60D73 /* LegacyModeratorsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382727B37FD300C60D73 /* LegacyModeratorsResponse.swift */; }; - FDC4382A27B3802D00C60D73 /* LegacyRoomsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382927B3802D00C60D73 /* LegacyRoomsResponse.swift */; }; - FDC4382C27B380E300C60D73 /* LegacyMemberCountResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382B27B380E300C60D73 /* LegacyMemberCountResponse.swift */; }; FDC4382F27B383AF00C60D73 /* UnregisterResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382E27B383AF00C60D73 /* UnregisterResponse.swift */; }; FDC4383127B3841C00C60D73 /* RegisterResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383027B3841C00C60D73 /* RegisterResponse.swift */; }; FDC4383827B3863200C60D73 /* VersionResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383727B3863200C60D73 /* VersionResponse.swift */; }; - FDC4383A27B4696200C60D73 /* LegacyAuthTokenResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383927B4696200C60D73 /* LegacyAuthTokenResponse.swift */; }; FDC4383E27B4708600C60D73 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383D27B4708600C60D73 /* Atomic.swift */; }; - FDC4384027B4746D00C60D73 /* LegacyGetInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383F27B4746D00C60D73 /* LegacyGetInfoResponse.swift */; }; - FDC4384727B47F4D00C60D73 /* LegacyOpenGroupMessageV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384327B47F4D00C60D73 /* LegacyOpenGroupMessageV2.swift */; }; - FDC4384827B47F4D00C60D73 /* LegacyRoomInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384427B47F4D00C60D73 /* LegacyRoomInfo.swift */; }; - FDC4384927B47F4D00C60D73 /* LegacyCompactPollResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384527B47F4D00C60D73 /* LegacyCompactPollResponse.swift */; }; - FDC4384A27B47F4D00C60D73 /* LegacyDeletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384627B47F4D00C60D73 /* LegacyDeletion.swift */; }; FDC4384C27B47F7700C60D73 /* OpenGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384B27B47F7700C60D73 /* OpenGroup.swift */; }; FDC4384F27B4804F00C60D73 /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384E27B4804F00C60D73 /* Header.swift */; }; FDC4385127B4807400C60D73 /* QueryParam.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385027B4807400C60D73 /* QueryParam.swift */; }; @@ -1960,23 +1948,11 @@ FDC4380827B31D4E00C60D73 /* SOGSError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOGSError.swift; sourceTree = ""; }; FDC4381427B329CE00C60D73 /* NonceGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonceGenerator.swift; sourceTree = ""; }; FDC4381627B32EC700C60D73 /* Personalization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Personalization.swift; sourceTree = ""; }; - FDC4381927B34EBA00C60D73 /* LegacyCompactPollBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyCompactPollBody.swift; sourceTree = ""; }; - FDC4381B27B354AC00C60D73 /* LegacyPublicKeyBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyPublicKeyBody.swift; sourceTree = ""; }; FDC4381F27B36ADC00C60D73 /* SOGSEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOGSEndpoint.swift; sourceTree = ""; }; - FDC4382527B37F6900C60D73 /* LegacyDeletedMessagesResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyDeletedMessagesResponse.swift; sourceTree = ""; }; - FDC4382727B37FD300C60D73 /* LegacyModeratorsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyModeratorsResponse.swift; sourceTree = ""; }; - FDC4382927B3802D00C60D73 /* LegacyRoomsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyRoomsResponse.swift; sourceTree = ""; }; - FDC4382B27B380E300C60D73 /* LegacyMemberCountResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyMemberCountResponse.swift; sourceTree = ""; }; FDC4382E27B383AF00C60D73 /* UnregisterResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnregisterResponse.swift; sourceTree = ""; }; FDC4383027B3841C00C60D73 /* RegisterResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterResponse.swift; sourceTree = ""; }; FDC4383727B3863200C60D73 /* VersionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionResponse.swift; sourceTree = ""; }; - FDC4383927B4696200C60D73 /* LegacyAuthTokenResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyAuthTokenResponse.swift; sourceTree = ""; }; FDC4383D27B4708600C60D73 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; - FDC4383F27B4746D00C60D73 /* LegacyGetInfoResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyGetInfoResponse.swift; sourceTree = ""; }; - FDC4384327B47F4D00C60D73 /* LegacyOpenGroupMessageV2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyOpenGroupMessageV2.swift; sourceTree = ""; }; - FDC4384427B47F4D00C60D73 /* LegacyRoomInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyRoomInfo.swift; sourceTree = ""; }; - FDC4384527B47F4D00C60D73 /* LegacyCompactPollResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyCompactPollResponse.swift; sourceTree = ""; }; - FDC4384627B47F4D00C60D73 /* LegacyDeletion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyDeletion.swift; sourceTree = ""; }; FDC4384B27B47F7700C60D73 /* OpenGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGroup.swift; sourceTree = ""; }; FDC4384E27B4804F00C60D73 /* Header.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = ""; }; FDC4385027B4807400C60D73 /* QueryParam.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryParam.swift; sourceTree = ""; }; @@ -3948,18 +3924,6 @@ FDC438A927BB12BB00C60D73 /* UserModeratorRequest.swift */, FDC438AB27BB145200C60D73 /* UserDeleteMessagesRequest.swift */, FDC438AD27BB148700C60D73 /* UserDeleteMessagesResponse.swift */, - FDC4381B27B354AC00C60D73 /* LegacyPublicKeyBody.swift */, - FDC4383927B4696200C60D73 /* LegacyAuthTokenResponse.swift */, - FDC4381927B34EBA00C60D73 /* LegacyCompactPollBody.swift */, - FDC4384527B47F4D00C60D73 /* LegacyCompactPollResponse.swift */, - FDC4383F27B4746D00C60D73 /* LegacyGetInfoResponse.swift */, - FDC4382927B3802D00C60D73 /* LegacyRoomsResponse.swift */, - FDC4384427B47F4D00C60D73 /* LegacyRoomInfo.swift */, - FDC4382B27B380E300C60D73 /* LegacyMemberCountResponse.swift */, - FDC4382727B37FD300C60D73 /* LegacyModeratorsResponse.swift */, - FDC4382527B37F6900C60D73 /* LegacyDeletedMessagesResponse.swift */, - FDC4384627B47F4D00C60D73 /* LegacyDeletion.swift */, - FDC4384327B47F4D00C60D73 /* LegacyOpenGroupMessageV2.swift */, ); path = Models; sourceTree = ""; @@ -5256,7 +5220,6 @@ buildActionMask = 2147483647; files = ( B8856D08256F10F1001CE70E /* DeviceSleepManager.swift in Sources */, - FDC4382A27B3802D00C60D73 /* LegacyRoomsResponse.swift in Sources */, C3471F4C25553AB000297E91 /* MessageReceiver+Decryption.swift in Sources */, C300A5D32554B05A00555489 /* TypingIndicator.swift in Sources */, C3A3A156256E1B91004D228D /* ProtoUtils.m in Sources */, @@ -5273,7 +5236,6 @@ FDC4385F27B4C4A200C60D73 /* PinnedMessage.swift in Sources */, C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */, FD859EF227BF6BA200510D0C /* Data+Utilities.swift in Sources */, - FDC4384927B47F4D00C60D73 /* LegacyCompactPollResponse.swift in Sources */, C352A3932557883D00338F3E /* JobDelegate.swift in Sources */, C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */, FDC4386527B4DE7600C60D73 /* RoomPollInfo.swift in Sources */, @@ -5283,7 +5245,6 @@ C3D9E3BF25676AD70040E4F3 /* TSAttachmentStream.m in Sources */, C3C2A7562553A3AB00C340D1 /* VisibleMessage+Quote.swift in Sources */, B8B32021258B1A650020074B /* Contact.swift in Sources */, - FDC4384027B4746D00C60D73 /* LegacyGetInfoResponse.swift in Sources */, C32C5C89256DD0D2003C73A2 /* Storage+Jobs.swift in Sources */, C300A5FC2554B0A000555489 /* MessageReceiver.swift in Sources */, FDC438AC27BB145200C60D73 /* UserDeleteMessagesRequest.swift in Sources */, @@ -5298,7 +5259,6 @@ FDC4386727B4E10E00C60D73 /* Capabilities.swift in Sources */, C32C59C1256DB41F003C73A2 /* TSGroupThread.m in Sources */, C3A3A08F256E1728004D228D /* FullTextSearchFinder.swift in Sources */, - FDC4381A27B34EBA00C60D73 /* LegacyCompactPollBody.swift in Sources */, B8856D1A256F114D001CE70E /* ProximityMonitoringManager.swift in Sources */, C32C5B9F256DC739003C73A2 /* OWSBlockingManager.m in Sources */, C3D9E52725677DF20040E4F3 /* OWSThumbnailService.swift in Sources */, @@ -5323,13 +5283,10 @@ B8F5F60325EDE16F003BF8D4 /* DataExtractionNotification.swift in Sources */, C32C5D24256DD4C0003C73A2 /* MentionsManager.swift in Sources */, C3A71D1E25589AC30043A11F /* WebSocketProto.swift in Sources */, - FDC4384A27B47F4D00C60D73 /* LegacyDeletion.swift in Sources */, C3C2A7852553AAF300C340D1 /* SessionProtos.pb.swift in Sources */, B8566C63256F55930045A0B9 /* OWSLinkPreview+Conversion.swift in Sources */, - FDC4382827B37FD300C60D73 /* LegacyModeratorsResponse.swift in Sources */, C32C5B3F256DC1DF003C73A2 /* TSQuotedMessage+Conversion.swift in Sources */, B8EB20EE2640F28000773E52 /* VisibleMessage+OpenGroupInvitation.swift in Sources */, - FDC4381C27B354AC00C60D73 /* LegacyPublicKeyBody.swift in Sources */, FDC4382F27B383AF00C60D73 /* UnregisterResponse.swift in Sources */, FDC4386327B4D94E00C60D73 /* SOGSMessage.swift in Sources */, C3C2A7712553A41E00C340D1 /* ControlMessage.swift in Sources */, @@ -5344,7 +5301,6 @@ C32C5D9C256DD6DC003C73A2 /* OWSOutgoingReceiptManager.m in Sources */, B8AE760B25ABFB5A001A84D2 /* GeneralUtilities.m in Sources */, C32C5C4F256DCC36003C73A2 /* Storage+OpenGroups.swift in Sources */, - FDC4384727B47F4D00C60D73 /* LegacyOpenGroupMessageV2.swift in Sources */, FDC4384F27B4804F00C60D73 /* Header.swift in Sources */, C3DA9C0725AE7396008F7C7E /* ConfigurationMessage.swift in Sources */, B8856CEE256F1054001CE70E /* OWSAudioPlayer.m in Sources */, @@ -5355,7 +5311,6 @@ FDC4381527B329CE00C60D73 /* NonceGenerator.swift in Sources */, C3BBE0762554CDA60050F1E3 /* Configuration.swift in Sources */, C35D76DB26606304009AA5FB /* ThreadUpdateBatcher.swift in Sources */, - FDC4382C27B380E300C60D73 /* LegacyMemberCountResponse.swift in Sources */, B8B32033258B235D0020074B /* Storage+Contacts.swift in Sources */, B8856D69256F141F001CE70E /* OWSWindowManager.m in Sources */, FD83B9AA27CF149D005E1583 /* ContactUtilities.swift in Sources */, @@ -5376,7 +5331,6 @@ C32C5BEF256DC8EE003C73A2 /* OWSDisappearingMessagesJob.m in Sources */, C34A977425A3E34A00852C71 /* ClosedGroupControlMessage.swift in Sources */, FDC4384C27B47F7700C60D73 /* OpenGroup.swift in Sources */, - FDC4382627B37F6900C60D73 /* LegacyDeletedMessagesResponse.swift in Sources */, B88FA7B826045D100049422F /* OpenGroupAPI.swift in Sources */, C32C5E97256DE0CB003C73A2 /* OWSPrimaryStorage.m in Sources */, FDC438C927BB706500C60D73 /* SendDirectMessageRequest.swift in Sources */, @@ -5385,7 +5339,6 @@ B8856E94256F1C37001CE70E /* OWSSounds.m in Sources */, C32C5BCC256DC830003C73A2 /* Storage+ClosedGroups.swift in Sources */, C3A3A0EC256E1949004D228D /* OWSRecipientIdentity.m in Sources */, - FDC4383A27B4696200C60D73 /* LegacyAuthTokenResponse.swift in Sources */, B8F5F56525EC8453003BF8D4 /* Notification+Contacts.swift in Sources */, C32C5AB2256DBE8F003C73A2 /* TSMessage.m in Sources */, C3A3A0FE256E1A3C004D228D /* TSDatabaseSecondaryIndexes.m in Sources */, @@ -5397,7 +5350,6 @@ B88A1AC725C90A4700E6D421 /* TypingIndicatorInteraction.swift in Sources */, C3D9E3C025676AD70040E4F3 /* TSAttachment.m in Sources */, FD83B9CE27D17A04005E1583 /* Request.swift in Sources */, - FDC4384827B47F4D00C60D73 /* LegacyRoomInfo.swift in Sources */, C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */, FDC438CB27BB7DB100C60D73 /* UpdateMessageRequest.swift in Sources */, C352A2FF25574B6300338F3E /* MessageSendJob.swift in Sources */, diff --git a/SessionMessagingKit/Database/Storage+Contacts.swift b/SessionMessagingKit/Database/Storage+Contacts.swift index 285285069..a6b1680f6 100644 --- a/SessionMessagingKit/Database/Storage+Contacts.swift +++ b/SessionMessagingKit/Database/Storage+Contacts.swift @@ -97,11 +97,11 @@ extension Storage { public func enumerateBlindedIdMapping(with block: @escaping (BlindedIdMapping, UnsafeMutablePointer) -> ()) { Storage.read { transaction in - self.enumerateBlindedIdMapping(with: block, transaction: transaction) + self.enumerateBlindedIdMapping(using: transaction, with: block) } } - public func enumerateBlindedIdMapping(with block: @escaping (BlindedIdMapping, UnsafeMutablePointer) -> (), transaction: YapDatabaseReadTransaction) { + public func enumerateBlindedIdMapping(using transaction: YapDatabaseReadTransaction, with block: @escaping (BlindedIdMapping, UnsafeMutablePointer) -> ()) { transaction.enumerateRows(inCollection: Storage.blindedIdCacheCollection) { _, object, _, stop in guard let mapping = object as? BlindedIdMapping else { return } diff --git a/SessionMessagingKit/Messages/Signal/TSIncomingMessage.h b/SessionMessagingKit/Messages/Signal/TSIncomingMessage.h index b4a0edb0e..c965ff51b 100644 --- a/SessionMessagingKit/Messages/Signal/TSIncomingMessage.h +++ b/SessionMessagingKit/Messages/Signal/TSIncomingMessage.h @@ -85,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSString *authorId; // convenience method for expiring a message which was just read -- (void)markAsReadNowWithSendReadReceipt:(BOOL)sendReadReceipt +- (void)markAsReadNowWithTrySendReadReceipt:(BOOL)trySendReadReceipt transaction:(YapDatabaseReadWriteTransaction *)transaction; - (void)setNotificationIdentifier:(NSString * _Nullable)notificationIdentifier diff --git a/SessionMessagingKit/Open Groups/Models/LegacyAuthTokenResponse.swift b/SessionMessagingKit/Open Groups/Models/LegacyAuthTokenResponse.swift deleted file mode 100644 index d15f21cc8..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyAuthTokenResponse.swift +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - struct LegacyAuthTokenResponse: Codable { - struct Challenge: Codable { - enum CodingKeys: String, CodingKey { - case ciphertext = "ciphertext" - case ephemeralPublicKey = "ephemeral_public_key" - } - - let ciphertext: Data - let ephemeralPublicKey: Data - } - - let challenge: Challenge - } -} - -// MARK: - Codable - -extension OpenGroupAPI.LegacyAuthTokenResponse.Challenge { - init(from decoder: Decoder) throws { - let container: KeyedDecodingContainer = try decoder.container(keyedBy: CodingKeys.self) - - let base64EncodedCiphertext: String = try container.decode(String.self, forKey: .ciphertext) - let base64EncodedEphemeralPublicKey: String = try container.decode(String.self, forKey: .ephemeralPublicKey) - - guard let ciphertext = Data(base64Encoded: base64EncodedCiphertext), let ephemeralPublicKey = Data(base64Encoded: base64EncodedEphemeralPublicKey) else { - throw HTTP.Error.parsingFailed - } - - self = OpenGroupAPI.LegacyAuthTokenResponse.Challenge( - ciphertext: ciphertext, - ephemeralPublicKey: ephemeralPublicKey - ) - } - - public func encode(to encoder: Encoder) throws { - var container: KeyedEncodingContainer = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(ciphertext.base64EncodedString(), forKey: .ciphertext) - try container.encode(ephemeralPublicKey.base64EncodedString(), forKey: .ephemeralPublicKey) - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyCompactPollBody.swift b/SessionMessagingKit/Open Groups/Models/LegacyCompactPollBody.swift deleted file mode 100644 index 70036c1e2..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyCompactPollBody.swift +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - struct LegacyCompactPollBody: Codable { - struct Room: Codable { - enum CodingKeys: String, CodingKey { - case id = "room_id" - case fromMessageServerId = "from_message_server_id" - case fromDeletionServerId = "from_deletion_server_id" - - // TODO: Remove this legacy value - case legacyAuthToken = "auth_token" - } - - let id: String - let fromMessageServerId: Int64? - let fromDeletionServerId: Int64? - - // TODO: This is a legacy value - let legacyAuthToken: String? - } - - let requests: [Room] - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyCompactPollResponse.swift b/SessionMessagingKit/Open Groups/Models/LegacyCompactPollResponse.swift deleted file mode 100644 index f699b3206..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyCompactPollResponse.swift +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - public struct LegacyCompactPollResponse: Codable { - public struct Result: Codable { - enum CodingKeys: String, CodingKey { - case room = "room_id" - case statusCode = "status_code" - case messages - case deletions - case moderators - } - - public let room: String - public let statusCode: UInt - public let messages: [LegacyOpenGroupMessageV2]? - public let deletions: [LegacyDeletion]? - public let moderators: [String]? - } - - public let results: [Result] - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyDeletedMessagesResponse.swift b/SessionMessagingKit/Open Groups/Models/LegacyDeletedMessagesResponse.swift deleted file mode 100644 index f063cc08c..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyDeletedMessagesResponse.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - struct LegacyDeletedMessagesResponse: Codable { - enum CodingKeys: String, CodingKey { - case deletions = "ids" - } - - let deletions: [LegacyDeletion] - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyDeletion.swift b/SessionMessagingKit/Open Groups/Models/LegacyDeletion.swift deleted file mode 100644 index 03fc1ae0a..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyDeletion.swift +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - public struct LegacyDeletion: Codable { - enum CodingKeys: String, CodingKey { - case id - case deletedMessageID = "deleted_message_id" - } - - let id: Int64 - let deletedMessageID: Int64 - - public static func from(_ json: JSON) -> LegacyDeletion? { - guard let id = json["id"] as? Int64, let deletedMessageID = json["deleted_message_id"] as? Int64 else { - return nil - } - - return LegacyDeletion(id: id, deletedMessageID: deletedMessageID) - } - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyGetInfoResponse.swift b/SessionMessagingKit/Open Groups/Models/LegacyGetInfoResponse.swift deleted file mode 100644 index b3e4317f3..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyGetInfoResponse.swift +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - struct LegacyGetInfoResponse: Codable { - let room: LegacyRoomInfo - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyMemberCountResponse.swift b/SessionMessagingKit/Open Groups/Models/LegacyMemberCountResponse.swift deleted file mode 100644 index 1f7c13cfb..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyMemberCountResponse.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - struct LegacyMemberCountResponse: Codable { - enum CodingKeys: String, CodingKey { - case memberCount = "member_count" - } - - let memberCount: UInt64 - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyModeratorsResponse.swift b/SessionMessagingKit/Open Groups/Models/LegacyModeratorsResponse.swift deleted file mode 100644 index 4846a5faf..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyModeratorsResponse.swift +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - struct LegacyModeratorsResponse: Codable { - let moderators: [String] - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyOpenGroupMessageV2.swift b/SessionMessagingKit/Open Groups/Models/LegacyOpenGroupMessageV2.swift deleted file mode 100644 index 17b0d5565..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyOpenGroupMessageV2.swift +++ /dev/null @@ -1,71 +0,0 @@ -import Foundation -import SessionUtilitiesKit - -public struct LegacyOpenGroupMessageV2: Codable { - enum CodingKeys: String, CodingKey { - case serverID = "server_id" - case sender = "public_key" - case sentTimestamp = "timestamp" - case base64EncodedData = "data" - case base64EncodedSignature = "signature" - } - - public let serverID: Int64? - public let sender: String? - public let sentTimestamp: UInt64 - /// The serialized protobuf in base64 encoding. - public let base64EncodedData: String - /// When sending a message, the sender signs the serialized protobuf with their private key so that - /// a receiving user can verify that the message wasn't tampered with. - public let base64EncodedSignature: String? - - public func sign(with publicKey: String) -> LegacyOpenGroupMessageV2? { - guard let userKeyPair = SNMessagingKitConfiguration.shared.storage.getUserKeyPair() else { return nil } - guard let data = Data(base64Encoded: base64EncodedData) else { return nil } - guard let signature = try? Ed25519.sign(data, with: userKeyPair) else { - SNLog("Failed to sign open group message.") - return nil - } - - return LegacyOpenGroupMessageV2( - serverID: serverID, - sender: sender, - sentTimestamp: sentTimestamp, - base64EncodedData: base64EncodedData, - base64EncodedSignature: signature.base64EncodedString() - ) - } -} - -// MARK: - Decoder - -extension LegacyOpenGroupMessageV2 { - public init(from decoder: Decoder) throws { - let container: KeyedDecodingContainer = try decoder.container(keyedBy: CodingKeys.self) - - let sender: String = try container.decode(String.self, forKey: .sender) - let base64EncodedData: String = try container.decode(String.self, forKey: .base64EncodedData) - let base64EncodedSignature: String = try container.decode(String.self, forKey: .base64EncodedSignature) - - // Validate the message signature - guard let data = Data(base64Encoded: base64EncodedData), let signature = Data(base64Encoded: base64EncodedSignature) else { - throw HTTP.Error.parsingFailed - } - - let publicKey = Data(hex: sender.removingIdPrefixIfNeeded()) - let isValid = (try? Ed25519.verifySignature(signature, publicKey: publicKey, data: data)) ?? false - - guard isValid else { - SNLog("Ignoring message with invalid signature.") - throw HTTP.Error.parsingFailed - } - - self = LegacyOpenGroupMessageV2( - serverID: try? container.decode(Int64.self, forKey: .serverID), - sender: sender, - sentTimestamp: try container.decode(UInt64.self, forKey: .sentTimestamp), - base64EncodedData: base64EncodedData, - base64EncodedSignature: base64EncodedSignature - ) - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyPublicKeyBody.swift b/SessionMessagingKit/Open Groups/Models/LegacyPublicKeyBody.swift deleted file mode 100644 index 572bdbdbf..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyPublicKeyBody.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - struct LegacyPublicKeyBody: Codable { - enum CodingKeys: String, CodingKey { - case publicKey = "public_key" - } - - let publicKey: String - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyRoomInfo.swift b/SessionMessagingKit/Open Groups/Models/LegacyRoomInfo.swift deleted file mode 100644 index bccd9ea93..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyRoomInfo.swift +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - public struct LegacyRoomInfo: Codable { - enum CodingKeys: String, CodingKey { - case id - case name - case imageID = "image_id" - } - - public let id: String - public let name: String - public let imageID: String? - } -} diff --git a/SessionMessagingKit/Open Groups/Models/LegacyRoomsResponse.swift b/SessionMessagingKit/Open Groups/Models/LegacyRoomsResponse.swift deleted file mode 100644 index 162e629fc..000000000 --- a/SessionMessagingKit/Open Groups/Models/LegacyRoomsResponse.swift +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import Foundation - -extension OpenGroupAPI { - struct LegacyRoomsResponse: Codable { - let rooms: [LegacyRoomInfo] - } -} diff --git a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift index 8ac37bc7f..7ac3f7e94 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift @@ -1036,7 +1036,6 @@ public final class OpenGroupAPI: NSObject { return Promise(error: Error.signingFailed) } - // TODO: 'removeAuthToken' as a migration??? (would previously do this when getting a `401`) return dependencies.api.sendOnionRequest(signedRequest, to: request.server, with: publicKey) } @@ -1045,640 +1044,4 @@ public final class OpenGroupAPI: NSObject { preconditionFailure("It's currently not allowed to send non onion routed requests.") } - - // MARK: - - // MARK: - - // MARK: - Legacy Requests (To be removed) - // TODO: Remove the legacy requests (should be unused once we release - just here for testing) - - public static var legacyDefaultRoomsPromise: Promise<[LegacyRoomInfo]>? - - // MARK: -- Legacy Auth - - @available(*, deprecated, message: "Use request signing instead") - private static func legacyGetAuthToken(for room: String, on server: String) -> Promise { - let storage = SNMessagingKitConfiguration.shared.storage - - if let authToken: String = storage.getAuthToken(for: room, on: server) { - return Promise.value(authToken) - } - - if let authTokenPromise: Promise = legacyAuthTokenPromises.wrappedValue["\(server).\(room)"] { - return authTokenPromise - } - - let promise: Promise = legacyRequestNewAuthToken(for: room, on: server) - .then(on: OpenGroupAPI.workQueue) { legacyClaimAuthToken($0, for: room, on: server) } - .then(on: OpenGroupAPI.workQueue) { authToken -> Promise in - let (promise, seal) = Promise.pending() - storage.write(with: { transaction in - storage.setAuthToken(for: room, on: server, to: authToken, using: transaction) - }, completion: { - seal.fulfill(authToken) - }) - return promise - } - - promise - .done(on: OpenGroupAPI.workQueue) { _ in - legacyAuthTokenPromises.wrappedValue["\(server).\(room)"] = nil - } - .catch(on: OpenGroupAPI.workQueue) { _ in - legacyAuthTokenPromises.wrappedValue["\(server).\(room)"] = nil - } - - legacyAuthTokenPromises.wrappedValue["\(server).\(room)"] = promise - return promise - } - - @available(*, deprecated, message: "Use request signing instead") - public static func legacyRequestNewAuthToken(for room: String, on server: String) -> Promise { - SNLog("Requesting auth token for server: \(server).") - guard let userKeyPair: ECKeyPair = SNMessagingKitConfiguration.shared.storage.getUserKeyPair() else { - return Promise(error: Error.generic) - } - - let request: Request = Request( - server: server, - room: room, - endpoint: .legacyAuthTokenChallenge(legacyAuth: true), - queryParameters: [ - .publicKey: getUserHexEncodedPublicKey() - ], - isAuthRequired: false - ) - - return legacySend(request).map(on: OpenGroupAPI.workQueue) { _, maybeData in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let response = try data.decoded(as: LegacyAuthTokenResponse.self, customError: Error.parsingFailed) - let symmetricKey = try AESGCM.generateSymmetricKey(x25519PublicKey: response.challenge.ephemeralPublicKey, x25519PrivateKey: userKeyPair.privateKey) - - guard let tokenAsData = try? AESGCM.decrypt(response.challenge.ciphertext, with: symmetricKey) else { - throw Error.decryptionFailed - } - - return tokenAsData.toHexString() - } - } - - @available(*, deprecated, message: "Use request signing instead") - public static func legacyClaimAuthToken(_ authToken: String, for room: String, on server: String) -> Promise { - let requestBody: LegacyPublicKeyBody = LegacyPublicKeyBody(publicKey: getUserHexEncodedPublicKey()) - - guard let body: Data = try? JSONEncoder().encode(requestBody) else { - return Promise(error: HTTP.Error.invalidJSON) - } - - let request: Request = Request( - method: .post, - server: server, - room: room, - endpoint: .legacyAuthTokenClaim(legacyAuth: true), - headers: [ - // Set explicitly here because is isn't in the database yet at this point - .authorization: authToken - ], - body: body, - isAuthRequired: false - ) - - return legacySend(request).map(on: OpenGroupAPI.workQueue) { _ in authToken } - } - - /// Should be called when leaving a group. - @available(*, deprecated, message: "Use request signing instead") - public static func legacyDeleteAuthToken(for room: String, on server: String) -> Promise { - let request: Request = Request( - method: .delete, - server: server, - room: room, - endpoint: .legacyAuthToken(legacyAuth: true) - ) - - return legacySend(request).map(on: OpenGroupAPI.workQueue) { _ in - let storage = SNMessagingKitConfiguration.shared.storage - - storage.write { transaction in - storage.removeAuthToken(for: room, on: server, using: transaction) - } - } - } - - // MARK: -- Legacy Requests - - @available(*, deprecated, message: "Use poll or batch instead") - public static func legacyCompactPoll(_ server: String) -> Promise { - let storage: SessionMessagingKitStorageProtocol = SNMessagingKitConfiguration.shared.storage - let rooms: [String] = storage.getAllOpenGroups().values - .filter { $0.server == server } - .map { $0.room } - var getAuthTokenPromises: [String: Promise] = [:] - let useMessageLimit = (hasPerformedInitialPoll[server] != true && timeSinceLastOpen > OpenGroupAPI.Poller.maxInactivityPeriod) - - hasPerformedInitialPoll[server] = true - - if !legacyHasUpdatedLastOpenDate { - UserDefaults.standard[.lastOpen] = Date() - legacyHasUpdatedLastOpenDate = true - } - - for room in rooms { - getAuthTokenPromises[room] = legacyGetAuthToken(for: room, on: server) - } - - let requestBody: LegacyCompactPollBody = LegacyCompactPollBody( - requests: rooms - .map { roomId -> LegacyCompactPollBody.Room in - LegacyCompactPollBody.Room( - id: roomId, - fromMessageServerId: (useMessageLimit ? nil : - storage.getLastMessageServerID(for: roomId, on: server) - ), - fromDeletionServerId: (useMessageLimit ? nil : - storage.getLastDeletionServerID(for: roomId, on: server) - ), - legacyAuthToken: nil - ) - } - ) - - return when(fulfilled: [Promise](getAuthTokenPromises.values)) - .then(on: OpenGroupAPI.workQueue) { _ -> Promise in - let requestBodyWithAuthTokens: LegacyCompactPollBody = LegacyCompactPollBody( - requests: requestBody.requests.compactMap { oldRoom -> LegacyCompactPollBody.Room? in - guard let authToken: String = getAuthTokenPromises[oldRoom.id]?.value else { return nil } - - return LegacyCompactPollBody.Room( - id: oldRoom.id, - fromMessageServerId: oldRoom.fromMessageServerId, - fromDeletionServerId: oldRoom.fromDeletionServerId, - legacyAuthToken: authToken - ) - } - ) - - guard let body: Data = try? JSONEncoder().encode(requestBodyWithAuthTokens) else { - return Promise(error: HTTP.Error.invalidJSON) - } - - let request = Request( - method: .post, - server: server, - endpoint: .legacyCompactPoll(legacyAuth: true), - body: body, - isAuthRequired: false - ) - - return legacySend(request) - .then(on: OpenGroupAPI.workQueue) { _, maybeData -> Promise in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let response: LegacyCompactPollResponse = try data.decoded(as: LegacyCompactPollResponse.self, customError: Error.parsingFailed) - - return when( - fulfilled: response.results - .compactMap { (result: LegacyCompactPollResponse.Result) -> Promise<[LegacyDeletion]>? in - // A 401 means that we didn't provide a (valid) auth token for a route that - // required one. We use this as an indication that the token we're using has - // expired. Note that a 403 has a different meaning; it means that we provided - // a valid token but it doesn't have a high enough permission level for the - // route in question. - guard result.statusCode != 401 else { - storage.writeSync { transaction in - storage.removeAuthToken(for: result.room, on: server, using: transaction) - } - - return nil - } - - return legacyProcess(messages: result.messages, for: result.room, on: server) - .then(on: OpenGroupAPI.workQueue) { _ -> Promise<[LegacyDeletion]> in - legacyProcess(deletions: result.deletions, for: result.room, on: server) - } - } - ).then(on: OpenGroupAPI.workQueue) { _ in Promise.value(response) } - } - } - } - - @available(*, deprecated, message: "Use getDefaultRoomsIfNeeded instead") - public static func legacyGetDefaultRoomsIfNeeded() { - Storage.shared.write( - with: { transaction in - Storage.shared.setOpenGroupPublicKey(for: defaultServer, to: defaultServerPublicKey, using: transaction) - }, - completion: { - let promise = attempt(maxRetryCount: 8, recoveringOn: DispatchQueue.main) { - OpenGroupAPI.legacyGetAllRooms(from: defaultServer) - } - _ = promise.done(on: OpenGroupAPI.workQueue) { items in - items.forEach { legacyGetGroupImage(for: $0.id, on: defaultServer).retainUntilComplete() } - } - promise.catch(on: OpenGroupAPI.workQueue) { _ in - OpenGroupAPI.legacyDefaultRoomsPromise = nil - } - legacyDefaultRoomsPromise = promise - } - ) - } - - @available(*, deprecated, message: "Use rooms(for:) instead") - public static func legacyGetAllRooms(from server: String) -> Promise<[LegacyRoomInfo]> { - let request: Request = Request( - server: server, - endpoint: .legacyRooms, - isAuthRequired: false - ) - - return legacySend(request) - .map(on: OpenGroupAPI.workQueue) { _, maybeData in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let response: LegacyRoomsResponse = try data.decoded(as: LegacyRoomsResponse.self, customError: Error.parsingFailed) - - return response.rooms - } - } - - @available(*, deprecated, message: "Use room(for:on:) instead") - public static func legacyGetRoomInfo(for room: String, on server: String) -> Promise { - let request: Request = Request( - server: server, - room: room, - endpoint: .legacyRoomInfo(room), - isAuthRequired: false - ) - - return legacySend(request) - .map(on: OpenGroupAPI.workQueue) { _, maybeData in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let response: LegacyGetInfoResponse = try data.decoded(as: LegacyGetInfoResponse.self, customError: Error.parsingFailed) - - return response.room - } - } - - @available(*, deprecated, message: "Use roomImage(_:for:on:) instead") - public static func legacyGetGroupImage(for room: String, on server: String) -> Promise { - // Normally the image for a given group is stored with the group thread, so it's only - // fetched once. However, on the join open group screen we show images for groups the - // user * hasn't * joined yet. We don't want to re-fetch these images every time the - // user opens the app because that could slow the app down or be data-intensive. So - // instead we assume that these images don't change that often and just fetch them once - // a week. We also assume that they're all fetched at the same time as well, so that - // we only need to maintain one date in user defaults. On top of all of this we also - // don't double up on fetch requests by storing the existing request as a promise if - // there is one. - let lastOpenGroupImageUpdate: Date? = UserDefaults.standard[.lastOpenGroupImageUpdate] - let now: Date = Date() - let timeSinceLastUpdate: TimeInterval = (given(lastOpenGroupImageUpdate) { now.timeIntervalSince($0) } ?? .greatestFiniteMagnitude) - let updateInterval: TimeInterval = (7 * 24 * 60 * 60) - - if let data = Storage.shared.getOpenGroupImage(for: room, on: server), server == defaultServer, timeSinceLastUpdate < updateInterval { - return Promise.value(data) - } - - if let promise = legacyGroupImagePromises["\(server).\(room)"] { - return promise - } - - let request: Request = Request( - server: server, - room: room, - endpoint: .legacyRoomImage(room), - isAuthRequired: false - ) - - let promise: Promise = legacySend(request).map(on: OpenGroupAPI.workQueue) { _, maybeData in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let response: LegacyFileDownloadResponse = try data.decoded(as: LegacyFileDownloadResponse.self, customError: Error.parsingFailed) - - if server == defaultServer { - Storage.shared.write { transaction in - Storage.shared.setOpenGroupImage(to: response.data, for: room, on: server, using: transaction) - } - UserDefaults.standard[.lastOpenGroupImageUpdate] = now - } - - return response.data - } - legacyGroupImagePromises["\(server).\(room)"] = promise - - return promise - } - - @available(*, deprecated, message: "Use room(for:on:) instead") - public static func legacyGetMemberCount(for room: String, on server: String) -> Promise { - let request: Request = Request( - server: server, - room: room, - endpoint: .legacyMemberCount(legacyAuth: true) - ) - - return legacySend(request) - .map(on: OpenGroupAPI.workQueue) { _, maybeData in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let response: LegacyMemberCountResponse = try data.decoded(as: LegacyMemberCountResponse.self, customError: Error.parsingFailed) - - let storage = SNMessagingKitConfiguration.shared.storage - storage.write { transaction in - storage.setUserCount(to: response.memberCount, forOpenGroupWithID: "\(server).\(room)", using: transaction) - } - - return response.memberCount - } - } - - // MARK: - Legacy File Storage - - @available(*, deprecated, message: "Use uploadFile(_:fileName:to:on:) instead") - public static func legacyUpload(_ file: Data, to room: String, on server: String) -> Promise { - let requestBody: FileUploadBody = FileUploadBody(file: file.base64EncodedString()) - - guard let body: Data = try? JSONEncoder().encode(requestBody) else { - return Promise(error: HTTP.Error.invalidJSON) - } - - let request = Request(method: .post, server: server, room: room, endpoint: .legacyFiles, body: body) - - return legacySend(request).map(on: OpenGroupAPI.workQueue) { _, maybeData in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let response: LegacyFileUploadResponse = try data.decoded(as: LegacyFileUploadResponse.self, customError: Error.parsingFailed) - - return response.fileId - } - } - - @available(*, deprecated, message: "Use downloadFile(_:from:on:) instead") - public static func legacyDownload(_ file: UInt64, from room: String, on server: String) -> Promise { - let request = Request(server: server, room: room, endpoint: .legacyFile(file)) - - return legacySend(request).map(on: OpenGroupAPI.workQueue) { _, maybeData in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let response: LegacyFileDownloadResponse = try data.decoded(as: LegacyFileDownloadResponse.self, customError: Error.parsingFailed) - - return response.data - } - } - - // MARK: - Legacy Message Sending & Receiving - - @available(*, deprecated, message: "Use send(_:to:on:whisperTo:whisperMods:with:) instead") - public static func legacySend(_ message: LegacyOpenGroupMessageV2, to room: String, on server: String, with publicKey: String) -> Promise { - guard let signedMessage = message.sign(with: publicKey) else { return Promise(error: Error.signingFailed) } - guard let body: Data = try? JSONEncoder().encode(signedMessage) else { - return Promise(error: Error.parsingFailed) - } - let request = Request(method: .post, server: server, room: room, endpoint: .legacyMessages, body: body) - - return legacySend(request).map(on: OpenGroupAPI.workQueue) { _, maybeData in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let message: LegacyOpenGroupMessageV2 = try data.decoded(as: LegacyOpenGroupMessageV2.self, customError: Error.parsingFailed) - Storage.shared.write { transaction in - Storage.shared.addReceivedMessageTimestamp(message.sentTimestamp, using: transaction) - } - return message - } - } - - @available(*, deprecated, message: "Use recentMessages(in:on:) or messagesSince(seqNo:in:on:) instead") - public static func legacyGetMessages(for room: String, on server: String) -> Promise<[LegacyOpenGroupMessageV2]> { - let storage = SNMessagingKitConfiguration.shared.storage - let request: Request = Request( - server: server, - room: room, - endpoint: .legacyMessages, - queryParameters: [ - .fromServerId: storage.getLastMessageServerID(for: room, on: server).map { String($0) } - ].compactMapValues { $0 } - ) - - return legacySend(request).then(on: OpenGroupAPI.workQueue) { _, maybeData -> Promise<[LegacyOpenGroupMessageV2]> in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let messages: [LegacyOpenGroupMessageV2] = try data.decoded(as: [LegacyOpenGroupMessageV2].self, customError: Error.parsingFailed) - - return legacyProcess(messages: messages, for: room, on: server) - } - } - - // MARK: - Legacy Message Deletion - - // TODO: No delete method????. - @available(*, deprecated, message: "Use v4 endpoint instead") - public static func legacyDeleteMessage(with serverID: Int64, from room: String, on server: String) -> Promise { - let request: Request = Request( - method: .delete, - server: server, - room: room, - endpoint: .legacyMessagesForServer(serverID) - ) - - return legacySend(request).map(on: OpenGroupAPI.workQueue) { _ in } - } - - @available(*, deprecated, message: "Use v4 endpoint instead") - public static func legacyGetDeletedMessages(for room: String, on server: String) -> Promise<[LegacyDeletion]> { - let storage = SNMessagingKitConfiguration.shared.storage - - let request: Request = Request( - server: server, - room: room, - endpoint: .legacyDeletedMessages, - queryParameters: [ - .fromServerId: storage.getLastDeletionServerID(for: room, on: server).map { String($0) } - ].compactMapValues { $0 } - ) - - return legacySend(request).then(on: OpenGroupAPI.workQueue) { _, maybeData -> Promise<[LegacyDeletion]> in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let response: LegacyDeletedMessagesResponse = try data.decoded(as: LegacyDeletedMessagesResponse.self, customError: Error.parsingFailed) - - return legacyProcess(deletions: response.deletions, for: room, on: server) - } - } - - // MARK: - Legacy Moderation - - @available(*, deprecated, message: "Use v4 endpoint instead") - public static func legacyGetModerators(for room: String, on server: String) -> Promise<[String]> { - let request: Request = Request( - server: server, - room: room, - endpoint: .legacyModerators - ) - - return legacySend(request) - .map(on: OpenGroupAPI.workQueue) { _, maybeData in - guard let data: Data = maybeData else { throw Error.parsingFailed } - let response: LegacyModeratorsResponse = try data.decoded(as: LegacyModeratorsResponse.self, customError: Error.parsingFailed) - - if var x = self.moderators[server] { - x[room] = Set(response.moderators) - self.moderators[server] = x - } - else { - self.moderators[server] = [room: Set(response.moderators)] - } - - return response.moderators - } - } - - @available(*, deprecated, message: "Use v4 endpoint instead") - public static func legacyBan(_ publicKey: String, from room: String, on server: String) -> Promise { - let requestBody: LegacyPublicKeyBody = LegacyPublicKeyBody(publicKey: getUserHexEncodedPublicKey()) - - guard let body: Data = try? JSONEncoder().encode(requestBody) else { - return Promise(error: HTTP.Error.invalidJSON) - } - - let request: Request = Request( - method: .post, - server: server, - room: room, - endpoint: .legacyBlockList, - body: body - ) - - return legacySend(request).map(on: OpenGroupAPI.workQueue) { _ in } - } - - @available(*, deprecated, message: "Use v4 endpoint instead") - public static func legacyBanAndDeleteAllMessages(_ publicKey: String, from room: String, on server: String) -> Promise { - let requestBody: LegacyPublicKeyBody = LegacyPublicKeyBody(publicKey: getUserHexEncodedPublicKey()) - - guard let body: Data = try? JSONEncoder().encode(requestBody) else { - return Promise(error: HTTP.Error.invalidJSON) - } - - let request: Request = Request( - method: .post, - server: server, - room: room, - endpoint: .legacyBanAndDeleteAll, - body: body - ) - - return legacySend(request).map(on: OpenGroupAPI.workQueue) { _ in } - } - - @available(*, deprecated, message: "Use v4 endpoint instead") - public static func legacyUnban(_ publicKey: String, from room: String, on server: String) -> Promise { - let request: Request = Request( - method: .delete, - server: server, - room: room, - endpoint: .legacyBlockListIndividual(publicKey) - ) - - return legacySend(request).map(on: OpenGroupAPI.workQueue) { _ in } - } - - // MARK: - Processing - // TODO: Move these methods to the OpenGroupManager? (seems odd for them to be in the API) - - @available(*, deprecated, message: "Use v4 endpoint instead") - private static func legacyProcess(messages: [LegacyOpenGroupMessageV2]?, for room: String, on server: String) -> Promise<[LegacyOpenGroupMessageV2]> { - guard let messages: [LegacyOpenGroupMessageV2] = messages, !messages.isEmpty else { return Promise.value([]) } - - let storage = SNMessagingKitConfiguration.shared.storage - let serverID: Int64 = (messages.compactMap { $0.serverID }.max() ?? 0) - let lastMessageServerID: Int64 = (storage.getLastMessageServerID(for: room, on: server) ?? 0) - - if serverID > lastMessageServerID { - let (promise, seal) = Promise<[LegacyOpenGroupMessageV2]>.pending() - - storage.write( - with: { transaction in - storage.setLastMessageServerID(for: room, on: server, to: serverID, using: transaction) - }, - completion: { - seal.fulfill(messages) - } - ) - - return promise - } - - return Promise.value(messages) - } - - @available(*, deprecated, message: "Use v4 endpoint instead") - private static func legacyProcess(deletions: [LegacyDeletion]?, for room: String, on server: String) -> Promise<[LegacyDeletion]> { - guard let deletions: [LegacyDeletion] = deletions else { return Promise.value([]) } - - let storage = SNMessagingKitConfiguration.shared.storage - let serverID: Int64 = (deletions.compactMap { $0.id }.max() ?? 0) - let lastDeletionServerID: Int64 = (storage.getLastDeletionServerID(for: room, on: server) ?? 0) - - if serverID > lastDeletionServerID { - let (promise, seal) = Promise<[LegacyDeletion]>.pending() - - storage.write( - with: { transaction in - storage.setLastDeletionServerID(for: room, on: server, to: serverID, using: transaction) - }, - completion: { - seal.fulfill(deletions) - } - ) - - return promise - } - - return Promise.value(deletions) - } - - // MARK: - Legacy Convenience - - @available(*, deprecated, message: "Use v4 endpoint instead") - private static func legacySend(_ request: Request, through api: OnionRequestAPIType.Type = OnionRequestAPI.self) -> Promise<(OnionRequestResponseInfoType, Data?)> { - guard let url: URL = request.url else { return Promise(error: Error.invalidURL) } - - var urlRequest: URLRequest = URLRequest(url: url) - urlRequest.httpMethod = request.method.rawValue - urlRequest.allHTTPHeaderFields = request.headers - .setting(.room, request.room) // TODO: Is this needed anymore? Add at the request level?. - .toHTTPHeaders() - urlRequest.httpBody = request.body - - if request.useOnionRouting { - guard let publicKey = SNMessagingKitConfiguration.shared.storage.getOpenGroupPublicKey(for: request.server) else { - return Promise(error: Error.noPublicKey) - } - - if request.isAuthRequired { - // Because legacy auth happens on a per-room basis, we need to have a room to - // make an authenticated request - guard let room = request.room else { - return api.sendOnionRequest(urlRequest, to: request.server, using: .v3, with: publicKey) - } - - return legacyGetAuthToken(for: room, on: request.server) - .then(on: OpenGroupAPI.workQueue) { authToken -> Promise<(OnionRequestResponseInfoType, Data?)> in - urlRequest.setValue(authToken, forHTTPHeaderField: Header.authorization.rawValue) - - let promise = api.sendOnionRequest(urlRequest, to: request.server, using: .v3, with: publicKey) - promise.catch(on: OpenGroupAPI.workQueue) { error in - // A 401 means that we didn't provide a (valid) auth token for a route - // that required one. We use this as an indication that the token we're - // using has expired. Note that a 403 has a different meaning; it means - // that we provided a valid token but it doesn't have a high enough - // permission level for the route in question. - if case OnionRequestAPI.Error.httpRequestFailedAtDestination(let statusCode, _, _) = error, statusCode == 401 { - let storage = SNMessagingKitConfiguration.shared.storage - - storage.writeSync { transaction in - storage.removeAuthToken(for: room, on: request.server, using: transaction) - } - } - } - - return promise - } - } - - return api.sendOnionRequest(urlRequest, to: request.server, using: .v3, with: publicKey) - } - - preconditionFailure("It's currently not allowed to send non onion routed requests.") - } } diff --git a/SessionMessagingKit/Open Groups/OpenGroupManager.swift b/SessionMessagingKit/Open Groups/OpenGroupManager.swift index d23ae1edd..c128a6bc9 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupManager.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupManager.swift @@ -14,8 +14,12 @@ public final class OpenGroupManager: NSObject { public static var defaultRoomsPromise: Promise<[OpenGroupAPI.Room]>? private static var groupImagePromises: [String: Promise] = [:] - private static var moderators: [String: [String: Set]] = [:] // Server URL to room ID to set of moderator IDs - private static var admins: [String: [String: Set]] = [:] // Server URL to room ID to set of admin IDs + + /// Server URL to room ID to set of moderator IDs + private static var moderators: Atomic<[String: [String: Set]]> = Atomic([:]) + + /// Server URL to room ID to set of admin IDs + private static var admins: Atomic<[String: [String: Set]]> = Atomic([:]) // MARK: - Polling @@ -243,14 +247,16 @@ public final class OpenGroupManager: NSObject { // - Moderators if let moderators: [String] = (pollInfo.details?.moderators ?? maybeUpdatedModel?.groupModeratorIds) { - OpenGroupManager.moderators[server] = (OpenGroupManager.moderators[server] ?? [:]) - .setting(roomToken, Set(moderators)) + OpenGroupManager.moderators.mutate { + $0[server] = ($0[server] ?? [:]).setting(roomToken, Set(moderators)) + } } // - Admins if let admins: [String] = (pollInfo.details?.admins ?? maybeUpdatedModel?.groupAdminIds) { - OpenGroupManager.admins[server] = (OpenGroupManager.admins[server] ?? [:]) - .setting(roomToken, Set(admins)) + OpenGroupManager.admins.mutate { + $0[server] = ($0[server] ?? [:]).setting(roomToken, Set(admins)) + } } // - Room image (if there is one) @@ -357,7 +363,7 @@ public final class OpenGroupManager: NSObject { let sortedMessages: [OpenGroupAPI.DirectMessage] = messages .sorted { lhs, rhs in lhs.id < rhs.id } let latestMessageId: Int64 = (sortedMessages.last?.id ?? 0) - var mappingCache: [String: BlindedIdMapping] = [:] + var mappingCache: [String: BlindedIdMapping] = [:] // Only want this cache to exist for the current loop // Update the 'latestMessageId' value if fromOutbox { @@ -377,7 +383,7 @@ public final class OpenGroupManager: NSObject { // Note: The `posted` value is in seconds but all messages in the database use milliseconds for timestamps let envelope = SNProtoEnvelope.builder(type: .sessionMessage, timestamp: UInt64(floor(message.posted * 1000))) envelope.setContent(messageData) - envelope.setSource(message.sender) // TODO: Need to un-blind/intercept outbox messages? (their sender will be the blinded id) + envelope.setSource(message.sender) do { let data = try envelope.buildSerializedData() @@ -391,7 +397,6 @@ public final class OpenGroupManager: NSObject { using: transaction ) - // TODO: Need to test and validate this unblinding logic. // If the message was an outgoing message then attempt to unblind the recipient (this will help put // messages in the correct thread in case of message request approval race conditions as well as // during device sync'ing and restoration) @@ -399,11 +404,13 @@ public final class OpenGroupManager: NSObject { // Attempt to un-blind the 'message.recipient' let mapping: BlindedIdMapping - // Minor optimisation to avoid processing the same sender multiple times + // Minor optimisation to avoid processing the same sender multiple times in the same + // 'handleMessages' call (since the 'mapping' call is done within a transaction we + // will never have a mapping come through part-way through processing these messages) if let result: BlindedIdMapping = mappingCache[message.recipient] { mapping = result } - else if let result: BlindedIdMapping = ContactUtilities.mapping(for: message.recipient, serverPublicKey: serverPublicKey) { + else if let result: BlindedIdMapping = ContactUtilities.mapping(for: message.recipient, serverPublicKey: serverPublicKey, using: transaction) { mapping = result } else { @@ -426,6 +433,15 @@ public final class OpenGroupManager: NSObject { } try MessageReceiver.handle(receivedMessage, associatedWithProto: proto, openGroupID: nil, isBackgroundPoll: isBackgroundPoll, using: transaction) + + // If this message is from the outbox then we should add the open group details back to the + // thread just in case this is from a restore (otherwise the user won't be able to send a new + // message to the target inbox if they are still blinded) + if fromOutbox, let contactThread: TSContactThread = TSContactThread.fetch(uniqueId: TSContactThread.threadID(fromContactSessionID: message.recipient), transaction: transaction) { + contactThread.originalOpenGroupServer = server + contactThread.originalOpenGroupPublicKey = serverPublicKey + contactThread.save(with: transaction) + } } catch let error { SNLog("Couldn't receive inbox message due to error: \(error).") @@ -442,35 +458,51 @@ public final class OpenGroupManager: NSObject { } public static func isUserModeratorOrAdmin(_ publicKey: String, for room: String, on server: String, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Bool { - var targetKeys: [String] = [publicKey] - - // If we are checking for the current users public key then check for the blinded one as well - if publicKey == getUserHexEncodedPublicKey() { - guard let userEdKeyPair: Box.KeyPair = dependencies.storage.getUserED25519KeyPair() else { return false } - guard let serverPublicKey: String = dependencies.storage.getOpenGroupPublicKey(for: server) else { - return false - } - - // Add the unblinded key as an option - targetKeys.append(SessionId(.unblinded, publicKey: userEdKeyPair.publicKey).hexString) - - let server: OpenGroupAPI.Server? = dependencies.storage.getOpenGroupServer(name: server) - - // Check if the server supports blinded keys, if so then sign using the blinded key - if server?.capabilities.capabilities.contains(.blind) == true { + let modAndAdminKeys: Set = (OpenGroupManager.moderators.wrappedValue[server]?[room] ?? Set()) + .union(OpenGroupManager.admins.wrappedValue[server]?[room] ?? Set()) + + // If the publicKey is in the set then return immediately, otherwise only continue if it's the + // current user + guard !modAndAdminKeys.contains(publicKey) else { return true } + guard let sessionId: SessionId = SessionId(from: publicKey) else { return false } + + // Conveniently the logic for these different cases works in order so we can fallthrough each + // case with only minor efficiency losses + switch sessionId.prefix { + case .standard: + guard publicKey == getUserHexEncodedPublicKey() else { return false } + fallthrough + + case .unblinded: + guard let userEdKeyPair: Box.KeyPair = dependencies.storage.getUserED25519KeyPair() else { return false } + guard sessionId.prefix != .unblinded || publicKey == SessionId(.unblinded, publicKey: userEdKeyPair.publicKey).hexString else { + return false + } + fallthrough + + case .blinded: + guard let userEdKeyPair: Box.KeyPair = dependencies.storage.getUserED25519KeyPair() else { return false } + guard let serverPublicKey: String = dependencies.storage.getOpenGroupPublicKey(for: server) else { + return false + } guard let blindedKeyPair: Box.KeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: serverPublicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else { return false } - - // Add the blinded key as an option - targetKeys.append(SessionId(.blinded, publicKey: blindedKeyPair.publicKey).hexString) - } + guard sessionId.prefix != .blinded || publicKey == SessionId(.blinded, publicKey: blindedKeyPair.publicKey).hexString else { + return false + } + + // If we got to here that means that the 'publicKey' value matches one of the current + // users 'standard', 'unblinded' or 'blinded' keys and as such we should check if any + // of them exist in the `modsAndAminKeys` Set + let possibleKeys: Set = Set([ + getUserHexEncodedPublicKey(), + SessionId(.unblinded, publicKey: userEdKeyPair.publicKey).hexString, + SessionId(.blinded, publicKey: blindedKeyPair.publicKey).hexString + ]) + + return !modAndAdminKeys.intersection(possibleKeys).isEmpty } - - return ( - (OpenGroupManager.moderators[server]?[room]?.contains(where: { key in targetKeys.contains(key) }) ?? false) || - (OpenGroupManager.admins[server]?[room]?.contains(where: { key in targetKeys.contains(key) }) ?? false) - ) } public static func getDefaultRoomsIfNeeded(using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) { diff --git a/SessionMessagingKit/Open Groups/Types/SOGSEndpoint.swift b/SessionMessagingKit/Open Groups/Types/SOGSEndpoint.swift index d664f682e..10c5088d3 100644 --- a/SessionMessagingKit/Open Groups/Types/SOGSEndpoint.swift +++ b/SessionMessagingKit/Open Groups/Types/SOGSEndpoint.swift @@ -53,31 +53,6 @@ extension OpenGroupAPI { case userModerator(String) case userDeleteMessages(String) - // Legacy endpoints (to be deprecated and removed) - - @available(*, deprecated, message: "Use v4 endpoint") case legacyFiles - @available(*, deprecated, message: "Use v4 endpoint") case legacyFile(UInt64) - - @available(*, deprecated, message: "Use v4 endpoint") case legacyMessages - @available(*, deprecated, message: "Use v4 endpoint") case legacyMessagesForServer(Int64) - @available(*, deprecated, message: "Use v4 endpoint") case legacyDeletedMessages - - @available(*, deprecated, message: "Use v4 endpoint") case legacyModerators - - @available(*, deprecated, message: "Use v4 endpoint") case legacyBlockList - @available(*, deprecated, message: "Use v4 endpoint") case legacyBlockListIndividual(String) - @available(*, deprecated, message: "Use v4 endpoint") case legacyBanAndDeleteAll - - @available(*, deprecated, message: "Use v4 endpoint") case legacyCompactPoll(legacyAuth: Bool) - @available(*, deprecated, message: "Use request signing") case legacyAuthToken(legacyAuth: Bool) - @available(*, deprecated, message: "Use request signing") case legacyAuthTokenChallenge(legacyAuth: Bool) - @available(*, deprecated, message: "Use request signing") case legacyAuthTokenClaim(legacyAuth: Bool) - - @available(*, deprecated, message: "Use v4 endpoint") case legacyRooms - @available(*, deprecated, message: "Use v4 endpoint") case legacyRoomInfo(String) - @available(*, deprecated, message: "Use v4 endpoint") case legacyRoomImage(String) - @available(*, deprecated, message: "Use v4 endpoint") case legacyMemberCount(legacyAuth: Bool) - var path: String { switch self { // Utility @@ -142,65 +117,6 @@ extension OpenGroupAPI { case .userPermission(let sessionId): return "user/\(sessionId)/permission" case .userModerator(let sessionId): return "user/\(sessionId)/moderator" case .userDeleteMessages(let sessionId): return "user/\(sessionId)/deleteMessages" - - // Legacy endpoints (to be deprecated and removed) - // TODO: Look for a nicer way to prepend 'legacy'? (OnionRequestAPI messes with this but the new auth needs it to be correct...) - - - case .legacyFiles: return "legacy/files" - case .legacyFile(let fileId): return "legacy/files/\(fileId)" - - case .legacyMessages: return "legacy/messages" - case .legacyMessagesForServer(let serverId): return "legacy/messages/\(serverId)" - case .legacyDeletedMessages: return "legacy/deleted_messages" - - case .legacyModerators: return "legacy/moderators" - - case .legacyBlockList: return "legacy/block_list" - case .legacyBlockListIndividual(let publicKey): return "legacy/block_list/\(publicKey)" - case .legacyBanAndDeleteAll: return "legacy/ban_and_delete_all" - - case .legacyCompactPoll(let useLegacyAuth): - return "\(useLegacyAuth ? "" : "legacy/")compact_poll" - - case .legacyAuthToken(let useLegacyAuth): - return "\(useLegacyAuth ? "" : "legacy/")auth_token" - - case .legacyAuthTokenChallenge(let useLegacyAuth): - return "\(useLegacyAuth ? "" : "legacy/")auth_token_challenge" - - case .legacyAuthTokenClaim(let useLegacyAuth): - return "\(useLegacyAuth ? "" : "legacy/")claim_auth_token" - - case .legacyRooms: return "legacy/rooms" - case .legacyRoomInfo(let roomName): return "legacy/rooms/\(roomName)" - case .legacyRoomImage(let roomName): return "legacy/rooms/\(roomName)/image" - - case .legacyMemberCount(let useLegacyAuth): - return "\(useLegacyAuth ? "" : "legacy/")member_count" - } - } - - var useLegacyAuth: Bool { - switch self { - // File upload/download should use legacy auth - case .legacyFiles, .legacyFile, .legacyMessages, - .legacyMessagesForServer, .legacyDeletedMessages, - .legacyModerators, .legacyBlockList, - .legacyBlockListIndividual, .legacyBanAndDeleteAll: - return true - - case .legacyCompactPoll(let useLegacyAuth), - .legacyAuthToken(let useLegacyAuth), - .legacyAuthTokenChallenge(let useLegacyAuth), - .legacyAuthTokenClaim(let useLegacyAuth), - .legacyMemberCount(let useLegacyAuth): - return useLegacyAuth - - case .legacyRooms, .legacyRoomInfo, .legacyRoomImage: - return true - - default: return false } } } diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift index 54a65d1a9..3e1c1b283 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift @@ -300,7 +300,7 @@ extension MessageReceiver { } if let messageToDelete = localMessage { if let incomingMessage = messageToDelete as? TSIncomingMessage { - incomingMessage.markAsReadNow(withSendReadReceipt: false, transaction: transaction) + incomingMessage.markAsReadNow(withTrySendReadReceipt: false, transaction: transaction) if let notificationIdentifier = incomingMessage.notificationIdentifier, !notificationIdentifier.isEmpty { UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [notificationIdentifier]) UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notificationIdentifier]) diff --git a/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift b/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift index 103491930..a742a9639 100644 --- a/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift +++ b/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift @@ -66,19 +66,6 @@ extension OpenGroupAPI { self?.isPolling = false seal.fulfill(()) // The promise is just used to keep track of when we're done } - // OpenGroupAPI.compactPoll(server) - // OpenGroupAPI.legacyCompactPoll(server) - // .done(on: OpenGroupAPI.workQueue) { [weak self] response in - // guard let self = self else { return } - // self.isPolling = false - // response.results.forEach { self.handleCompactPollBody($0, isBackgroundPoll: isBackgroundPoll) } - // seal.fulfill(()) - // } - // .catch(on: OpenGroupAPI.workQueue) { error in - // SNLog("Open group polling failed due to error: \(error).") - // self.isPolling = false - // seal.fulfill(()) // The promise is just used to keep track of when we're done - // } return promise } @@ -160,58 +147,5 @@ extension OpenGroupAPI { } } } - - // MARK: - Legacy Handling - - private func handleCompactPollBody(_ body: OpenGroupAPI.LegacyCompactPollResponse.Result, isBackgroundPoll: Bool) { - let storage = SNMessagingKitConfiguration.shared.storage - // - Messages - // Sorting the messages by server ID before importing them fixes an issue where messages that quote older messages can't find those older messages - let openGroupID = "\(server).\(body.room)" - let messages = (body.messages ?? []).sorted { ($0.serverID ?? 0) < ($1.serverID ?? 0) } - - storage.write { transaction in - messages.forEach { message in - guard let data = Data(base64Encoded: message.base64EncodedData) else { - return SNLog("Ignoring open group message with invalid encoding.") - } - let envelope = SNProtoEnvelope.builder(type: .sessionMessage, timestamp: message.sentTimestamp) - envelope.setContent(data) - envelope.setSource(message.sender!) // Safe because messages with a nil sender are filtered out - do { - let data = try envelope.buildSerializedData() - let (message, proto) = try MessageReceiver.parse(data, openGroupMessageServerID: UInt64(message.serverID!), isRetry: false, using: transaction) - try MessageReceiver.handle(message, associatedWithProto: proto, openGroupID: openGroupID, isBackgroundPoll: isBackgroundPoll, using: transaction) - } catch { - SNLog("Couldn't receive open group message due to error: \(error).") - } - } - } - - // - Moderators - if var x = OpenGroupAPI.moderators[server] { - x[body.room] = Set(body.moderators ?? []) - OpenGroupAPI.moderators[server] = x - } - else { - OpenGroupAPI.moderators[server] = [ body.room : Set(body.moderators ?? []) ] - } - - // - Deletions - let deletedMessageServerIDs = Set((body.deletions ?? []).map { UInt64($0.deletedMessageID) }) - storage.write { transaction in - let transaction = transaction as! YapDatabaseReadWriteTransaction - guard let threadID = storage.getThreadID(for: openGroupID), - let thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction) else { return } - var messagesToRemove: [TSMessage] = [] - - thread.enumerateInteractions(with: transaction) { interaction, stop in - guard let message = interaction as? TSMessage, deletedMessageServerIDs.contains(message.openGroupServerMessageID) else { return } - messagesToRemove.append(message) - } - - messagesToRemove.forEach { $0.remove(with: transaction) } - } - } } } diff --git a/SessionMessagingKit/Storage.swift b/SessionMessagingKit/Storage.swift index 92d3d12c9..3021827e5 100644 --- a/SessionMessagingKit/Storage.swift +++ b/SessionMessagingKit/Storage.swift @@ -28,7 +28,7 @@ public protocol SessionMessagingKitStorageProtocol { func cacheBlindedIdMapping(_ mapping: BlindedIdMapping) func cacheBlindedIdMapping(_ mapping: BlindedIdMapping, using transaction: YapDatabaseReadWriteTransaction) func enumerateBlindedIdMapping(with block: @escaping (BlindedIdMapping, UnsafeMutablePointer) -> ()) - func enumerateBlindedIdMapping(with block: @escaping (BlindedIdMapping, UnsafeMutablePointer) -> (), transaction: YapDatabaseReadTransaction) + func enumerateBlindedIdMapping(using transaction: YapDatabaseReadTransaction, with block: @escaping (BlindedIdMapping, UnsafeMutablePointer) -> ()) // MARK: - Closed Groups diff --git a/SessionMessagingKit/Utilities/ContactUtilities.swift b/SessionMessagingKit/Utilities/ContactUtilities.swift index 25e4e3cda..12de6d358 100644 --- a/SessionMessagingKit/Utilities/ContactUtilities.swift +++ b/SessionMessagingKit/Utilities/ContactUtilities.swift @@ -39,39 +39,46 @@ public enum ContactUtilities { .map { $0.sessionID } } - public static func enumerateApprovedContactThreads(with block: @escaping (TSContactThread, Contact, UnsafeMutablePointer) -> ()) { - Storage.read { transaction in - TSContactThread.enumerateCollectionObjects(with: transaction) { object, stop in - guard let contactThread: TSContactThread = object as? TSContactThread else { return } - guard let contact: Contact = approvedContact(in: object, using: transaction) else { return } - - block(contactThread, contact, stop) - } + public static func enumerateApprovedContactThreads(using transaction: YapDatabaseReadTransaction, with block: @escaping (TSContactThread, Contact, UnsafeMutablePointer) -> ()) { + TSContactThread.enumerateCollectionObjects(with: transaction) { object, stop in + guard let contactThread: TSContactThread = object as? TSContactThread else { return } + guard let contact: Contact = approvedContact(in: object, using: transaction) else { return } + + block(contactThread, contact, stop) } } public static func mapping(for blindedId: String, serverPublicKey: String, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> BlindedIdMapping? { - // TODO: Ensure the above case isn't going to be an issue due to legacy messages?. + var result: BlindedIdMapping? + + Storage.write { transaction in + result = ContactUtilities.mapping(for: blindedId, serverPublicKey: serverPublicKey, using: transaction, dependencies: dependencies) + } + + return result + } + + public static func mapping(for blindedId: String, serverPublicKey: String, using transaction: YapDatabaseReadWriteTransaction, dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> BlindedIdMapping? { // Unfortunately the whole point of id-blinding is to make it hard to reverse-engineer a standard // sessionId, as a result in order to see if there is an unblinded contact for this blindedId we // can only really generate blinded ids for each contact and check if any match // // Due to this we have made a few optimisations to try and early-out as often as possible, first // we try to retrieve a direct cached mapping - var mappingResult: BlindedIdMapping? = dependencies.storage.getBlindedIdMapping(with: blindedId) + var mappingResult: BlindedIdMapping? = dependencies.storage.getBlindedIdMapping(with: blindedId, using: transaction) // No need to continue if we already have a result if let mapping: BlindedIdMapping = mappingResult { return mapping } // Then we try loop through all approved contact threads to see if one of those contacts can be blinded to match - ContactUtilities.enumerateApprovedContactThreads { contactThread, contact, stop in + ContactUtilities.enumerateApprovedContactThreads(using: transaction) { contactThread, contact, stop in guard dependencies.sodium.sessionId(contact.sessionID, matchesBlindedId: blindedId, serverPublicKey: serverPublicKey) else { return } // Cache the mapping let newMapping: BlindedIdMapping = BlindedIdMapping(blindedId: blindedId, sessionId: contact.sessionID, serverPublicKey: serverPublicKey) - dependencies.storage.cacheBlindedIdMapping(newMapping) + dependencies.storage.cacheBlindedIdMapping(newMapping, using: transaction) mappingResult = newMapping stop.pointee = true } @@ -81,7 +88,7 @@ public enum ContactUtilities { // Lastly loop through existing id mappings (in case the user is looking at a different SOGS but once had // a thread with this contact in a different SOGS and had cached the mapping) - dependencies.storage.enumerateBlindedIdMapping { mapping, stop in + dependencies.storage.enumerateBlindedIdMapping(using: transaction) { mapping, stop in guard mapping.serverPublicKey != serverPublicKey else { return } guard dependencies.sodium.sessionId(mapping.sessionId, matchesBlindedId: blindedId, serverPublicKey: serverPublicKey) else { return @@ -89,7 +96,7 @@ public enum ContactUtilities { // Cache the new mapping let newMapping: BlindedIdMapping = BlindedIdMapping(blindedId: blindedId, sessionId: mapping.sessionId, serverPublicKey: serverPublicKey) - dependencies.storage.cacheBlindedIdMapping(newMapping) + dependencies.storage.cacheBlindedIdMapping(newMapping, using: transaction) mappingResult = newMapping stop.pointee = true } diff --git a/SessionMessagingKitTests/_TestUtilities/TestStorage.swift b/SessionMessagingKitTests/_TestUtilities/TestStorage.swift index bae468cd0..3424a7749 100644 --- a/SessionMessagingKitTests/_TestUtilities/TestStorage.swift +++ b/SessionMessagingKitTests/_TestUtilities/TestStorage.swift @@ -62,7 +62,7 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable { func cacheBlindedIdMapping(_ mapping: BlindedIdMapping) {} func cacheBlindedIdMapping(_ mapping: BlindedIdMapping, using transaction: YapDatabaseReadWriteTransaction) {} func enumerateBlindedIdMapping(with block: @escaping (BlindedIdMapping, UnsafeMutablePointer) -> ()) {} - func enumerateBlindedIdMapping(with block: @escaping (BlindedIdMapping, UnsafeMutablePointer) -> (), transaction: YapDatabaseReadTransaction) { + func enumerateBlindedIdMapping(using transaction: YapDatabaseReadTransaction, with block: @escaping (BlindedIdMapping, UnsafeMutablePointer) -> ()) { } // MARK: - Closed Groups