Fixed a number of bugs, resolved some TODOs and tested the outbox APIs

Updated the join open group method to retrieve the capabilities as part of the initial request
Updated the OpenGroupManager to require a transaction to be passed to the various 'handler' methods (allowing for everything to be processed within a single transaction)
Fixed a few issues where we weren't storing the timestamp for open group messages and DMs which could result in duplicate messages
Fixed an issue where we were setting the timestamp value for messages sent to an open group without converting it to be milliseconds to be consistent with other messages
Fixed an issue where the BatchRequestInfo could incorrectly flag it's response as failing to parse even though the type was optional
Fixed a bug where the open group would re-fetch all messages every other time
Fixed a bug where the long press context menu wouldn't appear after failing to delete a message
Fixed a bug where joining an open group would trigger the join behaviour and APIs twice
This commit is contained in:
Morgan Pretty 2022-03-03 17:46:35 +11:00
parent c04d4544f2
commit 8ca00ca578
19 changed files with 897 additions and 334 deletions

View File

@ -145,6 +145,7 @@
7BDCFC0B2421EB7600641C39 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; }; 7BDCFC0B2421EB7600641C39 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; };
8DDB50527360BA38AE415C6F /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C5060C3B36A848B71CCE4685 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit.framework */; }; 8DDB50527360BA38AE415C6F /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C5060C3B36A848B71CCE4685 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit.framework */; };
9B0A583E9B89FEF0916B793A /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278EF43EB1E6A0B83C9234F5 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit.framework */; }; 9B0A583E9B89FEF0916B793A /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278EF43EB1E6A0B83C9234F5 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit.framework */; };
9BF6299C8E265D8AC63E1D07 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit_SessionUtilitiesKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A06DA296F93403656DFA7991 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit_SessionUtilitiesKitTests.framework */; };
A11CD70D17FA230600A2D1B1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A11CD70C17FA230600A2D1B1 /* QuartzCore.framework */; }; A11CD70D17FA230600A2D1B1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A11CD70C17FA230600A2D1B1 /* QuartzCore.framework */; };
A163E8AB16F3F6AA0094D68B /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A163E8AA16F3F6A90094D68B /* Security.framework */; }; A163E8AB16F3F6AA0094D68B /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A163E8AA16F3F6A90094D68B /* Security.framework */; };
A1C32D5017A06538000A904E /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C32D4F17A06537000A904E /* AddressBookUI.framework */; }; A1C32D5017A06538000A904E /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C32D4F17A06537000A904E /* AddressBookUI.framework */; };
@ -784,6 +785,14 @@
FD705A94278D052B00F16121 /* UITableView+ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A93278D052B00F16121 /* UITableView+ReusableView.swift */; }; FD705A94278D052B00F16121 /* UITableView+ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A93278D052B00F16121 /* UITableView+ReusableView.swift */; };
FD705A98278E9F4D00F16121 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */; }; FD705A98278E9F4D00F16121 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */; };
FD83B9AA27CF149D005E1583 /* ContactUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9A927CF149D005E1583 /* ContactUtilities.swift */; }; FD83B9AA27CF149D005E1583 /* ContactUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9A927CF149D005E1583 /* ContactUtilities.swift */; };
FD83B9B327CF200A005E1583 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; platformFilter = ios; };
FD83B9BB27CF20AF005E1583 /* SessionIdSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9BA27CF20AF005E1583 /* SessionIdSpec.swift */; };
FD83B9BF27CF2294005E1583 /* TestConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9BD27CF2243005E1583 /* TestConstants.swift */; };
FD83B9C027CF2294005E1583 /* TestConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9BD27CF2243005E1583 /* TestConstants.swift */; };
FD83B9C327CF33F7005E1583 /* ServerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9C227CF33F7005E1583 /* ServerSpec.swift */; };
FD83B9C527CF3E2A005E1583 /* OpenGroupSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9C427CF3E2A005E1583 /* OpenGroupSpec.swift */; };
FD83B9C727CF3F10005E1583 /* CapabilitiesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9C627CF3F10005E1583 /* CapabilitiesSpec.swift */; };
FD83B9C927D0487A005E1583 /* SendDirectMessageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9C827D0487A005E1583 /* SendDirectMessageResponse.swift */; };
FD859EF227BF6BA200510D0C /* Data+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF127BF6BA200510D0C /* Data+Utilities.swift */; }; FD859EF227BF6BA200510D0C /* Data+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF127BF6BA200510D0C /* Data+Utilities.swift */; };
FD859EF427C2F49200510D0C /* TestSodium.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF327C2F49200510D0C /* TestSodium.swift */; }; FD859EF427C2F49200510D0C /* TestSodium.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF327C2F49200510D0C /* TestSodium.swift */; };
FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF527C2F52C00510D0C /* TestSign.swift */; }; FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF527C2F52C00510D0C /* TestSign.swift */; };
@ -834,7 +843,7 @@
FDC4387627B5BEF300C60D73 /* FileDownloadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4387527B5BEF300C60D73 /* FileDownloadResponse.swift */; }; FDC4387627B5BEF300C60D73 /* FileDownloadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4387527B5BEF300C60D73 /* FileDownloadResponse.swift */; };
FDC4387827B5C35400C60D73 /* SendMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4387727B5C35400C60D73 /* SendMessageRequest.swift */; }; FDC4387827B5C35400C60D73 /* SendMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4387727B5C35400C60D73 /* SendMessageRequest.swift */; };
FDC4389227B9FFC700C60D73 /* SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; platformFilter = ios; }; FDC4389227B9FFC700C60D73 /* SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; platformFilter = ios; };
FDC4389A27BA002500C60D73 /* OpenGroupAPIV2Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4389927BA002500C60D73 /* OpenGroupAPIV2Tests.swift */; }; FDC4389A27BA002500C60D73 /* OpenGroupAPISpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4389927BA002500C60D73 /* OpenGroupAPISpec.swift */; };
FDC4389D27BA01F000C60D73 /* TestStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4389C27BA01F000C60D73 /* TestStorage.swift */; }; FDC4389D27BA01F000C60D73 /* TestStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4389C27BA01F000C60D73 /* TestStorage.swift */; };
FDC438A427BB107F00C60D73 /* UserBanRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438A327BB107F00C60D73 /* UserBanRequest.swift */; }; FDC438A427BB107F00C60D73 /* UserBanRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438A327BB107F00C60D73 /* UserBanRequest.swift */; };
FDC438A627BB113A00C60D73 /* UserUnbanRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */; }; FDC438A627BB113A00C60D73 /* UserUnbanRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */; };
@ -969,6 +978,13 @@
remoteGlobalIDString = C33FD9AA255A548A00E217F9; remoteGlobalIDString = C33FD9AA255A548A00E217F9;
remoteInfo = SignalUtilitiesKit; remoteInfo = SignalUtilitiesKit;
}; };
FD83B9B427CF200A005E1583 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D221A080169C9E5E00537ABF /* Project object */;
proxyType = 1;
remoteGlobalIDString = C3C2A678255388CC00C340D1;
remoteInfo = SessionUtilitiesKit;
};
FDC4386E27B4E90300C60D73 /* PBXContainerItemProxy */ = { FDC4386E27B4E90300C60D73 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = D221A080169C9E5E00537ABF /* Project object */; containerPortal = D221A080169C9E5E00537ABF /* Project object */;
@ -1240,6 +1256,7 @@
9B3329176C10E9640865E65B /* Pods-GlobalDependencies-Session.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-Session.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-Session/Pods-GlobalDependencies-Session.debug.xcconfig"; sourceTree = "<group>"; }; 9B3329176C10E9640865E65B /* Pods-GlobalDependencies-Session.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-Session.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-Session/Pods-GlobalDependencies-Session.debug.xcconfig"; sourceTree = "<group>"; };
9B533A9FA46206D3D99C9ADA /* Pods-SignalMessaging.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalMessaging.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalMessaging/Pods-SignalMessaging.debug.xcconfig"; sourceTree = "<group>"; }; 9B533A9FA46206D3D99C9ADA /* Pods-SignalMessaging.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalMessaging.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalMessaging/Pods-SignalMessaging.debug.xcconfig"; sourceTree = "<group>"; };
9C0469AC557930C01552CC83 /* Pods-SignalUtilitiesKit.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalUtilitiesKit.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-SignalUtilitiesKit/Pods-SignalUtilitiesKit.app store release.xcconfig"; sourceTree = "<group>"; }; 9C0469AC557930C01552CC83 /* Pods-SignalUtilitiesKit.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalUtilitiesKit.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-SignalUtilitiesKit/Pods-SignalUtilitiesKit.app store release.xcconfig"; sourceTree = "<group>"; };
A06DA296F93403656DFA7991 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit_SessionUtilitiesKitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit_SessionUtilitiesKitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A0FB43B511403A5FAFAC88B8 /* Pods-SessionMessagingKitTests.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionMessagingKitTests.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-SessionMessagingKitTests/Pods-SessionMessagingKitTests.app store release.xcconfig"; sourceTree = "<group>"; }; A0FB43B511403A5FAFAC88B8 /* Pods-SessionMessagingKitTests.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionMessagingKitTests.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-SessionMessagingKitTests/Pods-SessionMessagingKitTests.app store release.xcconfig"; sourceTree = "<group>"; };
A11CD70C17FA230600A2D1B1 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; A11CD70C17FA230600A2D1B1 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
A163E8AA16F3F6A90094D68B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; A163E8AA16F3F6A90094D68B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
@ -1249,7 +1266,9 @@
A5509EC91A69AB8B00ABA4BC /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; }; A5509EC91A69AB8B00ABA4BC /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
A5C037C0D2746ABEE2684E70 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.debug.xcconfig"; sourceTree = "<group>"; }; A5C037C0D2746ABEE2684E70 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.debug.xcconfig"; sourceTree = "<group>"; };
A6344D429FFAC3B44E6A06FA /* Pods-SessionSnodeKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionSnodeKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SessionSnodeKit/Pods-SessionSnodeKit.debug.xcconfig"; sourceTree = "<group>"; }; A6344D429FFAC3B44E6A06FA /* Pods-SessionSnodeKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionSnodeKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SessionSnodeKit/Pods-SessionSnodeKit.debug.xcconfig"; sourceTree = "<group>"; };
A87BC4AF5BA3E3DE713B08E5 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests.app store release.xcconfig"; sourceTree = "<group>"; };
A9F14F620D87A5BA98DDB608 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionShareExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionShareExtension.debug.xcconfig"; sourceTree = "<group>"; }; A9F14F620D87A5BA98DDB608 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionShareExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
AAC5927BC89F6F265332C324 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests.debug.xcconfig"; sourceTree = "<group>"; };
AD2AB1207E8888E4262D781B /* Pods-SignalTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalTests/Pods-SignalTests.debug.xcconfig"; sourceTree = "<group>"; }; AD2AB1207E8888E4262D781B /* Pods-SignalTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalTests/Pods-SignalTests.debug.xcconfig"; sourceTree = "<group>"; };
ADF724B347C8815D97258101 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.app store release.xcconfig"; sourceTree = "<group>"; }; ADF724B347C8815D97258101 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SignalUtilitiesKit.app store release.xcconfig"; sourceTree = "<group>"; };
AEA8083C060FF9BAFF6E0C9F /* Pods-SessionProtocolKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionProtocolKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SessionProtocolKit/Pods-SessionProtocolKit.debug.xcconfig"; sourceTree = "<group>"; }; AEA8083C060FF9BAFF6E0C9F /* Pods-SessionProtocolKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionProtocolKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SessionProtocolKit/Pods-SessionProtocolKit.debug.xcconfig"; sourceTree = "<group>"; };
@ -1921,6 +1940,13 @@
FD705A93278D052B00F16121 /* UITableView+ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+ReusableView.swift"; sourceTree = "<group>"; }; FD705A93278D052B00F16121 /* UITableView+ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+ReusableView.swift"; sourceTree = "<group>"; };
FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; }; FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
FD83B9A927CF149D005E1583 /* ContactUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactUtilities.swift; sourceTree = "<group>"; }; FD83B9A927CF149D005E1583 /* ContactUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactUtilities.swift; sourceTree = "<group>"; };
FD83B9AF27CF200A005E1583 /* SessionUtilitiesKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SessionUtilitiesKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
FD83B9BA27CF20AF005E1583 /* SessionIdSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionIdSpec.swift; sourceTree = "<group>"; };
FD83B9BD27CF2243005E1583 /* TestConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestConstants.swift; sourceTree = "<group>"; };
FD83B9C227CF33F7005E1583 /* ServerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSpec.swift; sourceTree = "<group>"; };
FD83B9C427CF3E2A005E1583 /* OpenGroupSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupSpec.swift; sourceTree = "<group>"; };
FD83B9C627CF3F10005E1583 /* CapabilitiesSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapabilitiesSpec.swift; sourceTree = "<group>"; };
FD83B9C827D0487A005E1583 /* SendDirectMessageResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendDirectMessageResponse.swift; sourceTree = "<group>"; };
FD859EEF27BF207700510D0C /* SessionProtos.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = SessionProtos.proto; sourceTree = "<group>"; }; FD859EEF27BF207700510D0C /* SessionProtos.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = SessionProtos.proto; sourceTree = "<group>"; };
FD859EF027BF207C00510D0C /* WebSocketResources.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = WebSocketResources.proto; sourceTree = "<group>"; }; FD859EF027BF207C00510D0C /* WebSocketResources.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = WebSocketResources.proto; sourceTree = "<group>"; };
FD859EF127BF6BA200510D0C /* Data+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = "<group>"; }; FD859EF127BF6BA200510D0C /* Data+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = "<group>"; };
@ -1973,7 +1999,7 @@
FDC4387527B5BEF300C60D73 /* FileDownloadResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDownloadResponse.swift; sourceTree = "<group>"; }; FDC4387527B5BEF300C60D73 /* FileDownloadResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDownloadResponse.swift; sourceTree = "<group>"; };
FDC4387727B5C35400C60D73 /* SendMessageRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMessageRequest.swift; sourceTree = "<group>"; }; FDC4387727B5C35400C60D73 /* SendMessageRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMessageRequest.swift; sourceTree = "<group>"; };
FDC4388E27B9FFC700C60D73 /* SessionMessagingKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SessionMessagingKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; FDC4388E27B9FFC700C60D73 /* SessionMessagingKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SessionMessagingKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
FDC4389927BA002500C60D73 /* OpenGroupAPIV2Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupAPIV2Tests.swift; sourceTree = "<group>"; }; FDC4389927BA002500C60D73 /* OpenGroupAPISpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupAPISpec.swift; sourceTree = "<group>"; };
FDC4389C27BA01F000C60D73 /* TestStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestStorage.swift; sourceTree = "<group>"; }; FDC4389C27BA01F000C60D73 /* TestStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestStorage.swift; sourceTree = "<group>"; };
FDC438A327BB107F00C60D73 /* UserBanRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserBanRequest.swift; sourceTree = "<group>"; }; FDC438A327BB107F00C60D73 /* UserBanRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserBanRequest.swift; sourceTree = "<group>"; };
FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserUnbanRequest.swift; sourceTree = "<group>"; }; FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserUnbanRequest.swift; sourceTree = "<group>"; };
@ -2109,6 +2135,15 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
FD83B9AC27CF200A005E1583 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FD83B9B327CF200A005E1583 /* SessionUtilitiesKit.framework in Frameworks */,
9BF6299C8E265D8AC63E1D07 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit_SessionUtilitiesKitTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
FDC4388B27B9FFC700C60D73 /* Frameworks */ = { FDC4388B27B9FFC700C60D73 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -2329,6 +2364,8 @@
C8153B96A292A25045BE2C54 /* Pods-SessionTests.app store release.xcconfig */, C8153B96A292A25045BE2C54 /* Pods-SessionTests.app store release.xcconfig */,
949F269926ABA08C125DCA9D /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionMessagingKit-SessionMessagingKitTests.debug.xcconfig */, 949F269926ABA08C125DCA9D /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionMessagingKit-SessionMessagingKitTests.debug.xcconfig */,
0208C84C4D15048D699BEC10 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionMessagingKit-SessionMessagingKitTests.app store release.xcconfig */, 0208C84C4D15048D699BEC10 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionMessagingKit-SessionMessagingKitTests.app store release.xcconfig */,
AAC5927BC89F6F265332C324 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests.debug.xcconfig */,
A87BC4AF5BA3E3DE713B08E5 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests.app store release.xcconfig */,
); );
name = Pods; name = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
@ -3722,7 +3759,9 @@
C3C2A6F125539DE700C340D1 /* SessionMessagingKit */, C3C2A6F125539DE700C340D1 /* SessionMessagingKit */,
C3C2A5A0255385C100C340D1 /* SessionSnodeKit */, C3C2A5A0255385C100C340D1 /* SessionSnodeKit */,
C3C2A67A255388CC00C340D1 /* SessionUtilitiesKit */, C3C2A67A255388CC00C340D1 /* SessionUtilitiesKit */,
FD83B9BC27CF2215005E1583 /* SharedTest */,
FDC4388F27B9FFC700C60D73 /* SessionMessagingKitTests */, FDC4388F27B9FFC700C60D73 /* SessionMessagingKitTests */,
FD83B9B027CF200A005E1583 /* SessionUtilitiesKitTests */,
D221A08C169C9E5E00537ABF /* Frameworks */, D221A08C169C9E5E00537ABF /* Frameworks */,
D221A08A169C9E5E00537ABF /* Products */, D221A08A169C9E5E00537ABF /* Products */,
9404664EC513585B05DF1350 /* Pods */, 9404664EC513585B05DF1350 /* Pods */,
@ -3741,6 +3780,7 @@
C331FF1B2558F9D300070591 /* SessionUIKit.framework */, C331FF1B2558F9D300070591 /* SessionUIKit.framework */,
C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */, C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */,
FDC4388E27B9FFC700C60D73 /* SessionMessagingKitTests.xctest */, FDC4388E27B9FFC700C60D73 /* SessionMessagingKitTests.xctest */,
FD83B9AF27CF200A005E1583 /* SessionUtilitiesKitTests.xctest */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@ -3795,6 +3835,7 @@
8962372EEC51D3F56FE3A68A /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SignalUtilitiesKit.framework */, 8962372EEC51D3F56FE3A68A /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SignalUtilitiesKit.framework */,
D2C155B76C8483CB9A6EA9B4 /* Pods_SessionTests.framework */, D2C155B76C8483CB9A6EA9B4 /* Pods_SessionTests.framework */,
F1E0F51F17E4443731B94D32 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit_SessionMessagingKitTests.framework */, F1E0F51F17E4443731B94D32 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit_SessionMessagingKitTests.framework */,
A06DA296F93403656DFA7991 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit_SessionUtilitiesKitTests.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@ -3830,6 +3871,40 @@
path = "Message Requests"; path = "Message Requests";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
FD83B9B027CF200A005E1583 /* SessionUtilitiesKitTests */ = {
isa = PBXGroup;
children = (
FD83B9B927CF20A5005E1583 /* General */,
);
path = SessionUtilitiesKitTests;
sourceTree = "<group>";
};
FD83B9B927CF20A5005E1583 /* General */ = {
isa = PBXGroup;
children = (
FD83B9BA27CF20AF005E1583 /* SessionIdSpec.swift */,
);
path = General;
sourceTree = "<group>";
};
FD83B9BC27CF2215005E1583 /* SharedTest */ = {
isa = PBXGroup;
children = (
FD83B9BD27CF2243005E1583 /* TestConstants.swift */,
);
path = SharedTest;
sourceTree = "<group>";
};
FD83B9C127CF33EE005E1583 /* Models */ = {
isa = PBXGroup;
children = (
FD83B9C227CF33F7005E1583 /* ServerSpec.swift */,
FD83B9C427CF3E2A005E1583 /* OpenGroupSpec.swift */,
FD83B9C627CF3F10005E1583 /* CapabilitiesSpec.swift */,
);
path = Models;
sourceTree = "<group>";
};
FD88BAD727A7438E00BBC442 /* Views */ = { FD88BAD727A7438E00BBC442 /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -3868,6 +3943,7 @@
FDC4386227B4D94E00C60D73 /* OGMessage.swift */, FDC4386227B4D94E00C60D73 /* OGMessage.swift */,
FDC438C627BB6DF000C60D73 /* DirectMessage.swift */, FDC438C627BB6DF000C60D73 /* DirectMessage.swift */,
FDC438C827BB706500C60D73 /* SendDirectMessageRequest.swift */, FDC438C827BB706500C60D73 /* SendDirectMessageRequest.swift */,
FD83B9C827D0487A005E1583 /* SendDirectMessageResponse.swift */,
FDC438A327BB107F00C60D73 /* UserBanRequest.swift */, FDC438A327BB107F00C60D73 /* UserBanRequest.swift */,
FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */, FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */,
FDC438A927BB12BB00C60D73 /* UserModeratorRequest.swift */, FDC438A927BB12BB00C60D73 /* UserModeratorRequest.swift */,
@ -3940,7 +4016,8 @@
FDC4389827BA001800C60D73 /* Open Groups */ = { FDC4389827BA001800C60D73 /* Open Groups */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FDC4389927BA002500C60D73 /* OpenGroupAPIV2Tests.swift */, FD83B9C127CF33EE005E1583 /* Models */,
FDC4389927BA002500C60D73 /* OpenGroupAPISpec.swift */,
); );
path = "Open Groups"; path = "Open Groups";
sourceTree = "<group>"; sourceTree = "<group>";
@ -4305,6 +4382,26 @@
productReference = D221A089169C9E5E00537ABF /* Session.app */; productReference = D221A089169C9E5E00537ABF /* Session.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
FD83B9AE27CF200A005E1583 /* SessionUtilitiesKitTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = FD83B9B627CF200A005E1583 /* Build configuration list for PBXNativeTarget "SessionUtilitiesKitTests" */;
buildPhases = (
96A0691CEB0B517292629903 /* [CP] Check Pods Manifest.lock */,
FD83B9AB27CF200A005E1583 /* Sources */,
FD83B9AC27CF200A005E1583 /* Frameworks */,
FD83B9AD27CF200A005E1583 /* Resources */,
71F2A4CB38075EC3A9D35AE3 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
FD83B9B527CF200A005E1583 /* PBXTargetDependency */,
);
name = SessionUtilitiesKitTests;
productName = SessionUtilitiesKitTests;
productReference = FD83B9AF27CF200A005E1583 /* SessionUtilitiesKitTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
FDC4388D27B9FFC700C60D73 /* SessionMessagingKitTests */ = { FDC4388D27B9FFC700C60D73 /* SessionMessagingKitTests */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = FDC4389527B9FFC700C60D73 /* Build configuration list for PBXNativeTarget "SessionMessagingKitTests" */; buildConfigurationList = FDC4389527B9FFC700C60D73 /* Build configuration list for PBXNativeTarget "SessionMessagingKitTests" */;
@ -4423,6 +4520,9 @@
}; };
}; };
}; };
FD83B9AE27CF200A005E1583 = {
CreatedOnToolsVersion = 13.2.1;
};
FDC4388D27B9FFC700C60D73 = { FDC4388D27B9FFC700C60D73 = {
CreatedOnToolsVersion = 13.2.1; CreatedOnToolsVersion = 13.2.1;
}; };
@ -4471,6 +4571,7 @@
C3C2A59E255385C100C340D1 /* SessionSnodeKit */, C3C2A59E255385C100C340D1 /* SessionSnodeKit */,
C3C2A678255388CC00C340D1 /* SessionUtilitiesKit */, C3C2A678255388CC00C340D1 /* SessionUtilitiesKit */,
FDC4388D27B9FFC700C60D73 /* SessionMessagingKitTests */, FDC4388D27B9FFC700C60D73 /* SessionMessagingKitTests */,
FD83B9AE27CF200A005E1583 /* SessionUtilitiesKitTests */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@ -4595,6 +4696,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
FD83B9AD27CF200A005E1583 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
FDC4388C27B9FFC700C60D73 /* Resources */ = { FDC4388C27B9FFC700C60D73 /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -4702,6 +4810,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
71F2A4CB38075EC3A9D35AE3 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
7D43E8AB603234C5ADEF2812 /* [CP] Embed Pods Frameworks */ = { 7D43E8AB603234C5ADEF2812 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -4763,6 +4888,28 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
96A0691CEB0B517292629903 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
A067C0B8A52FC6C6FDA49939 /* [CP] Check Pods Manifest.lock */ = { A067C0B8A52FC6C6FDA49939 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -5291,6 +5438,7 @@
FDC4386B27B4E88F00C60D73 /* BatchRequestInfo.swift in Sources */, FDC4386B27B4E88F00C60D73 /* BatchRequestInfo.swift in Sources */,
FDC4385127B4807400C60D73 /* QueryParam.swift in Sources */, FDC4385127B4807400C60D73 /* QueryParam.swift in Sources */,
C32C5B62256DC333003C73A2 /* OWSDisappearingConfigurationUpdateInfoMessage.m in Sources */, C32C5B62256DC333003C73A2 /* OWSDisappearingConfigurationUpdateInfoMessage.m in Sources */,
FD83B9C927D0487A005E1583 /* SendDirectMessageResponse.swift in Sources */,
C352A2F525574B4700338F3E /* Job.swift in Sources */, C352A2F525574B4700338F3E /* Job.swift in Sources */,
FDC4385727B484B700C60D73 /* LegacyFileUploadResponse.swift in Sources */, FDC4385727B484B700C60D73 /* LegacyFileUploadResponse.swift in Sources */,
FDC4385B27B485DE00C60D73 /* LegacyFileDownloadResponse.swift in Sources */, FDC4385B27B485DE00C60D73 /* LegacyFileDownloadResponse.swift in Sources */,
@ -5477,15 +5625,28 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
FD83B9AB27CF200A005E1583 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FD83B9BF27CF2294005E1583 /* TestConstants.swift in Sources */,
FD83B9BB27CF20AF005E1583 /* SessionIdSpec.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
FDC4388A27B9FFC700C60D73 /* Sources */ = { FDC4388A27B9FFC700C60D73 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
FD859EFA27C2F5C500510D0C /* TestGenericHash.swift in Sources */, FD859EFA27C2F5C500510D0C /* TestGenericHash.swift in Sources */,
FD83B9C327CF33F7005E1583 /* ServerSpec.swift in Sources */,
FD83B9C727CF3F10005E1583 /* CapabilitiesSpec.swift in Sources */,
FD859EF827C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift in Sources */, FD859EF827C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift in Sources */,
FD859EF427C2F49200510D0C /* TestSodium.swift in Sources */, FD859EF427C2F49200510D0C /* TestSodium.swift in Sources */,
FD859EFC27C2F60700510D0C /* TestEd25519.swift in Sources */, FD859EFC27C2F60700510D0C /* TestEd25519.swift in Sources */,
FDC4389A27BA002500C60D73 /* OpenGroupAPIV2Tests.swift in Sources */, FD83B9C027CF2294005E1583 /* TestConstants.swift in Sources */,
FDC4389A27BA002500C60D73 /* OpenGroupAPISpec.swift in Sources */,
FD83B9C527CF3E2A005E1583 /* OpenGroupSpec.swift in Sources */,
FDC438BD27BB2AB400C60D73 /* Mockable.swift in Sources */, FDC438BD27BB2AB400C60D73 /* Mockable.swift in Sources */,
FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */, FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */,
FDC4389D27BA01F000C60D73 /* TestStorage.swift in Sources */, FDC4389D27BA01F000C60D73 /* TestStorage.swift in Sources */,
@ -5575,6 +5736,12 @@
target = C33FD9AA255A548A00E217F9 /* SignalUtilitiesKit */; target = C33FD9AA255A548A00E217F9 /* SignalUtilitiesKit */;
targetProxy = C3D90A7025773A44002C9DF5 /* PBXContainerItemProxy */; targetProxy = C3D90A7025773A44002C9DF5 /* PBXContainerItemProxy */;
}; };
FD83B9B527CF200A005E1583 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
platformFilter = ios;
target = C3C2A678255388CC00C340D1 /* SessionUtilitiesKit */;
targetProxy = FD83B9B427CF200A005E1583 /* PBXContainerItemProxy */;
};
FDC4386F27B4E90300C60D73 /* PBXTargetDependency */ = { FDC4386F27B4E90300C60D73 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = C3C2A678255388CC00C340D1 /* SessionUtilitiesKit */; target = C3C2A678255388CC00C340D1 /* SessionUtilitiesKit */;
@ -6936,6 +7103,112 @@
}; };
name = "App Store Release"; name = "App Store Release";
}; };
FD83B9B727CF200A005E1583 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AAC5927BC89F6F265332C324 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests.debug.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.2;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = io.oxen.SessionUtilitiesKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
FD83B9B827CF200A005E1583 /* App Store Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A87BC4AF5BA3E3DE713B08E5 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit-SessionUtilitiesKitTests.app store release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.2;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = io.oxen.SessionUtilitiesKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = "App Store Release";
};
FDC4389627B9FFC700C60D73 /* Debug */ = { FDC4389627B9FFC700C60D73 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 949F269926ABA08C125DCA9D /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionMessagingKit-SessionMessagingKitTests.debug.xcconfig */; baseConfigurationReference = 949F269926ABA08C125DCA9D /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionMessagingKit-SessionMessagingKitTests.debug.xcconfig */;
@ -7126,6 +7399,15 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = "App Store Release"; defaultConfigurationName = "App Store Release";
}; };
FD83B9B627CF200A005E1583 /* Build configuration list for PBXNativeTarget "SessionUtilitiesKitTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
FD83B9B727CF200A005E1583 /* Debug */,
FD83B9B827CF200A005E1583 /* App Store Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "App Store Release";
};
FDC4389527B9FFC700C60D73 /* Build configuration list for PBXNativeTarget "SessionMessagingKitTests" */ = { FDC4389527B9FFC700C60D73 /* Build configuration list for PBXNativeTarget "SessionMessagingKitTests" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (

View File

@ -105,7 +105,9 @@
</CodeCoverageTargets> </CodeCoverageTargets>
<Testables> <Testables>
<TestableReference <TestableReference
skipped = "NO"> skipped = "NO"
parallelizable = "YES"
testExecutionOrdering = "random">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "FDC4388D27B9FFC700C60D73" BlueprintIdentifier = "FDC4388D27B9FFC700C60D73"
@ -114,6 +116,18 @@
ReferencedContainer = "container:Session.xcodeproj"> ReferencedContainer = "container:Session.xcodeproj">
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES"
testExecutionOrdering = "random">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FD83B9AE27CF200A005E1583"
BuildableName = "SessionUtilitiesKitTests.xctest"
BlueprintName = "SessionUtilitiesKitTests"
ReferencedContainer = "container:Session.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction

View File

@ -40,7 +40,9 @@
</CodeCoverageTargets> </CodeCoverageTargets>
<Testables> <Testables>
<TestableReference <TestableReference
skipped = "NO"> skipped = "NO"
parallelizable = "YES"
testExecutionOrdering = "random">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "FDC4388D27B9FFC700C60D73" BlueprintIdentifier = "FDC4388D27B9FFC700C60D73"

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1320"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C3C2A678255388CC00C340D1"
BuildableName = "SessionUtilitiesKit.framework"
BlueprintName = "SessionUtilitiesKit"
ReferencedContainer = "container:Session.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C3C2A678255388CC00C340D1"
BuildableName = "SessionUtilitiesKit.framework"
BlueprintName = "SessionUtilitiesKit"
ReferencedContainer = "container:Session.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES"
testExecutionOrdering = "random">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FD83B9AE27CF200A005E1583"
BuildableName = "SessionUtilitiesKitTests.xctest"
BlueprintName = "SessionUtilitiesKitTests"
ReferencedContainer = "container:Session.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "App Store Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C3C2A678255388CC00C340D1"
BuildableName = "SessionUtilitiesKit.framework"
BlueprintName = "SessionUtilitiesKit"
ReferencedContainer = "container:Session.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "App Store Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -490,11 +490,17 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
// MARK: View Item Interaction // MARK: View Item Interaction
func handleViewItemLongPressed(_ viewItem: ConversationViewItem) { func handleViewItemLongPressed(_ viewItem: ConversationViewItem) {
// Show the context menu if applicable // Note: There seems to be some odd behaviours with how the UI and the data update and as a result the `viewItem` the
guard let index = viewItems.firstIndex(where: { $0 === viewItem }), // user interacted with can be a different instance from what is in `viewItems` (likely the data updated but the user
// interacted with old UI which had cached the old value)
//
// By using an equality check on the interaction we avoid this odd behaviour
guard let index = viewItems.firstIndex(where: { $0.interaction == viewItem.interaction }),
let cell = messagesTableView.cellForRow(at: IndexPath(row: index, section: 0)) as? VisibleMessageCell, let cell = messagesTableView.cellForRow(at: IndexPath(row: index, section: 0)) as? VisibleMessageCell,
let snapshot = cell.bubbleView.snapshotView(afterScreenUpdates: false), contextMenuWindow == nil, let snapshot = cell.bubbleView.snapshotView(afterScreenUpdates: false), contextMenuWindow == nil,
!ContextMenuVC.actions(for: viewItem, delegate: self).isEmpty else { return } !ContextMenuVC.actions(for: viewItem, delegate: self).isEmpty else { return }
// Show the context menu if applicable
UIImpactFeedbackGenerator(style: .heavy).impactOccurred() UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
let frame = cell.convert(cell.bubbleView.frame, to: UIApplication.shared.keyWindow!) let frame = cell.convert(cell.bubbleView.frame, to: UIApplication.shared.keyWindow!)
let window = ContextMenuWindow() let window = ContextMenuWindow()

View File

@ -72,7 +72,7 @@ final class JoinOpenGroupModal : Modal {
Storage.shared.write { [presentingViewController = self.presentingViewController!] transaction in Storage.shared.write { [presentingViewController = self.presentingViewController!] transaction in
OpenGroupManager.shared OpenGroupManager.shared
.add(roomToken: room, server: server, publicKey: publicKey, using: transaction) .add(roomToken: room, server: server, publicKey: publicKey, isConfigMessage: false, using: transaction as! YapDatabaseReadWriteTransaction)
.done(on: DispatchQueue.main) { _ in .done(on: DispatchQueue.main) { _ in
let appDelegate = UIApplication.shared.delegate as! AppDelegate let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() // FIXME: It's probably cleaner to do this inside addOpenGroup(...) appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() // FIXME: It's probably cleaner to do this inside addOpenGroup(...)

View File

@ -142,7 +142,7 @@ final class JoinOpenGroupVC : BaseVC, UIPageViewControllerDataSource, UIPageView
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
Storage.shared.write { transaction in Storage.shared.write { transaction in
OpenGroupManager.shared OpenGroupManager.shared
.add(roomToken: roomToken, server: server, publicKey: publicKey, using: transaction) .add(roomToken: roomToken, server: server, publicKey: publicKey, isConfigMessage: false, using: transaction as! YapDatabaseReadWriteTransaction)
.done(on: DispatchQueue.main) { [weak self] _ in .done(on: DispatchQueue.main) { [weak self] _ in
self?.presentingViewController?.dismiss(animated: true, completion: nil) self?.presentingViewController?.dismiss(animated: true, completion: nil)
let appDelegate = UIApplication.shared.delegate as! AppDelegate let appDelegate = UIApplication.shared.delegate as! AppDelegate

View File

@ -5,9 +5,9 @@ import Foundation
enum Header: String { enum Header: String {
case authorization = "Authorization" case authorization = "Authorization"
case contentType = "Content-Type" case contentType = "Content-Type"
case contentDisposition = "Content-Disposition"
case room = "Room" // TODO: Confirm this is needed case room = "Room" // TODO: Confirm this is needed
case fileName = "X-Filename"
case sogsPubKey = "X-SOGS-Pubkey" case sogsPubKey = "X-SOGS-Pubkey"
case sogsNonce = "X-SOGS-Nonce" case sogsNonce = "X-SOGS-Nonce"

View File

@ -124,7 +124,7 @@ extension OpenGroupAPI.BatchSubResponse {
code: try container.decode(Int32.self, forKey: .code), code: try container.decode(Int32.self, forKey: .code),
headers: try container.decode([String: String].self, forKey: .headers), headers: try container.decode([String: String].self, forKey: .headers),
body: body, body: body,
failedToParseBody: (body == nil && T.self != OpenGroupAPI.NoResponse.self) failedToParseBody: (body == nil && T.self != OpenGroupAPI.NoResponse.self && !(T.self is ExpressibleByNilLiteral.Type))
) )
} }
} }

View File

@ -16,11 +16,11 @@ extension OpenGroupAPI {
/// The unique integer message id /// The unique integer message id
public let id: Int64 public let id: Int64
/// The (blinded) Session ID of the sender of the message (null on outgoing messages) /// The (blinded) Session ID of the sender of the message
public let sender: String? public let sender: String
/// The (blinded) Session ID of the recipient of the message (null on incoming message) /// The (blinded) Session ID of the recipient of the message
public let recipient: String? public let recipient: String
/// Unix timestamp when the message was received by SOGS /// Unix timestamp when the message was received by SOGS
public let posted: TimeInterval public let posted: TimeInterval

View File

@ -0,0 +1,30 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPI {
public struct SendDirectMessageResponse: Codable {
enum CodingKeys: String, CodingKey {
case id
case sender
case recipient
case posted = "posted_at"
case expires = "expires_at"
}
/// The unique integer message id
public let id: Int64
/// The (blinded) Session ID of the sender of the message
public let sender: String
/// The (blinded) Session ID of the recipient of the message
public let recipient: String
/// Unix timestamp when the message was received by SOGS
public let posted: TimeInterval
/// Unix timestamp when SOGS will expire and delete the message
public let expires: TimeInterval
}
}

View File

@ -228,6 +228,11 @@ public final class OpenGroupAPI: NSObject {
} }
/// Returns the details of a single room /// Returns the details of a single room
///
/// **Note:** This is the direct request to retrieve a room so should only be called from either the `poll()` or `joinRoom()` methods, in order to call
/// this directly remove the `@available` line and make sure to route the response of this method to the `OpenGroupManager.handlePollInfo`
/// method to ensure things are processed correctly
@available(*, unavailable, message: "Avoid using this directly, use the pre-built `poll()` method instead")
public static func room(for roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Room)> { public static func room(for roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Room)> {
let request: Request = Request<NoBody>( let request: Request = Request<NoBody>(
server: server, server: server,
@ -246,7 +251,7 @@ public final class OpenGroupAPI: NSObject {
/// **Note:** This is the direct request to retrieve room updates so should be retrieved automatically from the `poll()` method, in order to call /// **Note:** This is the direct request to retrieve room updates so should be retrieved automatically from the `poll()` method, in order to call
/// this directly remove the `@available` line and make sure to route the response of this method to the `OpenGroupManager.handlePollInfo` /// this directly remove the `@available` line and make sure to route the response of this method to the `OpenGroupManager.handlePollInfo`
/// method to ensure things are processed correctly /// method to ensure things are processed correctly
@available(*, unavailable, message: "Avoid using this directly, use the pre-build `poll()` method instead") @available(*, unavailable, message: "Avoid using this directly, use the pre-built `poll()` method instead")
public static func roomPollInfo(lastUpdated: Int64, for roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, RoomPollInfo)> { public static func roomPollInfo(lastUpdated: Int64, for roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, RoomPollInfo)> {
let request: Request = Request<NoBody>( let request: Request = Request<NoBody>(
server: server, server: server,
@ -257,6 +262,66 @@ public final class OpenGroupAPI: NSObject {
.decoded(as: RoomPollInfo.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies) .decoded(as: RoomPollInfo.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
} }
/// This is a convenience method which constructs a `/sequence` of the `capabilities` and `room` requests, refer to those
/// methods for the documented behaviour of each method
public static func capabilitiesAndRoom(
for roomToken: String,
on server: String,
using dependencies: Dependencies = Dependencies()
) -> Promise<(capabilities: (OnionRequestResponseInfoType, Capabilities?), room: (OnionRequestResponseInfoType, Room?))> {
let requestResponseType: [BatchRequestInfoType] = [
// Get the latest capabilities for the server (in case it's a new server or the cached ones are stale)
BatchRequestInfo(
request: Request<NoBody>(
server: server,
endpoint: .capabilities
),
responseType: Capabilities.self
),
// And the room info
BatchRequestInfo(
request: Request<NoBody>(
server: server,
endpoint: .room(roomToken)
),
responseType: Room.self
)
]
return sequence(server, requests: requestResponseType, using: dependencies)
.map { response -> (capabilities: (OnionRequestResponseInfoType, Capabilities?), room: (OnionRequestResponseInfoType, Room?)) in
var capabilities: (OnionRequestResponseInfoType, Capabilities?)? = nil
var room: (OnionRequestResponseInfoType, Room?)? = nil
try response.forEach { (endpoint: Endpoint, endpointResponse: (info: OnionRequestResponseInfoType, data: Codable?)) in
switch endpoint {
case .capabilities:
guard let responseData: BatchSubResponse<Capabilities> = endpointResponse.data as? BatchSubResponse<Capabilities>, let responseBody: Capabilities = responseData.body else {
throw Error.parsingFailed
}
capabilities = (endpointResponse.info, responseBody)
case .room:
guard let responseData: OpenGroupAPI.BatchSubResponse<OpenGroupAPI.Room> = endpointResponse.data as? OpenGroupAPI.BatchSubResponse<OpenGroupAPI.Room>, let responseBody: OpenGroupAPI.Room = responseData.body else {
throw Error.parsingFailed
}
room = (endpointResponse.info, responseBody)
default: break // No custom handling needed
}
}
guard let capabilities: (OnionRequestResponseInfoType, Capabilities?) = capabilities, let room: (OnionRequestResponseInfoType, Room?) = room else {
throw Error.parsingFailed
}
return (capabilities, room)
}
}
// MARK: - Messages // MARK: - Messages
/// Posts a new message to a room /// Posts a new message to a room
@ -289,6 +354,15 @@ public final class OpenGroupAPI: NSObject {
return send(request, using: dependencies) return send(request, using: dependencies)
.decoded(as: Message.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies) .decoded(as: Message.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
.map { response, message in
// Store the 'message.posted' timestamp to prevent the sent message getting duplicated when it is later retrieved
dependencies.storage.write { transaction in
// The `posted` value is in seconds but we sent it in ms so need that for de-duping
dependencies.storage.addReceivedMessageTimestamp(UInt64(floor(message.posted * 1000)), using: transaction)
}
return (response, message)
}
} }
/// Returns a single message by ID /// Returns a single message by ID
@ -334,7 +408,6 @@ public final class OpenGroupAPI: NSObject {
return send(request, using: dependencies) return send(request, using: dependencies)
} }
// TODO: Need to test this once the API has been implemented
public static func messageDelete( public static func messageDelete(
_ id: Int64, _ id: Int64,
in roomToken: String, in roomToken: String,
@ -347,19 +420,13 @@ public final class OpenGroupAPI: NSObject {
endpoint: .roomMessageIndividual(roomToken, id: id) endpoint: .roomMessageIndividual(roomToken, id: id)
) )
// TODO: Handle custom response info? Need to let the OpenGroupManager know to delete the message?
// TODO: !!!! This is currently broken - looks like there isn't currently a DELETE endpoint (but there should be)
return send(request, using: dependencies) return send(request, using: dependencies)
.map { response in
print("RAWR")
return response
}
} }
/// **Note:** This is the direct request to retrieve recent messages so should be retrieved automatically from the `poll()` method, in order to call /// **Note:** This is the direct request to retrieve recent messages so should be retrieved automatically from the `poll()` method, in order to call
/// this directly remove the `@available` line and make sure to route the response of this method to the `OpenGroupManager.handleMessages` /// this directly remove the `@available` line and make sure to route the response of this method to the `OpenGroupManager.handleMessages`
/// method to ensure things are processed correctly /// method to ensure things are processed correctly
@available(*, unavailable, message: "Avoid using this directly, use the pre-build `poll()` method instead") @available(*, unavailable, message: "Avoid using this directly, use the pre-built `poll()` method instead")
public static func recentMessages(in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [Message])> { public static func recentMessages(in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [Message])> {
let request: Request = Request<NoBody>( let request: Request = Request<NoBody>(
server: server, server: server,
@ -375,7 +442,7 @@ public final class OpenGroupAPI: NSObject {
/// **Note:** This is the direct request to retrieve recent messages before a given message and is currently unused, in order to call this directly /// **Note:** This is the direct request to retrieve recent messages before a given message and is currently unused, in order to call this directly
/// remove the `@available` line and make sure to route the response of this method to the `OpenGroupManager.handleMessages` /// remove the `@available` line and make sure to route the response of this method to the `OpenGroupManager.handleMessages`
/// method to ensure things are processed correctly /// method to ensure things are processed correctly
@available(*, unavailable, message: "Avoid using this directly, use the pre-build `poll()` method instead") @available(*, unavailable, message: "Avoid using this directly, use the pre-built `poll()` method instead")
public static func messagesBefore(messageId: Int64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [Message])> { public static func messagesBefore(messageId: Int64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [Message])> {
// TODO: Do we need to be able to load old messages? // TODO: Do we need to be able to load old messages?
let request: Request = Request<NoBody>( let request: Request = Request<NoBody>(
@ -392,7 +459,7 @@ public final class OpenGroupAPI: NSObject {
/// **Note:** This is the direct request to retrieve messages since a given message `seqNo` so should be retrieved automatically from the /// **Note:** This is the direct request to retrieve messages since a given message `seqNo` so should be retrieved automatically from the
/// `poll()` method, in order to call this directly remove the `@available` line and make sure to route the response of this method to the /// `poll()` method, in order to call this directly remove the `@available` line and make sure to route the response of this method to the
/// `OpenGroupManager.handleMessages` method to ensure things are processed correctly /// `OpenGroupManager.handleMessages` method to ensure things are processed correctly
@available(*, unavailable, message: "Avoid using this directly, use the pre-build `poll()` method instead") @available(*, unavailable, message: "Avoid using this directly, use the pre-built `poll()` method instead")
public static func messagesSince(seqNo: Int64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [Message])> { public static func messagesSince(seqNo: Int64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [Message])> {
let request: Request = Request<NoBody>( let request: Request = Request<NoBody>(
server: server, server: server,
@ -463,7 +530,12 @@ public final class OpenGroupAPI: NSObject {
method: .post, method: .post,
server: server, server: server,
endpoint: .roomFile(roomToken), endpoint: .roomFile(roomToken),
headers: [ .fileName: fileName ].compactMapValues { $0 }, headers: [
.contentDisposition: [ "attachment", fileName.map { "filename=\"\($0)\"" } ]
.compactMap{ $0 }
.joined(separator: "; "),
.contentType: "application/octet-stream"
],
body: bytes body: bytes
) )
@ -478,7 +550,11 @@ public final class OpenGroupAPI: NSObject {
method: .post, method: .post,
server: server, server: server,
endpoint: .roomFileJson(roomToken), endpoint: .roomFileJson(roomToken),
headers: [ .fileName: fileName ].compactMapValues { $0 }, headers: [
.contentDisposition: [ "attachment", fileName.map { "filename=\"\($0)\"" } ]
.compactMap{ $0 }
.joined(separator: "; "),
],
body: base64EncodedString body: base64EncodedString
) )
@ -517,7 +593,7 @@ public final class OpenGroupAPI: NSObject {
/// **Note:** This is the direct request to retrieve DMs for a specific Open Group so should be retrieved automatically from the `poll()` /// **Note:** This is the direct request to retrieve DMs for a specific Open Group so should be retrieved automatically from the `poll()`
/// method, in order to call this directly remove the `@available` line and make sure to route the response of this method to the /// method, in order to call this directly remove the `@available` line and make sure to route the response of this method to the
/// `OpenGroupManager.handleDirectMessages` method to ensure things are processed correctly /// `OpenGroupManager.handleDirectMessages` method to ensure things are processed correctly
@available(*, unavailable, message: "Avoid using this directly, use the pre-build `poll()` method instead") @available(*, unavailable, message: "Avoid using this directly, use the pre-built `poll()` method instead")
public static func inbox(on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage]?)> { public static func inbox(on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage]?)> {
let request: Request = Request<NoBody>( let request: Request = Request<NoBody>(
server: server, server: server,
@ -533,7 +609,7 @@ public final class OpenGroupAPI: NSObject {
/// **Note:** This is the direct request to retrieve messages requests for a specific Open Group since a given messages so should be retrieved /// **Note:** This is the direct request to retrieve messages requests for a specific Open Group since a given messages so should be retrieved
/// automatically from the `poll()` method, in order to call this directly remove the `@available` line and make sure to route the response /// automatically from the `poll()` method, in order to call this directly remove the `@available` line and make sure to route the response
/// of this method to the `OpenGroupManager.handleDirectMessages` method to ensure things are processed correctly /// of this method to the `OpenGroupManager.handleDirectMessages` method to ensure things are processed correctly
@available(*, unavailable, message: "Avoid using this directly, use the pre-build `poll()` method instead") @available(*, unavailable, message: "Avoid using this directly, use the pre-built `poll()` method instead")
public static func inboxSince(id: Int64, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage]?)> { public static func inboxSince(id: Int64, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage]?)> {
let request: Request = Request<NoBody>( let request: Request = Request<NoBody>(
server: server, server: server,
@ -547,7 +623,7 @@ public final class OpenGroupAPI: NSObject {
/// Delivers a direct message to a user via their blinded Session ID /// Delivers a direct message to a user via their blinded Session ID
/// ///
/// The body of this request is a JSON object containing a message key with a value of the encrypted-then-base64-encoded message to deliver /// The body of this request is a JSON object containing a message key with a value of the encrypted-then-base64-encoded message to deliver
public static func send(_ ciphertext: Data, toInboxFor blindedSessionId: String, on server: String/*, with serverPublicKey: String*/, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Data?)> { public static func send(_ ciphertext: Data, toInboxFor blindedSessionId: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, SendDirectMessageResponse)> {
let requestBody: SendDirectMessageRequest = SendDirectMessageRequest( let requestBody: SendDirectMessageRequest = SendDirectMessageRequest(
message: ciphertext message: ciphertext
) )
@ -560,6 +636,16 @@ public final class OpenGroupAPI: NSObject {
) )
return send(request, using: dependencies) return send(request, using: dependencies)
.decoded(as: SendDirectMessageResponse.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
.map { response, message in
// Store the 'message.posted' timestamp to prevent the sent message getting duplicated when it is later retrieved
dependencies.storage.write { transaction in
// The `posted` value is in seconds but we sent it in ms so need that for de-duping
dependencies.storage.addReceivedMessageTimestamp(UInt64(floor(message.posted * 1000)), using: transaction)
}
return (response, message)
}
} }
/// Retrieves all of the user's sent DMs (up to limit) /// Retrieves all of the user's sent DMs (up to limit)
@ -567,7 +653,7 @@ public final class OpenGroupAPI: NSObject {
/// **Note:** This is the direct request to retrieve DMs sent by the user for a specific Open Group so should be retrieved automatically /// **Note:** This is the direct request to retrieve DMs sent by the user for a specific Open Group so should be retrieved automatically
/// from the `poll()` method, in order to call this directly remove the `@available` line and make sure to route the response of /// from the `poll()` method, in order to call this directly remove the `@available` line and make sure to route the response of
/// this method to the `OpenGroupManager.handleDirectMessages` method to ensure things are processed correctly /// this method to the `OpenGroupManager.handleDirectMessages` method to ensure things are processed correctly
@available(*, unavailable, message: "Avoid using this directly, use the pre-build `poll()` method instead") @available(*, unavailable, message: "Avoid using this directly, use the pre-built `poll()` method instead")
public static func outbox(on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage]?)> { public static func outbox(on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage]?)> {
let request: Request = Request<NoBody>( let request: Request = Request<NoBody>(
server: server, server: server,
@ -583,7 +669,7 @@ public final class OpenGroupAPI: NSObject {
/// **Note:** This is the direct request to retrieve messages requests sent by the user for a specific Open Group since a given messages so /// **Note:** This is the direct request to retrieve messages requests sent by the user for a specific Open Group since a given messages so
/// should be retrieved automatically from the `poll()` method, in order to call this directly remove the `@available` line and make sure /// should be retrieved automatically from the `poll()` method, in order to call this directly remove the `@available` line and make sure
/// to route the response of this method to the `OpenGroupManager.handleDirectMessages` method to ensure things are processed correctly /// to route the response of this method to the `OpenGroupManager.handleDirectMessages` method to ensure things are processed correctly
@available(*, unavailable, message: "Avoid using this directly, use the pre-build `poll()` method instead") @available(*, unavailable, message: "Avoid using this directly, use the pre-built `poll()` method instead")
public static func outboxSince(id: Int64, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage]?)> { public static func outboxSince(id: Int64, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage]?)> {
let request: Request = Request<NoBody>( let request: Request = Request<NoBody>(
server: server, server: server,

View File

@ -1,6 +1,7 @@
import PromiseKit import PromiseKit
import Sodium import Sodium
import SessionUtilitiesKit import SessionUtilitiesKit
import SessionSnodeKit
@objc(SNOpenGroupManager) @objc(SNOpenGroupManager)
public final class OpenGroupManager: NSObject { public final class OpenGroupManager: NSObject {
@ -40,28 +41,54 @@ public final class OpenGroupManager: NSObject {
// MARK: - Adding & Removing // MARK: - Adding & Removing
public func add(roomToken: String, server: String, publicKey: String, using transaction: Any) -> Promise<Void> { public func add(roomToken: String, server: String, publicKey: String, isConfigMessage: Bool, using transaction: YapDatabaseReadWriteTransaction, dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<Void> {
let storage = Storage.shared // If we are currently polling for this server and already have a TSGroupThread for this room the do nothing
let groupId: Data = LKGroupUtilities.getEncodedOpenGroupIDAsData("\(server).\(roomToken)")
if OpenGroupManager.shared.pollers[server] != nil && TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupId), transaction: transaction) != nil {
SNLog("Ignoring join open group attempt, user initiated: \(!isConfigMessage)")
return Promise.value(())
}
// Clear any existing data if needed // Clear any existing data if needed
storage.removeOpenGroupSequenceNumber(for: roomToken, on: server, using: transaction) dependencies.storage.removeOpenGroupSequenceNumber(for: roomToken, on: server, using: transaction)
// Store the public key // Store the public key
storage.setOpenGroupPublicKey(for: server, to: publicKey, using: transaction) dependencies.storage.setOpenGroupPublicKey(for: server, to: publicKey, using: transaction)
let (promise, seal) = Promise<Void>.pending() let (promise, seal) = Promise<Void>.pending()
let transaction = transaction as! YapDatabaseReadWriteTransaction
transaction.addCompletionQueue(DispatchQueue.global(qos: .userInitiated)) { transaction.addCompletionQueue(DispatchQueue.global(qos: .userInitiated)) {
OpenGroupAPI.room(for: roomToken, on: server) OpenGroupAPI.capabilitiesAndRoom(for: roomToken, on: server, using: dependencies)
.done(on: DispatchQueue.global(qos: .userInitiated)) { _, room in .done(on: DispatchQueue.global(qos: .userInitiated)) { (capabilitiesResponse: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Capabilities?), roomResponse: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Room?)) in
OpenGroupManager.handleRoom( guard let capabilities: OpenGroupAPI.Capabilities = capabilitiesResponse.data, let room: OpenGroupAPI.Room = roomResponse.data else {
room, SNLog("Failed to join open group due to invalid data.")
publicKey: publicKey, seal.reject(OpenGroupAPI.Error.generic)
for: roomToken, return
on: server }
) {
seal.fulfill(()) dependencies.storage.write { anyTransactionas in
guard let transaction: YapDatabaseReadWriteTransaction = anyTransactionas as? YapDatabaseReadWriteTransaction else { return }
// Store the capabilities first
OpenGroupManager.handleCapabilities(
capabilities,
on: server,
using: transaction,
dependencies: dependencies
)
// Then the room
OpenGroupManager.handleRoom(
room,
publicKey: publicKey,
for: roomToken,
on: server,
using: transaction,
dependencies: dependencies
) {
seal.fulfill(())
}
} }
} }
.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in .catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
@ -109,79 +136,15 @@ public final class OpenGroupManager: NSObject {
internal static func handleCapabilities( internal static func handleCapabilities(
_ capabilities: OpenGroupAPI.Capabilities, _ capabilities: OpenGroupAPI.Capabilities,
on server: String, on server: String,
using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies() using transaction: YapDatabaseReadWriteTransaction,
dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()
) { ) {
dependencies.storage.write { transaction in let updatedServer: OpenGroupAPI.Server = OpenGroupAPI.Server(
let updatedServer: OpenGroupAPI.Server = OpenGroupAPI.Server( name: server,
name: server, capabilities: capabilities
capabilities: capabilities )
)
dependencies.storage.setOpenGroupServer(updatedServer, using: transaction)
}
}
internal static func handleMessages(
_ messages: [OpenGroupAPI.Message],
for roomToken: String,
on server: String,
isBackgroundPoll: Bool,
using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()
) {
// 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).\(roomToken)"
let sortedMessages: [OpenGroupAPI.Message] = messages
.sorted { lhs, rhs in lhs.id < rhs.id }
let seqNo: Int64 = (sortedMessages.map { $0.seqNo }.max() ?? 0)
dependencies.storage.write { transaction in dependencies.storage.setOpenGroupServer(updatedServer, using: transaction)
var messageServerIDsToRemove: [UInt64] = []
// Update the 'openGroupSequenceNumber' value (Note: SOGS V4 uses the 'seqNo' instead of the 'serverId')
dependencies.storage.setOpenGroupSequenceNumber(for: roomToken, on: server, to: seqNo, using: transaction)
// Process the messages
sortedMessages.forEach { message in
guard let base64EncodedString: String = message.base64EncodedData, let data = Data(base64Encoded: base64EncodedString), let sender: String = message.sender else {
// A message with no data has been deleted so add it to the list to remove
messageServerIDsToRemove.append(UInt64(message.id))
return
}
// 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(data)
envelope.setSource(sender)
do {
let data = try envelope.buildSerializedData()
let (message, proto) = try MessageReceiver.parse(data, openGroupMessageServerID: UInt64(message.id), 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).")
}
}
// Handle any deletions that are needed
guard !messageServerIDsToRemove.isEmpty else { return }
guard let transaction: YapDatabaseReadWriteTransaction = transaction as? YapDatabaseReadWriteTransaction else { return }
guard let threadID = dependencies.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: TSMessage = interaction as? TSMessage, messageServerIDsToRemove.contains(message.openGroupServerMessageID) else {
return
}
messagesToRemove.append(message)
}
messagesToRemove.forEach { $0.remove(with: transaction) }
}
} }
internal static func handleRoom( internal static func handleRoom(
@ -189,7 +152,8 @@ public final class OpenGroupManager: NSObject {
publicKey: String, publicKey: String,
for roomToken: String, for roomToken: String,
on server: String, on server: String,
using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies(), using transaction: YapDatabaseReadWriteTransaction,
dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies(),
completion: (() -> ())? = nil completion: (() -> ())? = nil
) { ) {
OpenGroupManager.handlePollInfo( OpenGroupManager.handlePollInfo(
@ -197,7 +161,8 @@ public final class OpenGroupManager: NSObject {
publicKey: publicKey, publicKey: publicKey,
for: roomToken, for: roomToken,
on: server, on: server,
using: dependencies, using: transaction,
dependencies: dependencies,
completion: completion completion: completion
) )
} }
@ -207,7 +172,8 @@ public final class OpenGroupManager: NSObject {
publicKey maybePublicKey: String?, publicKey maybePublicKey: String?,
for roomToken: String, for roomToken: String,
on server: String, on server: String,
using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies(), using transaction: YapDatabaseReadWriteTransaction,
dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies(),
completion: (() -> ())? = nil completion: (() -> ())? = nil
) { ) {
// Create the open group model and get or create the thread // Create the open group model and get or create the thread
@ -225,90 +191,148 @@ public final class OpenGroupManager: NSObject {
var maybeUpdatedModel: TSGroupModel? = nil var maybeUpdatedModel: TSGroupModel? = nil
// Store/Update everything // Store/Update everything
dependencies.storage.write( let thread = TSGroupThread.getOrCreateThread(with: initialModel, transaction: transaction)
with: { transaction in let existingOpenGroup: OpenGroup? = thread.uniqueId.flatMap { uniqueId -> OpenGroup? in
let transaction = transaction as! YapDatabaseReadWriteTransaction dependencies.storage.getOpenGroup(for: uniqueId)
let thread = TSGroupThread.getOrCreateThread(with: initialModel, transaction: transaction) }
let existingOpenGroup: OpenGroup? = thread.uniqueId.flatMap { uniqueId -> OpenGroup? in
dependencies.storage.getOpenGroup(for: uniqueId)
}
guard let threadUniqueId: String = thread.uniqueId else { return } guard let threadUniqueId: String = thread.uniqueId else { return }
guard let publicKey: String = (maybePublicKey ?? existingOpenGroup?.publicKey) else { return } guard let publicKey: String = (maybePublicKey ?? existingOpenGroup?.publicKey) else { return }
let updatedModel: TSGroupModel = TSGroupModel( let updatedModel: TSGroupModel = TSGroupModel(
title: (pollInfo.details?.name ?? thread.groupModel.groupName), title: (pollInfo.details?.name ?? thread.groupModel.groupName),
memberIds: Array(Set(thread.groupModel.groupMemberIds).inserting(userPublicKey)), memberIds: Array(Set(thread.groupModel.groupMemberIds).inserting(userPublicKey)),
image: thread.groupModel.groupImage, image: thread.groupModel.groupImage,
groupId: groupId, groupId: groupId,
groupType: .openGroup, groupType: .openGroup,
adminIds: (pollInfo.details?.admins ?? thread.groupModel.groupAdminIds), adminIds: (pollInfo.details?.admins ?? thread.groupModel.groupAdminIds),
moderatorIds: (pollInfo.details?.moderators ?? thread.groupModel.groupModeratorIds) moderatorIds: (pollInfo.details?.moderators ?? thread.groupModel.groupModeratorIds)
)
maybeUpdatedModel = updatedModel
let updatedOpenGroup: OpenGroup = OpenGroup(
server: server,
room: pollInfo.token,
publicKey: publicKey,
name: (pollInfo.details?.name ?? thread.name()),
groupDescription: (pollInfo.details?.description ?? existingOpenGroup?.description),
imageID: (pollInfo.details?.imageId.map { "\($0)" } ?? existingOpenGroup?.imageID),
infoUpdates: ((pollInfo.details?.infoUpdates ?? existingOpenGroup?.infoUpdates) ?? 0)
)
// - Thread changes
thread.shouldBeVisible = true
thread.groupModel = updatedModel
thread.save(with: transaction)
// - Open Group changes
dependencies.storage.setOpenGroup(updatedOpenGroup, for: threadUniqueId, using: transaction)
// - User Count
dependencies.storage.setUserCount(
to: UInt64(pollInfo.activeUsers),
forOpenGroupWithID: updatedOpenGroup.id,
using: transaction
)
},
completion: {
// Start the poller if needed
if OpenGroupManager.shared.pollers[server] == nil {
OpenGroupManager.shared.pollers[server] = OpenGroupAPI.Poller(for: server)
OpenGroupManager.shared.pollers[server]?.startIfNeeded()
}
// - Moderators
if let moderators: [String] = (pollInfo.details?.moderators ?? maybeUpdatedModel?.groupModeratorIds) {
OpenGroupManager.moderators[server] = (OpenGroupManager.moderators[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))
}
// - Room image (if there is one)
if let imageId: Int64 = pollInfo.details?.imageId {
OpenGroupManager.roomImage(imageId, for: roomToken, on: server)
.done(on: DispatchQueue.global(qos: .userInitiated)) { data in
dependencies.storage.write { transaction in
// Update the thread
let transaction = transaction as! YapDatabaseReadWriteTransaction
let thread = TSGroupThread.getOrCreateThread(with: initialModel, transaction: transaction)
thread.groupModel.groupImage = UIImage(data: data)
thread.save(with: transaction)
}
}
.retainUntilComplete()
}
// Finish
completion?()
}
) )
maybeUpdatedModel = updatedModel
let updatedOpenGroup: OpenGroup = OpenGroup(
server: server,
room: pollInfo.token,
publicKey: publicKey,
name: (pollInfo.details?.name ?? thread.name()),
groupDescription: (pollInfo.details?.description ?? existingOpenGroup?.description),
imageID: (pollInfo.details?.imageId.map { "\($0)" } ?? existingOpenGroup?.imageID),
infoUpdates: ((pollInfo.details?.infoUpdates ?? existingOpenGroup?.infoUpdates) ?? 0)
)
// - Thread changes
thread.shouldBeVisible = true
thread.groupModel = updatedModel
thread.save(with: transaction)
// - Open Group changes
dependencies.storage.setOpenGroup(updatedOpenGroup, for: threadUniqueId, using: transaction)
// - User Count
dependencies.storage.setUserCount(
to: UInt64(pollInfo.activeUsers),
forOpenGroupWithID: updatedOpenGroup.id,
using: transaction
)
transaction.addCompletionQueue(DispatchQueue.global(qos: .userInitiated)) {
// Start the poller if needed
if OpenGroupManager.shared.pollers[server] == nil {
OpenGroupManager.shared.pollers[server] = OpenGroupAPI.Poller(for: server)
OpenGroupManager.shared.pollers[server]?.startIfNeeded()
}
// - Moderators
if let moderators: [String] = (pollInfo.details?.moderators ?? maybeUpdatedModel?.groupModeratorIds) {
OpenGroupManager.moderators[server] = (OpenGroupManager.moderators[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))
}
// - Room image (if there is one)
if let imageId: Int64 = pollInfo.details?.imageId {
OpenGroupManager.roomImage(imageId, for: roomToken, on: server)
.done(on: DispatchQueue.global(qos: .userInitiated)) { data in
dependencies.storage.write { transaction in
// Update the thread
let transaction = transaction as! YapDatabaseReadWriteTransaction
let thread = TSGroupThread.getOrCreateThread(with: initialModel, transaction: transaction)
thread.groupModel.groupImage = UIImage(data: data)
thread.save(with: transaction)
}
}
.retainUntilComplete()
}
// Finish
completion?()
}
}
internal static func handleMessages(
_ messages: [OpenGroupAPI.Message],
for roomToken: String,
on server: String,
isBackgroundPoll: Bool,
using transaction: YapDatabaseReadWriteTransaction,
dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()
) {
// 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).\(roomToken)"
let sortedMessages: [OpenGroupAPI.Message] = messages
.sorted { lhs, rhs in lhs.id < rhs.id }
let seqNo: Int64? = sortedMessages.map { $0.seqNo }.max()
var messageServerIDsToRemove: [UInt64] = []
// Update the 'openGroupSequenceNumber' value (Note: SOGS V4 uses the 'seqNo' instead of the 'serverId')
if let seqNo: Int64 = seqNo {
dependencies.storage.setOpenGroupSequenceNumber(for: roomToken, on: server, to: seqNo, using: transaction)
}
// Process the messages
sortedMessages.forEach { message in
guard let base64EncodedString: String = message.base64EncodedData, let data = Data(base64Encoded: base64EncodedString), let sender: String = message.sender else {
// A message with no data has been deleted so add it to the list to remove
messageServerIDsToRemove.append(UInt64(message.id))
return
}
// 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(data)
envelope.setSource(sender)
do {
let data = try envelope.buildSerializedData()
let (message, proto) = try MessageReceiver.parse(data, openGroupMessageServerID: UInt64(message.id), 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).")
}
}
// Handle any deletions that are needed
guard !messageServerIDsToRemove.isEmpty else { return }
guard let threadID = dependencies.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: TSMessage = interaction as? TSMessage, messageServerIDsToRemove.contains(message.openGroupServerMessageID) else {
return
}
messagesToRemove.append(message)
}
messagesToRemove.forEach { $0.remove(with: transaction) }
} }
internal static func handleDirectMessages( internal static func handleDirectMessages(
@ -318,7 +342,8 @@ public final class OpenGroupManager: NSObject {
fromOutbox: Bool, fromOutbox: Bool,
on server: String, on server: String,
isBackgroundPoll: Bool, isBackgroundPoll: Bool,
using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies() using transaction: YapDatabaseReadWriteTransaction,
dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()
) { ) {
// Don't need to do anything if we have no messages (it's a valid case) // Don't need to do anything if we have no messages (it's a valid case)
guard !messages.isEmpty else { return } guard !messages.isEmpty else { return }
@ -332,73 +357,78 @@ public final class OpenGroupManager: NSObject {
let sortedMessages: [OpenGroupAPI.DirectMessage] = messages let sortedMessages: [OpenGroupAPI.DirectMessage] = messages
.sorted { lhs, rhs in lhs.id < rhs.id } .sorted { lhs, rhs in lhs.id < rhs.id }
let latestMessageId: Int64 = (sortedMessages.last?.id ?? 0) let latestMessageId: Int64 = (sortedMessages.last?.id ?? 0)
let userSessionId: String = getUserHexEncodedPublicKey()
var mappingCache: [String: BlindedIdMapping] = [:] var mappingCache: [String: BlindedIdMapping] = [:]
dependencies.storage.write { transaction in // Update the 'latestMessageId' value
// Update the 'latestMessageId' value if fromOutbox {
if fromOutbox { dependencies.storage.setOpenGroupOutboxLatestMessageId(for: server, to: latestMessageId, using: transaction)
dependencies.storage.setOpenGroupOutboxLatestMessageId(for: server, to: latestMessageId, using: transaction) }
} else {
else { dependencies.storage.setOpenGroupInboxLatestMessageId(for: server, to: latestMessageId, using: transaction)
dependencies.storage.setOpenGroupInboxLatestMessageId(for: server, to: latestMessageId, using: transaction) }
// Process the messages
sortedMessages.forEach { message in
guard let messageData = Data(base64Encoded: message.base64EncodedMessage) else {
SNLog("Couldn't receive inbox message.")
return
} }
// Process the messages // Note: The `posted` value is in seconds but all messages in the database use milliseconds for timestamps
sortedMessages.forEach { message in let envelope = SNProtoEnvelope.builder(type: .sessionMessage, timestamp: UInt64(floor(message.posted * 1000)))
guard let messageData = Data(base64Encoded: message.base64EncodedMessage) else { envelope.setContent(messageData)
SNLog("Couldn't receive inbox message.") envelope.setSource(message.sender) // TODO: Need to un-blind/intercept outbox messages? (their sender will be the blinded id)
return
}
// Note: The `posted` value is in seconds but all messages in the database use milliseconds for timestamps do {
let envelope = SNProtoEnvelope.builder(type: .sessionMessage, timestamp: UInt64(floor(message.posted * 1000))) let data = try envelope.buildSerializedData()
envelope.setContent(messageData) let (receivedMessage, proto) = try MessageReceiver.parse(
envelope.setSource(message.sender ?? userSessionId) // Outbox messages have no 'sender' so default to current user data,
openGroupMessageServerID: nil,
do { openGroupServerPublicKey: serverPublicKey,
let data = try envelope.buildSerializedData() isOutgoing: fromOutbox,
let (receivedMessage, proto) = try MessageReceiver.parse(data, openGroupMessageServerID: nil, openGroupServerPublicKey: serverPublicKey, isRetry: false, using: transaction) otherBlindedPublicKey: (fromOutbox ? message.recipient : message.sender),
isRetry: false,
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)
if fromOutbox {
// Attempt to un-blind the 'message.recipient'
let mapping: BlindedIdMapping
// TODO: Need to test and validate this unblinding logic // Minor optimisation to avoid processing the same sender multiple times
// If the message was an outgoing message then attempt to unblind the recipient (this will help put if let result: BlindedIdMapping = mappingCache[message.recipient] {
// messages in the correct thread in case of message request approval race conditions as well as mapping = result
// during device sync'ing and restoration) }
if fromOutbox, let recipientBlindedId: String = message.recipient { else if let result: BlindedIdMapping = ContactUtilities.mapping(for: message.recipient, serverPublicKey: serverPublicKey) {
// Attempt to un-blind the 'message.recipient' mapping = result
let mapping: BlindedIdMapping }
else {
// Minor optimisation to avoid processing the same sender multiple times // Cache an "invalid" mapping that has the 'sessionId' set to the recipient so we don't
if let result: BlindedIdMapping = mappingCache[recipientBlindedId] { // re-process this recipient if there is another message from them
mapping = result mapping = BlindedIdMapping(
} blindedId: "",
else if let result: BlindedIdMapping = ContactUtilities.mapping(for: recipientBlindedId, serverPublicKey: serverPublicKey) { sessionId: message.recipient,
mapping = result serverPublicKey: ""
} )
else {
// Cache an "invalid" mapping that has the 'sessionId' set to the recipient so we don't
// re-process this recipient if there is another message from them
mapping = BlindedIdMapping(
blindedId: "",
sessionId: recipientBlindedId,
serverPublicKey: ""
)
}
switch receivedMessage {
case let receivedMessage as VisibleMessage: receivedMessage.syncTarget = mapping.sessionId
case let receivedMessage as ExpirationTimerUpdate: receivedMessage.syncTarget = mapping.sessionId
default: break
}
mappingCache[recipientBlindedId] = mapping
} }
try MessageReceiver.handle(receivedMessage, associatedWithProto: proto, openGroupID: nil, isBackgroundPoll: isBackgroundPoll, using: transaction) switch receivedMessage {
} case let receivedMessage as VisibleMessage: receivedMessage.syncTarget = mapping.sessionId
catch let error { case let receivedMessage as ExpirationTimerUpdate: receivedMessage.syncTarget = mapping.sessionId
SNLog("Couldn't receive inbox message due to error: \(error).") default: break
}
mappingCache[message.recipient] = mapping
} }
try MessageReceiver.handle(receivedMessage, associatedWithProto: proto, openGroupID: nil, isBackgroundPoll: isBackgroundPoll, using: transaction)
}
catch let error {
SNLog("Couldn't receive inbox message due to error: \(error).")
} }
} }
} }

View File

@ -24,22 +24,22 @@ extension MessageReceiver {
guard isValid else { throw Error.invalidSignature } guard isValid else { throw Error.invalidSignature }
// 4. ) Get the sender's X25519 public key // 4. ) Get the sender's X25519 public key
guard let senderX25519PublicKey = sodium.sign.toX25519(ed25519PublicKey: senderED25519PublicKey) else { throw Error.decryptionFailed } guard let senderX25519PublicKey = sodium.sign.toX25519(ed25519PublicKey: senderED25519PublicKey) else { throw Error.decryptionFailed }
// TODO: Need to rework this as it'll be based on the blinded id
return (Data(plaintext), SessionId(.standard, publicKey: senderX25519PublicKey).hexString) return (Data(plaintext), SessionId(.standard, publicKey: senderX25519PublicKey).hexString)
} }
internal static func decryptWithSessionBlindingProtocol(data: Data, fromBlindedPublicKey: String, with openGroupPublicKey: String, userEd25519KeyPair: Box.KeyPair, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) throws -> (plaintext: Data, senderX25519PublicKey: String) { internal static func decryptWithSessionBlindingProtocol(data: Data, isOutgoing: Bool, otherBlindedPublicKey: String, with openGroupPublicKey: String, userEd25519KeyPair: Box.KeyPair, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) throws -> (plaintext: Data, senderX25519PublicKey: String) {
guard let blindedKeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: openGroupPublicKey, edKeyPair: userEd25519KeyPair, genericHash: dependencies.genericHash) else { guard let blindedKeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: openGroupPublicKey, edKeyPair: userEd25519KeyPair, genericHash: dependencies.genericHash) else {
throw Error.decryptionFailed throw Error.decryptionFailed
} }
/// Step one: calculate the shared encryption key, receiving from A to B /// Step one: calculate the shared encryption key, receiving from A to B
let kA: Bytes = Data(hex: fromBlindedPublicKey.removingIdPrefixIfNeeded()).bytes let otherKeyBytes: Bytes = Data(hex: otherBlindedPublicKey.removingIdPrefixIfNeeded()).bytes
let kA: Bytes = (isOutgoing ? blindedKeyPair.publicKey : otherKeyBytes)
guard let dec_key: Bytes = dependencies.sodium.sharedBlindedEncryptionKey( guard let dec_key: Bytes = dependencies.sodium.sharedBlindedEncryptionKey(
secretKey: userEd25519KeyPair.secretKey, secretKey: userEd25519KeyPair.secretKey,
otherBlindedPublicKey: kA, otherBlindedPublicKey: otherKeyBytes,
fromBlindedPublicKey: kA, fromBlindedPublicKey: kA,
toBlindedPublicKey: blindedKeyPair.publicKey, toBlindedPublicKey: (isOutgoing ? otherKeyBytes : blindedKeyPair.publicKey),
genericHash: dependencies.genericHash genericHash: dependencies.genericHash
) else { ) else {
throw Error.decryptionFailed throw Error.decryptionFailed

View File

@ -277,7 +277,7 @@ extension MessageReceiver {
// Open groups // Open groups
for openGroupURL in message.openGroups { for openGroupURL in message.openGroups {
if let (room, server, publicKey) = OpenGroupManager.parseV2OpenGroup(from: openGroupURL) { if let (room, server, publicKey) = OpenGroupManager.parseV2OpenGroup(from: openGroupURL) {
OpenGroupManager.shared.add(roomToken: room, server: server, publicKey: publicKey, using: transaction).retainUntilComplete() OpenGroupManager.shared.add(roomToken: room, server: server, publicKey: publicKey, isConfigMessage: true, using: transaction).retainUntilComplete()
} }
} }
} }

View File

@ -48,7 +48,15 @@ public enum MessageReceiver {
} }
} }
public static func parse(_ data: Data, openGroupMessageServerID: UInt64?, openGroupServerPublicKey: String? = nil, isRetry: Bool = false, using transaction: Any) throws -> (Message, SNProtoContent) { public static func parse(
_ data: Data,
openGroupMessageServerID: UInt64?,
openGroupServerPublicKey: String? = nil,
isOutgoing: Bool? = nil,
otherBlindedPublicKey: String? = nil,
isRetry: Bool = false,
using transaction: Any
) throws -> (Message, SNProtoContent) {
let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey()
let isOpenGroupMessage = (openGroupMessageServerID != nil) let isOpenGroupMessage = (openGroupMessageServerID != nil)
@ -79,7 +87,7 @@ public enum MessageReceiver {
(plaintext, sender) = try decryptWithSessionProtocol(ciphertext: ciphertext, using: userX25519KeyPair) (plaintext, sender) = try decryptWithSessionProtocol(ciphertext: ciphertext, using: userX25519KeyPair)
case .blinded: case .blinded:
guard let senderSessionId: String = envelope.source else { throw Error.noData } guard let otherBlindedPublicKey: String = otherBlindedPublicKey else { throw Error.noData }
guard let openGroupServerPublicKey: String = openGroupServerPublicKey else { guard let openGroupServerPublicKey: String = openGroupServerPublicKey else {
throw Error.invalidGroupPublicKey throw Error.invalidGroupPublicKey
} }
@ -89,7 +97,8 @@ public enum MessageReceiver {
(plaintext, sender) = try decryptWithSessionBlindingProtocol( (plaintext, sender) = try decryptWithSessionBlindingProtocol(
data: ciphertext, data: ciphertext,
fromBlindedPublicKey: senderSessionId, isOutgoing: (isOutgoing == true),
otherBlindedPublicKey: otherBlindedPublicKey,
with: openGroupServerPublicKey, with: openGroupServerPublicKey,
userEd25519KeyPair: userEd25519KeyPair userEd25519KeyPair: userEd25519KeyPair
) )

View File

@ -392,10 +392,11 @@ public final class MessageSender : NSObject {
whisperMods: whisperMods whisperMods: whisperMods
) )
.done(on: DispatchQueue.global(qos: .userInitiated)) { responseInfo, data in .done(on: DispatchQueue.global(qos: .userInitiated)) { responseInfo, data in
message.openGroupServerMessageID = given(data.id) { UInt64($0) } message.openGroupServerMessageID = UInt64(data.id)
dependencies.storage.write { transaction in dependencies.storage.write { transaction in
MessageSender.handleSuccessfulMessageSend(message, to: destination, serverTimestamp: UInt64(floor(data.posted)), using: transaction) // The `posted` value is in seconds but we sent it in ms so need that for de-duping
MessageSender.handleSuccessfulMessageSend(message, to: destination, serverTimestamp: UInt64(floor(data.posted * 1000)), using: transaction)
seal.fulfill(()) seal.fulfill(())
} }
} }

View File

@ -84,66 +84,79 @@ extension OpenGroupAPI {
} }
private func handlePollResponse(_ response: [OpenGroupAPI.Endpoint: (info: OnionRequestResponseInfoType, data: Codable?)], isBackgroundPoll: Bool) { private func handlePollResponse(_ response: [OpenGroupAPI.Endpoint: (info: OnionRequestResponseInfoType, data: Codable?)], isBackgroundPoll: Bool) {
response.forEach { endpoint, endpointResponse in let server: String = self.server
switch endpoint {
case .capabilities: Storage.shared.write { anyTransaction in
guard let responseData: BatchSubResponse<Capabilities> = endpointResponse.data as? BatchSubResponse<Capabilities>, let responseBody: Capabilities = responseData.body else { guard let transaction: YapDatabaseReadWriteTransaction = anyTransaction as? YapDatabaseReadWriteTransaction else {
SNLog("Open group polling failed due to invalid data.") SNLog("Open group polling failed due to invalid database transaction.")
return return
} }
OpenGroupManager.handleCapabilities( response.forEach { endpoint, endpointResponse in
responseBody, switch endpoint {
on: server case .capabilities:
) guard let responseData: BatchSubResponse<Capabilities> = endpointResponse.data as? BatchSubResponse<Capabilities>, let responseBody: Capabilities = responseData.body else {
SNLog("Open group polling failed due to invalid data.")
case .roomMessagesRecent(let roomToken), .roomMessagesBefore(let roomToken, _), .roomMessagesSince(let roomToken, _): return
guard let responseData: BatchSubResponse<[Message]> = endpointResponse.data as? BatchSubResponse<[Message]>, let responseBody: [Message] = responseData.body else {
SNLog("Open group polling failed due to invalid data.")
return
}
OpenGroupManager.handleMessages(
responseBody,
for: roomToken,
on: server,
isBackgroundPoll: isBackgroundPoll
)
case .roomPollInfo(let roomToken, _):
guard let responseData: BatchSubResponse<RoomPollInfo> = endpointResponse.data as? BatchSubResponse<RoomPollInfo>, let responseBody: RoomPollInfo = responseData.body else {
SNLog("Open group polling failed due to invalid data.")
return
}
OpenGroupManager.handlePollInfo(
responseBody,
publicKey: nil,
for: roomToken,
on: server
)
case .inbox, .inboxSince, .outbox, .outboxSince:
guard let responseData: BatchSubResponse<[DirectMessage]?> = endpointResponse.data as? BatchSubResponse<[DirectMessage]?>, let responseBody: [DirectMessage]? = responseData.body else {
SNLog("Open group polling failed due to invalid data.")
return
}
let fromOutbox: Bool = {
switch endpoint {
case .outbox, .outboxSince: return true
default: return false
} }
}()
OpenGroupManager.handleCapabilities(
OpenGroupManager.handleDirectMessages( responseBody,
(responseBody ?? []), on: server,
fromOutbox: fromOutbox, using: transaction
on: server, )
isBackgroundPoll: isBackgroundPoll
) case .roomMessagesRecent(let roomToken), .roomMessagesBefore(let roomToken, _), .roomMessagesSince(let roomToken, _):
guard let responseData: BatchSubResponse<[Message]> = endpointResponse.data as? BatchSubResponse<[Message]>, let responseBody: [Message] = responseData.body else {
default: break // No custom handling needed SNLog("Open group polling failed due to invalid data.")
return
}
OpenGroupManager.handleMessages(
responseBody,
for: roomToken,
on: server,
isBackgroundPoll: isBackgroundPoll,
using: transaction
)
case .roomPollInfo(let roomToken, _):
guard let responseData: BatchSubResponse<RoomPollInfo> = endpointResponse.data as? BatchSubResponse<RoomPollInfo>, let responseBody: RoomPollInfo = responseData.body else {
SNLog("Open group polling failed due to invalid data.")
return
}
OpenGroupManager.handlePollInfo(
responseBody,
publicKey: nil,
for: roomToken,
on: server,
using: transaction
)
case .inbox, .inboxSince, .outbox, .outboxSince:
guard let responseData: BatchSubResponse<[DirectMessage]?> = endpointResponse.data as? BatchSubResponse<[DirectMessage]?>, !responseData.failedToParseBody else {
SNLog("Open group polling failed due to invalid data.")
return
}
let fromOutbox: Bool = {
switch endpoint {
case .outbox, .outboxSince: return true
default: return false
}
}()
OpenGroupManager.handleDirectMessages(
((responseData.body ?? []) ?? []), // Double optional because the server can return a `304` with an empty body
fromOutbox: fromOutbox,
on: server,
isBackgroundPoll: isBackgroundPoll,
using: transaction
)
default: break // No custom handling needed
}
} }
} }
} }

View File

@ -123,7 +123,7 @@ NS_ASSUME_NONNULL_BEGIN
TSThread *thread = [TSThread fetchObjectWithUniqueID:groupID transaction:transaction]; TSThread *thread = [TSThread fetchObjectWithUniqueID:groupID transaction:transaction];
// Only increase the count for message requests // Only increase the count for message requests
if (!thread.isMessageRequest) { continue; } if (![thread isMessageRequestUsingTransaction:transaction]) { continue; }
[unreadMessages enumerateKeysAndObjectsInGroup:groupID [unreadMessages enumerateKeysAndObjectsInGroup:groupID
usingBlock:^(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) { usingBlock:^(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) {