Started work on updated SOGS support

Split the OpenGroupAPIV2 into separate files
Started working on the new auth and blinded-id approaches (new auth working with un-blinded id suggesting blinded-id code is incorrect)
Updated the SOGS request/response types to use Codable
Updated the SOGS Request type to use enums instead of strings for keys (to reduce likelihood of typos breaking things)
Updated SessionMessagingKit to use Codable and JSONEncoder/JSONDecoder instead of the legacy JSONSerialization
Cleaned up some naming conventions in the SessionMessagingKit (calling a URLRequest body 'parameters' is very confusing...)
Removed the custom TSRequest class (just using standard URLRequest everywhere instead)
Added a number of extension functions to enable some more functional-coding styles
Added extensions to Sodium methods to allow scalar multiplication and the ability to hash providing a salt and a personalisation value (both needed for new SOGS auth)
Fixed an issue where the legacy auth for SOGS could crash due to threading issues (multiple threads accessing the same variable)
Fixed an issue where if you were in two rooms in a single SOGS and deleted one of them, the other room would stop getting updates as the server public key was getting removed
This commit is contained in:
Morgan Pretty 2022-02-10 11:17:41 +11:00
parent 49dd52a1fb
commit 2284375fc0
55 changed files with 1927 additions and 721 deletions

View File

@ -196,7 +196,7 @@
B8569AC325CB5D2900DBA3DB /* ConversationVC+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */; };
B8569AD325CBA13D00DBA3DB /* MediaTextOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AD225CBA13D00DBA3DB /* MediaTextOverlayView.swift */; };
B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AE225CBB19A00DBA3DB /* DocumentView.swift */; };
B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Conversion.swift */; };
B866CE112581C1A900535CC4 /* Sodium+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Utilities.swift */; };
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; };
B875885A264503A6000E60D0 /* JoinOpenGroupModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8758859264503A6000E60D0 /* JoinOpenGroupModal.swift */; };
@ -238,7 +238,7 @@
B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */; };
B894D0752339EDCF00B4D94D /* NukeDataModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B894D0742339EDCF00B4D94D /* NukeDataModal.swift */; };
B897621C25D201F7004F83B2 /* ScrollToBottomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B897621B25D201F7004F83B2 /* ScrollToBottomButton.swift */; };
B8AE75A425A6C6A6001A84D2 /* Data+Trimming.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8AE75A325A6C6A6001A84D2 /* Data+Trimming.swift */; };
B8AE75A425A6C6A6001A84D2 /* Data+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8AE75A325A6C6A6001A84D2 /* Data+Utilities.swift */; };
B8AE760B25ABFB5A001A84D2 /* GeneralUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B8AE760A25ABFB5A001A84D2 /* GeneralUtilities.m */; };
B8AE761425ABFBB9001A84D2 /* GeneralUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = B8AE760925ABFB00001A84D2 /* GeneralUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; };
B8AF4BB426A5204600583500 /* SendSeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8AF4BB326A5204600583500 /* SendSeedModal.swift */; };
@ -300,7 +300,6 @@
C31D1DE32521718E005D4DA8 /* UserSelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */; };
C31D1DE9252172D4005D4DA8 /* ContactUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */; };
C31FFE57254A5FFE00F19441 /* KeyPairUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */; };
C3227FF6260AAD66006EA627 /* OpenGroupMessageV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3227FF5260AAD66006EA627 /* OpenGroupMessageV2.swift */; };
C32824D325C9F9790062D0A7 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; };
C328250F25CA06020062D0A7 /* VoiceMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328250E25CA06020062D0A7 /* VoiceMessageView.swift */; };
C328251F25CA3A900062D0A7 /* QuoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328251E25CA3A900062D0A7 /* QuoteView.swift */; };
@ -510,8 +509,6 @@
C352A3772557864000338F3E /* NSTimer+Proxying.h in Headers */ = {isa = PBXBuildFile; fileRef = C352A3762557859C00338F3E /* NSTimer+Proxying.h */; settings = {ATTRIBUTES = (Public, ); }; };
C352A3892557876500338F3E /* JobQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A3882557876500338F3E /* JobQueue.swift */; };
C352A3932557883D00338F3E /* JobDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A3922557883D00338F3E /* JobDelegate.swift */; };
C352A3A62557B60D00338F3E /* TSRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = C352A3A52557B60D00338F3E /* TSRequest.m */; };
C352A3B72557B6ED00338F3E /* TSRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = C352A3A42557B5F000338F3E /* TSRequest.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3548F0624456447009433A8 /* PNModeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3548F0524456447009433A8 /* PNModeVC.swift */; };
C3548F0824456AB6009433A8 /* UIView+Wrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3548F0724456AB6009433A8 /* UIView+Wrapping.swift */; };
C354E75A23FE2A7600CE22E3 /* BaseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C354E75923FE2A7600CE22E3 /* BaseVC.swift */; };
@ -676,7 +673,7 @@
C3A7219A2558C1660043A11F /* AnyPromise+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A721992558C1660043A11F /* AnyPromise+Conversion.swift */; };
C3A7225E2558C38D0043A11F /* Promise+Retaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A7225D2558C38D0043A11F /* Promise+Retaining.swift */; };
C3A76A8D25DB83F90074CB90 /* PermissionMissingModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A76A8C25DB83F90074CB90 /* PermissionMissingModal.swift */; };
C3AABDDF2553ECF00042FF4C /* Array+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Description.swift */; };
C3AABDDF2553ECF00042FF4C /* Array+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Utilities.swift */; };
C3AAFFF225AE99710089E6DD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFF125AE99710089E6DD /* AppDelegate.swift */; };
C3ADC66126426688005F1414 /* ShareVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ADC66026426688005F1414 /* ShareVC.swift */; };
C3BBE0762554CDA60050F1E3 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BBE0752554CDA60050F1E3 /* Configuration.swift */; };
@ -684,7 +681,7 @@
C3BBE0A72554D4DE0050F1E3 /* Promise+Retrying.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D62553860B00C340D1 /* Promise+Retrying.swift */; };
C3BBE0A82554D4DE0050F1E3 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D92553860B00C340D1 /* JSON.swift */; };
C3BBE0A92554D4DE0050F1E3 /* HTTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5BC255385EE00C340D1 /* HTTP.swift */; };
C3BBE0AA2554D4DE0050F1E3 /* Dictionary+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D52553860A00C340D1 /* Dictionary+Description.swift */; };
C3BBE0AA2554D4DE0050F1E3 /* Dictionary+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D52553860A00C340D1 /* Dictionary+Utilities.swift */; };
C3BBE0B52554F0E10050F1E3 /* ProofOfWork.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BBE0B42554F0E10050F1E3 /* ProofOfWork.swift */; };
C3BBE0C72554F1570050F1E3 /* FixedWidthInteger+BigEndian.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BBE0C62554F1570050F1E3 /* FixedWidthInteger+BigEndian.swift */; };
C3C2A5A3255385C100C340D1 /* SessionSnodeKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C3C2A5A1255385C100C340D1 /* SessionSnodeKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -754,7 +751,6 @@
C3D9E52725677DF20040E4F3 /* OWSThumbnailService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF1255A580500E217F9 /* OWSThumbnailService.swift */; };
C3DA9C0725AE7396008F7C7E /* ConfigurationMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DA9C0625AE7396008F7C7E /* ConfigurationMessage.swift */; };
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
C3DB6695260AC923001EFC55 /* OpenGroupV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB6694260AC923001EFC55 /* OpenGroupV2.swift */; };
C3DB66AC260ACA42001EFC55 /* OpenGroupManagerV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66AB260ACA42001EFC55 /* OpenGroupManagerV2.swift */; };
C3DB66C3260ACCE6001EFC55 /* OpenGroupPollerV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66C2260ACCE6001EFC55 /* OpenGroupPollerV2.swift */; };
C3DB66CC260AF1F3001EFC55 /* OpenGroupAPIV2+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66CB260AF1F3001EFC55 /* OpenGroupAPIV2+ObjC.swift */; };
@ -778,6 +774,8 @@
FD5D200F27AA2B6000FEA984 /* MessageRequestResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D200E27AA2B6000FEA984 /* MessageRequestResponse.swift */; };
FD5D201127AA331F00FEA984 /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */; };
FD5D201E27B0D87C00FEA984 /* IdPrefix.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201D27B0D87C00FEA984 /* IdPrefix.swift */; };
FD5D202027B0E67900FEA984 /* String+Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201F27B0E67800FEA984 /* String+Encoding.swift */; };
FD5D202227B1D74F00FEA984 /* ECKeyPair+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D202127B1D74F00FEA984 /* ECKeyPair+Conversion.swift */; };
FD659AC027A7649600F12C02 /* MessageRequestsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD659ABF27A7649600F12C02 /* MessageRequestsViewController.swift */; };
FD705A8C278CDB5600F16121 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8B278CDB5600F16121 /* SAEScreenLockViewController.swift */; };
FD705A8E278CE29800F16121 /* String+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8D278CE29800F16121 /* String+Localization.swift */; };
@ -787,6 +785,33 @@
FD705A98278E9F4D00F16121 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */; };
FD88BAD927A7439C00BBC442 /* MessageRequestsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */; };
FD88BADB27A750F200BBC442 /* MessageRequestsMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */; };
FDC4380927B31D4E00C60D73 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4380827B31D4E00C60D73 /* Error.swift */; };
FDC4380B27B31D7E00C60D73 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4380A27B31D7E00C60D73 /* Request.swift */; };
FDC4381527B329CE00C60D73 /* NonceGenerator16Byte.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381427B329CE00C60D73 /* NonceGenerator16Byte.swift */; };
FDC4381727B32EC700C60D73 /* Personalization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381627B32EC700C60D73 /* Personalization.swift */; };
FDC4381A27B34EBA00C60D73 /* CompactPollBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381927B34EBA00C60D73 /* CompactPollBody.swift */; };
FDC4381C27B354AC00C60D73 /* PublicKeyBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381B27B354AC00C60D73 /* PublicKeyBody.swift */; };
FDC4382027B36ADC00C60D73 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381F27B36ADC00C60D73 /* Endpoint.swift */; };
FDC4382627B37F6900C60D73 /* DeletedMessagesResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382527B37F6900C60D73 /* DeletedMessagesResponse.swift */; };
FDC4382827B37FD300C60D73 /* ModeratorsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382727B37FD300C60D73 /* ModeratorsResponse.swift */; };
FDC4382A27B3802D00C60D73 /* RoomsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382927B3802D00C60D73 /* RoomsResponse.swift */; };
FDC4382C27B380E300C60D73 /* MemberCountResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382B27B380E300C60D73 /* MemberCountResponse.swift */; };
FDC4382F27B383AF00C60D73 /* UnregisterResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382E27B383AF00C60D73 /* UnregisterResponse.swift */; };
FDC4383127B3841C00C60D73 /* RegisterResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383027B3841C00C60D73 /* RegisterResponse.swift */; };
FDC4383827B3863200C60D73 /* VersionResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383727B3863200C60D73 /* VersionResponse.swift */; };
FDC4383A27B4696200C60D73 /* AuthTokenResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383927B4696200C60D73 /* AuthTokenResponse.swift */; };
FDC4383E27B4708600C60D73 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383D27B4708600C60D73 /* Atomic.swift */; };
FDC4384027B4746D00C60D73 /* GetInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383F27B4746D00C60D73 /* GetInfoResponse.swift */; };
FDC4384727B47F4D00C60D73 /* OpenGroupMessageV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384327B47F4D00C60D73 /* OpenGroupMessageV2.swift */; };
FDC4384827B47F4D00C60D73 /* RoomInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384427B47F4D00C60D73 /* RoomInfo.swift */; };
FDC4384927B47F4D00C60D73 /* CompactPollResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384527B47F4D00C60D73 /* CompactPollResponse.swift */; };
FDC4384A27B47F4D00C60D73 /* Deletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384627B47F4D00C60D73 /* Deletion.swift */; };
FDC4384C27B47F7700C60D73 /* OpenGroupV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384B27B47F7700C60D73 /* OpenGroupV2.swift */; };
FDC4384F27B4804F00C60D73 /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384E27B4804F00C60D73 /* Header.swift */; };
FDC4385127B4807400C60D73 /* QueryParam.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385027B4807400C60D73 /* QueryParam.swift */; };
FDC4385727B484B700C60D73 /* FileUploadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385627B484B700C60D73 /* FileUploadResponse.swift */; };
FDC4385927B484E800C60D73 /* FileUploadBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385827B484E800C60D73 /* FileUploadBody.swift */; };
FDC4385B27B485DE00C60D73 /* FileDownloadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385A27B485DE00C60D73 /* FileDownloadResponse.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -1236,7 +1261,7 @@
B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQRCodeWrapperVC.swift; sourceTree = "<group>"; };
B894D0742339EDCF00B4D94D /* NukeDataModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NukeDataModal.swift; sourceTree = "<group>"; };
B897621B25D201F7004F83B2 /* ScrollToBottomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollToBottomButton.swift; sourceTree = "<group>"; };
B8AE75A325A6C6A6001A84D2 /* Data+Trimming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Trimming.swift"; sourceTree = "<group>"; };
B8AE75A325A6C6A6001A84D2 /* Data+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = "<group>"; };
B8AE760925ABFB00001A84D2 /* GeneralUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneralUtilities.h; sourceTree = "<group>"; };
B8AE760A25ABFB5A001A84D2 /* GeneralUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GeneralUtilities.m; sourceTree = "<group>"; };
B8AF4BB326A5204600583500 /* SendSeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendSeedModal.swift; sourceTree = "<group>"; };
@ -1314,7 +1339,6 @@
C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSelectionVC.swift; sourceTree = "<group>"; };
C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactUtilities.swift; sourceTree = "<group>"; };
C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPairUtilities.swift; sourceTree = "<group>"; };
C3227FF5260AAD66006EA627 /* OpenGroupMessageV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupMessageV2.swift; sourceTree = "<group>"; };
C328250E25CA06020062D0A7 /* VoiceMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageView.swift; sourceTree = "<group>"; };
C328251E25CA3A900062D0A7 /* QuoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuoteView.swift; sourceTree = "<group>"; };
C328252F25CA55360062D0A7 /* ContextMenuWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuWindow.swift; sourceTree = "<group>"; };
@ -1525,8 +1549,6 @@
C352A3762557859C00338F3E /* NSTimer+Proxying.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSTimer+Proxying.h"; sourceTree = "<group>"; };
C352A3882557876500338F3E /* JobQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobQueue.swift; sourceTree = "<group>"; };
C352A3922557883D00338F3E /* JobDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobDelegate.swift; sourceTree = "<group>"; };
C352A3A42557B5F000338F3E /* TSRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSRequest.h; sourceTree = "<group>"; };
C352A3A52557B60D00338F3E /* TSRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TSRequest.m; sourceTree = "<group>"; };
C353F8F8244809150011121A /* PNOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNOptionView.swift; sourceTree = "<group>"; };
C3548F0524456447009433A8 /* PNModeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNModeVC.swift; sourceTree = "<group>"; };
C3548F0724456AB6009433A8 /* UIView+Wrapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Wrapping.swift"; sourceTree = "<group>"; };
@ -1731,11 +1753,11 @@
C3C2A5CE2553860700C340D1 /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
C3C2A5CF2553860700C340D1 /* Promise+Hashing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Promise+Hashing.swift"; sourceTree = "<group>"; };
C3C2A5D02553860800C340D1 /* Promise+Threading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Promise+Threading.swift"; sourceTree = "<group>"; };
C3C2A5D12553860800C340D1 /* Array+Description.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Description.swift"; sourceTree = "<group>"; };
C3C2A5D12553860800C340D1 /* Array+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Utilities.swift"; sourceTree = "<group>"; };
C3C2A5D22553860900C340D1 /* String+Trimming.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Trimming.swift"; sourceTree = "<group>"; };
C3C2A5D32553860900C340D1 /* Promise+Delaying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Promise+Delaying.swift"; sourceTree = "<group>"; };
C3C2A5D42553860A00C340D1 /* Threading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Threading.swift; sourceTree = "<group>"; };
C3C2A5D52553860A00C340D1 /* Dictionary+Description.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+Description.swift"; sourceTree = "<group>"; };
C3C2A5D52553860A00C340D1 /* Dictionary+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+Utilities.swift"; sourceTree = "<group>"; };
C3C2A5D62553860B00C340D1 /* Promise+Retrying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Promise+Retrying.swift"; sourceTree = "<group>"; };
C3C2A5D72553860B00C340D1 /* AESGCM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AESGCM.swift; sourceTree = "<group>"; };
C3C2A5D82553860B00C340D1 /* Data+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = "<group>"; };
@ -1767,13 +1789,12 @@
C3D9E43025676D3D0040E4F3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
C3DA9C0625AE7396008F7C7E /* ConfigurationMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationMessage.swift; sourceTree = "<group>"; };
C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCopyableLabel.swift; sourceTree = "<group>"; };
C3DB6694260AC923001EFC55 /* OpenGroupV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupV2.swift; sourceTree = "<group>"; };
C3DB66AB260ACA42001EFC55 /* OpenGroupManagerV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupManagerV2.swift; sourceTree = "<group>"; };
C3DB66C2260ACCE6001EFC55 /* OpenGroupPollerV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupPollerV2.swift; sourceTree = "<group>"; };
C3DB66CB260AF1F3001EFC55 /* OpenGroupAPIV2+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenGroupAPIV2+ObjC.swift"; sourceTree = "<group>"; };
C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = "<group>"; };
C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditClosedGroupVC.swift; sourceTree = "<group>"; };
C3E7134E251C867C009649BB /* Sodium+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sodium+Conversion.swift"; sourceTree = "<group>"; };
C3E7134E251C867C009649BB /* Sodium+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sodium+Utilities.swift"; sourceTree = "<group>"; };
C3ECBF7A257056B700EA7FCE /* Threading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Threading.swift; sourceTree = "<group>"; };
C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoopNotificationsManager.swift; sourceTree = "<group>"; };
C3F0A5B2255C915C007BE2A3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -1813,6 +1834,8 @@
FD5D200E27AA2B6000FEA984 /* MessageRequestResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestResponse.swift; sourceTree = "<group>"; };
FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfigurationMessage+Convenience.swift"; sourceTree = "<group>"; };
FD5D201D27B0D87C00FEA984 /* IdPrefix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdPrefix.swift; sourceTree = "<group>"; };
FD5D201F27B0E67800FEA984 /* String+Encoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Encoding.swift"; sourceTree = "<group>"; };
FD5D202127B1D74F00FEA984 /* ECKeyPair+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ECKeyPair+Conversion.swift"; sourceTree = "<group>"; };
FD659ABF27A7649600F12C02 /* MessageRequestsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsViewController.swift; sourceTree = "<group>"; };
FD705A8B278CDB5600F16121 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = "<group>"; };
FD705A8D278CE29800F16121 /* String+Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Localization.swift"; sourceTree = "<group>"; };
@ -1823,6 +1846,33 @@
FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsCell.swift; sourceTree = "<group>"; };
FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsMigration.swift; sourceTree = "<group>"; };
FD9039443F7CB729CF71350E /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; sourceTree = "<group>"; };
FDC4380827B31D4E00C60D73 /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
FDC4380A27B31D7E00C60D73 /* Request.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = "<group>"; };
FDC4381427B329CE00C60D73 /* NonceGenerator16Byte.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonceGenerator16Byte.swift; sourceTree = "<group>"; };
FDC4381627B32EC700C60D73 /* Personalization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Personalization.swift; sourceTree = "<group>"; };
FDC4381927B34EBA00C60D73 /* CompactPollBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactPollBody.swift; sourceTree = "<group>"; };
FDC4381B27B354AC00C60D73 /* PublicKeyBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicKeyBody.swift; sourceTree = "<group>"; };
FDC4381F27B36ADC00C60D73 /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = "<group>"; };
FDC4382527B37F6900C60D73 /* DeletedMessagesResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedMessagesResponse.swift; sourceTree = "<group>"; };
FDC4382727B37FD300C60D73 /* ModeratorsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModeratorsResponse.swift; sourceTree = "<group>"; };
FDC4382927B3802D00C60D73 /* RoomsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomsResponse.swift; sourceTree = "<group>"; };
FDC4382B27B380E300C60D73 /* MemberCountResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberCountResponse.swift; sourceTree = "<group>"; };
FDC4382E27B383AF00C60D73 /* UnregisterResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnregisterResponse.swift; sourceTree = "<group>"; };
FDC4383027B3841C00C60D73 /* RegisterResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterResponse.swift; sourceTree = "<group>"; };
FDC4383727B3863200C60D73 /* VersionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionResponse.swift; sourceTree = "<group>"; };
FDC4383927B4696200C60D73 /* AuthTokenResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTokenResponse.swift; sourceTree = "<group>"; };
FDC4383D27B4708600C60D73 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
FDC4383F27B4746D00C60D73 /* GetInfoResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetInfoResponse.swift; sourceTree = "<group>"; };
FDC4384327B47F4D00C60D73 /* OpenGroupMessageV2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGroupMessageV2.swift; sourceTree = "<group>"; };
FDC4384427B47F4D00C60D73 /* RoomInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomInfo.swift; sourceTree = "<group>"; };
FDC4384527B47F4D00C60D73 /* CompactPollResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompactPollResponse.swift; sourceTree = "<group>"; };
FDC4384627B47F4D00C60D73 /* Deletion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deletion.swift; sourceTree = "<group>"; };
FDC4384B27B47F7700C60D73 /* OpenGroupV2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGroupV2.swift; sourceTree = "<group>"; };
FDC4384E27B4804F00C60D73 /* Header.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = "<group>"; };
FDC4385027B4807400C60D73 /* QueryParam.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryParam.swift; sourceTree = "<group>"; };
FDC4385627B484B700C60D73 /* FileUploadResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUploadResponse.swift; sourceTree = "<group>"; };
FDC4385827B484E800C60D73 /* FileUploadBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUploadBody.swift; sourceTree = "<group>"; };
FDC4385A27B485DE00C60D73 /* FileDownloadResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDownloadResponse.swift; sourceTree = "<group>"; };
FEDBAE1B98C49BBE8C87F575 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; sourceTree = "<group>"; };
FF9BA33D021B115B1F5B4E46 /* Pods-SessionMessagingKit.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionMessagingKit.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-SessionMessagingKit/Pods-SessionMessagingKit.app store release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -2303,8 +2353,6 @@
B8FF8EA525C11FEF004D1F22 /* IPv4.swift */,
C3C2A5D92553860B00C340D1 /* JSON.swift */,
C33FDAF2255A580500E217F9 /* ProxiedContentDownloader.swift */,
C352A3A42557B5F000338F3E /* TSRequest.h */,
C352A3A52557B60D00338F3E /* TSRequest.m */,
);
path = Networking;
sourceTree = "<group>";
@ -2330,11 +2378,11 @@
children = (
C33FDB8A255A581200E217F9 /* AppContext.h */,
C33FDB85255A581100E217F9 /* AppContext.m */,
C3C2A5D12553860800C340D1 /* Array+Description.swift */,
C3C2A5D12553860800C340D1 /* Array+Utilities.swift */,
C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */,
B8F5F58225EC94A6003BF8D4 /* Collection+Subscripting.swift */,
B8AE75A325A6C6A6001A84D2 /* Data+Trimming.swift */,
C3C2A5D52553860A00C340D1 /* Dictionary+Description.swift */,
B8AE75A325A6C6A6001A84D2 /* Data+Utilities.swift */,
C3C2A5D52553860A00C340D1 /* Dictionary+Utilities.swift */,
B87EF18026377A1D00124B3C /* Features.swift */,
B8BC00BF257D90E30032E807 /* General.swift */,
C3C2A5CE2553860700C340D1 /* Logging.swift */,
@ -2357,6 +2405,7 @@
C33FDB3F255A580C00E217F9 /* String+SSK.swift */,
C3C2AC2D2553CBEB00C340D1 /* String+Trimming.swift */,
FD705A8D278CE29800F16121 /* String+Localization.swift */,
FD5D201F27B0E67800FEA984 /* String+Encoding.swift */,
C38EF237255B6D65007E1867 /* UIDevice+featureSupport.swift */,
C35D0DB425AE5F1200B6BF49 /* UIEdgeInsets.swift */,
FD705A91278D051200F16121 /* ReusableView.swift */,
@ -3043,6 +3092,7 @@
C379DC6825672B5E0002D4EB /* Notifications */ = {
isa = PBXGroup;
children = (
FDC4382D27B383A600C60D73 /* Models */,
C33FDB7A255A581000E217F9 /* NotificationsProtocol.h */,
C33FDBDE255A581900E217F9 /* PushNotificationAPI.swift */,
);
@ -3182,11 +3232,11 @@
C3A721332558BDDF0043A11F /* Open Groups */ = {
isa = PBXGroup;
children = (
C3DB6694260AC923001EFC55 /* OpenGroupV2.swift */,
FDC4381827B34EAD00C60D73 /* Models */,
FDC4380727B31D3A00C60D73 /* Types */,
B88FA7B726045D100049422F /* OpenGroupAPIV2.swift */,
C3DB66CB260AF1F3001EFC55 /* OpenGroupAPIV2+ObjC.swift */,
C3DB66AB260ACA42001EFC55 /* OpenGroupManagerV2.swift */,
C3227FF5260AAD66006EA627 /* OpenGroupMessageV2.swift */,
);
path = "Open Groups";
sourceTree = "<group>";
@ -3194,6 +3244,7 @@
C3A7215C2558C0AC0043A11F /* File Server */ = {
isa = PBXGroup;
children = (
FDC4383227B385B200C60D73 /* Models */,
B87EF17026367CF800124B3C /* FileServerAPIV2.swift */,
);
path = "File Server";
@ -3204,6 +3255,7 @@
children = (
C33FDB01255A580700E217F9 /* AppReadiness.h */,
C33FDB75255A581000E217F9 /* AppReadiness.m */,
FDC4383D27B4708600C60D73 /* Atomic.swift */,
C38EF309255B6DBE007E1867 /* DeviceSleepManager.swift */,
C37F53E8255BA9BB002AEA92 /* Environment.h */,
C37F5402255BA9ED002AEA92 /* Environment.m */,
@ -3241,7 +3293,8 @@
C33FDB91255A581200E217F9 /* ProtoUtils.h */,
C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */,
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
C3E7134E251C867C009649BB /* Sodium+Conversion.swift */,
C3E7134E251C867C009649BB /* Sodium+Utilities.swift */,
FD5D202127B1D74F00FEA984 /* ECKeyPair+Conversion.swift */,
C33FDB31255A580A00E217F9 /* SSKEnvironment.h */,
C33FDAF4255A580600E217F9 /* SSKEnvironment.m */,
C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */,
@ -3329,6 +3382,7 @@
C32C5BB9256DC7C4003C73A2 /* To Do */,
C3BBE0752554CDA60050F1E3 /* Configuration.swift */,
C3BBE07F2554CDD70050F1E3 /* Storage.swift */,
FDC4384D27B47FD600C60D73 /* Common Networking */,
B8B3201F258B1A540020074B /* Contacts */,
C32C5BCB256DC818003C73A2 /* Database */,
C300A5BB2554AFFB00555489 /* Messages */,
@ -3636,6 +3690,75 @@
path = Views;
sourceTree = "<group>";
};
FDC4380727B31D3A00C60D73 /* Types */ = {
isa = PBXGroup;
children = (
FDC4380A27B31D7E00C60D73 /* Request.swift */,
FDC4381F27B36ADC00C60D73 /* Endpoint.swift */,
FDC4380827B31D4E00C60D73 /* Error.swift */,
FDC4381627B32EC700C60D73 /* Personalization.swift */,
FDC4381427B329CE00C60D73 /* NonceGenerator16Byte.swift */,
);
path = Types;
sourceTree = "<group>";
};
FDC4381827B34EAD00C60D73 /* Models */ = {
isa = PBXGroup;
children = (
FDC4384B27B47F7700C60D73 /* OpenGroupV2.swift */,
FDC4381B27B354AC00C60D73 /* PublicKeyBody.swift */,
FDC4381927B34EBA00C60D73 /* CompactPollBody.swift */,
FDC4384527B47F4D00C60D73 /* CompactPollResponse.swift */,
FDC4383F27B4746D00C60D73 /* GetInfoResponse.swift */,
FDC4382927B3802D00C60D73 /* RoomsResponse.swift */,
FDC4384427B47F4D00C60D73 /* RoomInfo.swift */,
FDC4382B27B380E300C60D73 /* MemberCountResponse.swift */,
FDC4384327B47F4D00C60D73 /* OpenGroupMessageV2.swift */,
FDC4382527B37F6900C60D73 /* DeletedMessagesResponse.swift */,
FDC4384627B47F4D00C60D73 /* Deletion.swift */,
FDC4382727B37FD300C60D73 /* ModeratorsResponse.swift */,
FDC4383927B4696200C60D73 /* AuthTokenResponse.swift */,
);
path = Models;
sourceTree = "<group>";
};
FDC4382D27B383A600C60D73 /* Models */ = {
isa = PBXGroup;
children = (
FDC4382E27B383AF00C60D73 /* UnregisterResponse.swift */,
FDC4383027B3841C00C60D73 /* RegisterResponse.swift */,
);
path = Models;
sourceTree = "<group>";
};
FDC4383227B385B200C60D73 /* Models */ = {
isa = PBXGroup;
children = (
FDC4383727B3863200C60D73 /* VersionResponse.swift */,
);
path = Models;
sourceTree = "<group>";
};
FDC4384D27B47FD600C60D73 /* Common Networking */ = {
isa = PBXGroup;
children = (
FDC4385527B484AE00C60D73 /* Models */,
FDC4384E27B4804F00C60D73 /* Header.swift */,
FDC4385027B4807400C60D73 /* QueryParam.swift */,
);
path = "Common Networking";
sourceTree = "<group>";
};
FDC4385527B484AE00C60D73 /* Models */ = {
isa = PBXGroup;
children = (
FDC4385827B484E800C60D73 /* FileUploadBody.swift */,
FDC4385627B484B700C60D73 /* FileUploadResponse.swift */,
FDC4385A27B485DE00C60D73 /* FileDownloadResponse.swift */,
);
path = Models;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -3723,7 +3846,6 @@
C3D9E3FA25676BCE0040E4F3 /* TSYapDatabaseObject.h in Headers */,
C3D9E3A4256763DE0040E4F3 /* AppContext.h in Headers */,
C3D9E38A256760390040E4F3 /* OWSFileSystem.h in Headers */,
C352A3B72557B6ED00338F3E /* TSRequest.h in Headers */,
C32C5A36256DB856003C73A2 /* LKGroupUtilities.h in Headers */,
C3D9E379256760340040E4F3 /* MIMETypeUtil.h in Headers */,
C3D9E50E25677A510040E4F3 /* DataSource.h in Headers */,
@ -4635,7 +4757,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C3AABDDF2553ECF00042FF4C /* Array+Description.swift in Sources */,
C3AABDDF2553ECF00042FF4C /* Array+Utilities.swift in Sources */,
C32C5A47256DB8F0003C73A2 /* ECKeyPair+Hexadecimal.swift in Sources */,
C3D9E41525676C320040E4F3 /* Storage.swift in Sources */,
C32C5D83256DD5B6003C73A2 /* SSKKeychainStorage.swift in Sources */,
@ -4654,17 +4776,17 @@
C3A7211A2558BCA10043A11F /* DiffieHellman.swift in Sources */,
C32C5FA1256DFED5003C73A2 /* NSArray+Functional.m in Sources */,
C3A7225E2558C38D0043A11F /* Promise+Retaining.swift in Sources */,
FD5D202027B0E67900FEA984 /* String+Encoding.swift in Sources */,
C3D9E4D12567777D0040E4F3 /* OWSMediaUtils.swift in Sources */,
C3BBE0AA2554D4DE0050F1E3 /* Dictionary+Description.swift in Sources */,
C3BBE0AA2554D4DE0050F1E3 /* Dictionary+Utilities.swift in Sources */,
C3D9E4DA256778410040E4F3 /* UIImage+OWS.m in Sources */,
C32C600F256E07F5003C73A2 /* NSUserDefaults+OWS.m in Sources */,
C3D9E35E25675F640040E4F3 /* OWSFileSystem.m in Sources */,
C3C2AC2E2553CBEB00C340D1 /* String+Trimming.swift in Sources */,
C32C5B48256DC211003C73A2 /* NSNotificationCenter+OWS.m in Sources */,
C3D9E3C925676AF30040E4F3 /* TSYapDatabaseObject.m in Sources */,
C352A3A62557B60D00338F3E /* TSRequest.m in Sources */,
FD705A92278D051200F16121 /* ReusableView.swift in Sources */,
B8AE75A425A6C6A6001A84D2 /* Data+Trimming.swift in Sources */,
B8AE75A425A6C6A6001A84D2 /* Data+Utilities.swift in Sources */,
B8856DE6256F15F2001CE70E /* String+SSK.swift in Sources */,
C3471ED42555386B00297E91 /* AESGCM.swift in Sources */,
C32C5DDB256DD9FF003C73A2 /* ContentProxy.swift in Sources */,
@ -4699,6 +4821,7 @@
buildActionMask = 2147483647;
files = (
B8856D08256F10F1001CE70E /* DeviceSleepManager.swift in Sources */,
FDC4382A27B3802D00C60D73 /* RoomsResponse.swift in Sources */,
C3471F4C25553AB000297E91 /* MessageReceiver+Decryption.swift in Sources */,
C300A5D32554B05A00555489 /* TypingIndicator.swift in Sources */,
C3A3A156256E1B91004D228D /* ProtoUtils.m in Sources */,
@ -4711,6 +4834,7 @@
FD5D201127AA331F00FEA984 /* ConfigurationMessage+Convenience.swift in Sources */,
C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */,
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */,
FDC4384927B47F4D00C60D73 /* CompactPollResponse.swift in Sources */,
C352A3932557883D00338F3E /* JobDelegate.swift in Sources */,
C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */,
C3A3A108256E1A5C004D228D /* OWSIncomingMessageFinder.m in Sources */,
@ -4718,17 +4842,20 @@
C32C5BDD256DC88D003C73A2 /* OWSReadReceiptManager.m in Sources */,
C3D9E3BF25676AD70040E4F3 /* TSAttachmentStream.m in Sources */,
C3C2A7562553A3AB00C340D1 /* VisibleMessage+Quote.swift in Sources */,
C3227FF6260AAD66006EA627 /* OpenGroupMessageV2.swift in Sources */,
B8B32021258B1A650020074B /* Contact.swift in Sources */,
FDC4380B27B31D7E00C60D73 /* Request.swift in Sources */,
FDC4384027B4746D00C60D73 /* GetInfoResponse.swift in Sources */,
C32C5C89256DD0D2003C73A2 /* Storage+Jobs.swift in Sources */,
C300A5FC2554B0A000555489 /* MessageReceiver.swift in Sources */,
7B1581E2271E743B00848B49 /* OWSSounds.swift in Sources */,
FDC4383E27B4708600C60D73 /* Atomic.swift in Sources */,
C32C5A76256DBBCF003C73A2 /* SignalAttachment.swift in Sources */,
C32C5CA4256DD1DC003C73A2 /* TSAccountManager.m in Sources */,
C352A3892557876500338F3E /* JobQueue.swift in Sources */,
C3BBE0B52554F0E10050F1E3 /* ProofOfWork.swift in Sources */,
C32C59C1256DB41F003C73A2 /* TSGroupThread.m in Sources */,
C3A3A08F256E1728004D228D /* FullTextSearchFinder.swift in Sources */,
FDC4381A27B34EBA00C60D73 /* CompactPollBody.swift in Sources */,
B8856D1A256F114D001CE70E /* ProximityMonitoringManager.swift in Sources */,
C32C5B9F256DC739003C73A2 /* OWSBlockingManager.m in Sources */,
C3D9E52725677DF20040E4F3 /* OWSThumbnailService.swift in Sources */,
@ -4751,41 +4878,56 @@
B8F5F60325EDE16F003BF8D4 /* DataExtractionNotification.swift in Sources */,
C32C5D24256DD4C0003C73A2 /* MentionsManager.swift in Sources */,
C3A71D1E25589AC30043A11F /* WebSocketProto.swift in Sources */,
FDC4384A27B47F4D00C60D73 /* Deletion.swift in Sources */,
C3C2A7852553AAF300C340D1 /* SessionProtos.pb.swift in Sources */,
B8566C63256F55930045A0B9 /* OWSLinkPreview+Conversion.swift in Sources */,
FDC4382827B37FD300C60D73 /* ModeratorsResponse.swift in Sources */,
C32C5B3F256DC1DF003C73A2 /* TSQuotedMessage+Conversion.swift in Sources */,
B8EB20EE2640F28000773E52 /* VisibleMessage+OpenGroupInvitation.swift in Sources */,
FD5D202227B1D74F00FEA984 /* ECKeyPair+Conversion.swift in Sources */,
FDC4381C27B354AC00C60D73 /* PublicKeyBody.swift in Sources */,
FDC4382F27B383AF00C60D73 /* UnregisterResponse.swift in Sources */,
C3C2A7712553A41E00C340D1 /* ControlMessage.swift in Sources */,
C32C5D19256DD493003C73A2 /* OWSLinkPreview.swift in Sources */,
C32C5CF0256DD3E4003C73A2 /* Storage+Shared.swift in Sources */,
C300A5BD2554B00D00555489 /* ReadReceipt.swift in Sources */,
C32C5AB5256DBE8F003C73A2 /* TSOutgoingMessage+Conversion.swift in Sources */,
FDC4385927B484E800C60D73 /* FileUploadBody.swift in Sources */,
C32C5E5B256DDF45003C73A2 /* OWSStorage.m in Sources */,
C32C5E15256DDC78003C73A2 /* SSKPreferences.swift in Sources */,
C32C5D9C256DD6DC003C73A2 /* OWSOutgoingReceiptManager.m in Sources */,
B8AE760B25ABFB5A001A84D2 /* GeneralUtilities.m in Sources */,
C32C5C4F256DCC36003C73A2 /* Storage+OpenGroups.swift in Sources */,
FDC4384727B47F4D00C60D73 /* OpenGroupMessageV2.swift in Sources */,
FDC4384F27B4804F00C60D73 /* Header.swift in Sources */,
C3DA9C0725AE7396008F7C7E /* ConfigurationMessage.swift in Sources */,
B8856CEE256F1054001CE70E /* OWSAudioPlayer.m in Sources */,
FD5D200F27AA2B6000FEA984 /* MessageRequestResponse.swift in Sources */,
FDC4381727B32EC700C60D73 /* Personalization.swift in Sources */,
C32C5EDC256DF501003C73A2 /* YapDatabaseConnection+OWS.m in Sources */,
FDC4381527B329CE00C60D73 /* NonceGenerator16Byte.swift in Sources */,
C3BBE0762554CDA60050F1E3 /* Configuration.swift in Sources */,
C35D76DB26606304009AA5FB /* ThreadUpdateBatcher.swift in Sources */,
FDC4382C27B380E300C60D73 /* MemberCountResponse.swift in Sources */,
B8B32033258B235D0020074B /* Storage+Contacts.swift in Sources */,
B8856D69256F141F001CE70E /* OWSWindowManager.m in Sources */,
C3D9E3BE25676AD70040E4F3 /* TSAttachmentPointer.m in Sources */,
C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */,
C32C5AAB256DBE8F003C73A2 /* TSIncomingMessage+Conversion.swift in Sources */,
B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */,
B866CE112581C1A900535CC4 /* Sodium+Utilities.swift in Sources */,
C32C5A88256DBCF9003C73A2 /* MessageReceiver+Handling.swift in Sources */,
C32C5C1B256DC9E0003C73A2 /* General.swift in Sources */,
C32C5A02256DB658003C73A2 /* MessageSender+Convenience.swift in Sources */,
B8566C6C256F60F50045A0B9 /* OWSUserProfile.m in Sources */,
B8D0A25925E367AC00C1835E /* Notification+MessageReceiver.swift in Sources */,
C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */,
FDC4380927B31D4E00C60D73 /* Error.swift in Sources */,
FDC4382027B36ADC00C60D73 /* Endpoint.swift in Sources */,
C3DB66CC260AF1F3001EFC55 /* OpenGroupAPIV2+ObjC.swift in Sources */,
C32C5BEF256DC8EE003C73A2 /* OWSDisappearingMessagesJob.m in Sources */,
C34A977425A3E34A00852C71 /* ClosedGroupControlMessage.swift in Sources */,
FDC4384C27B47F7700C60D73 /* OpenGroupV2.swift in Sources */,
FDC4382627B37F6900C60D73 /* DeletedMessagesResponse.swift in Sources */,
B88FA7B826045D100049422F /* OpenGroupAPIV2.swift in Sources */,
C32C5E97256DE0CB003C73A2 /* OWSPrimaryStorage.m in Sources */,
C32C5EB9256DE130003C73A2 /* OWSQuotedReplyModel+Conversion.swift in Sources */,
@ -4793,17 +4935,18 @@
B8856E94256F1C37001CE70E /* OWSSounds.m in Sources */,
C32C5BCC256DC830003C73A2 /* Storage+ClosedGroups.swift in Sources */,
C3A3A0EC256E1949004D228D /* OWSRecipientIdentity.m in Sources */,
FDC4383A27B4696200C60D73 /* AuthTokenResponse.swift in Sources */,
B8F5F56525EC8453003BF8D4 /* Notification+Contacts.swift in Sources */,
C32C5AB2256DBE8F003C73A2 /* TSMessage.m in Sources */,
C3A3A0FE256E1A3C004D228D /* TSDatabaseSecondaryIndexes.m in Sources */,
C38D5E8D2575011E00B6A65C /* MessageSender+ClosedGroups.swift in Sources */,
C32C5B1C256DC19D003C73A2 /* TSQuotedMessage.m in Sources */,
C3DB6695260AC923001EFC55 /* OpenGroupV2.swift in Sources */,
C352A349255781F400338F3E /* AttachmentDownloadJob.swift in Sources */,
C352A30925574D8500338F3E /* Message+Destination.swift in Sources */,
C300A5E72554B07300555489 /* ExpirationTimerUpdate.swift in Sources */,
B88A1AC725C90A4700E6D421 /* TypingIndicatorInteraction.swift in Sources */,
C3D9E3C025676AD70040E4F3 /* TSAttachment.m in Sources */,
FDC4384827B47F4D00C60D73 /* RoomInfo.swift in Sources */,
C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */,
C352A2FF25574B6300338F3E /* MessageSendJob.swift in Sources */,
B8856D11256F112A001CE70E /* OWSAudioSession.swift in Sources */,
@ -4824,16 +4967,21 @@
C32C5D23256DD4C0003C73A2 /* Mention.swift in Sources */,
C32C5FD6256E0346003C73A2 /* Notification+Thread.swift in Sources */,
C3BBE0C72554F1570050F1E3 /* FixedWidthInteger+BigEndian.swift in Sources */,
FDC4383827B3863200C60D73 /* VersionResponse.swift in Sources */,
C32C5C88256DD0D2003C73A2 /* Storage+Messaging.swift in Sources */,
C32C59C7256DB41F003C73A2 /* TSThread.m in Sources */,
C300A5B22554AF9800555489 /* VisibleMessage+Profile.swift in Sources */,
C32C5A75256DBBCF003C73A2 /* TSAttachmentPointer+Conversion.swift in Sources */,
C32C5AF8256DC051003C73A2 /* OWSDisappearingMessagesConfiguration.m in Sources */,
C32C5EBA256DE130003C73A2 /* OWSQuotedReplyModel.m in Sources */,
FDC4385127B4807400C60D73 /* QueryParam.swift in Sources */,
C32C5B62256DC333003C73A2 /* OWSDisappearingConfigurationUpdateInfoMessage.m in Sources */,
C352A2F525574B4700338F3E /* Job.swift in Sources */,
FDC4385727B484B700C60D73 /* FileUploadResponse.swift in Sources */,
FDC4385B27B485DE00C60D73 /* FileDownloadResponse.swift in Sources */,
C32C5C01256DC9A0003C73A2 /* OWSIdentityManager.m in Sources */,
C32C59C4256DB41F003C73A2 /* TSContactThread.m in Sources */,
FDC4383127B3841C00C60D73 /* RegisterResponse.swift in Sources */,
C32C5AB0256DBE8F003C73A2 /* TSOutgoingMessage.m in Sources */,
B82A0C3826B9098200C1BCE3 /* MessageInvalidator.swift in Sources */,
);

View File

@ -238,7 +238,7 @@ private final class EnterURLVC : UIViewController, UIGestureRecognizerDelegate,
return !suggestionGrid.frame.contains(location)
}
func join(_ room: OpenGroupAPIV2.Info) {
func join(_ room: OpenGroupAPIV2.RoomInfo) {
joinOpenGroupVC.joinV2OpenGroup(room: room.id, server: OpenGroupAPIV2.defaultServer, publicKey: OpenGroupAPIV2.defaultServerPublicKey)
}

View File

@ -3,7 +3,7 @@ import NVActivityIndicatorView
final class OpenGroupSuggestionGrid : UIView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private let maxWidth: CGFloat
private var rooms: [OpenGroupAPIV2.Info] = [] { didSet { update() } }
private var rooms: [OpenGroupAPIV2.RoomInfo] = [] { didSet { update() } }
private var heightConstraint: NSLayoutConstraint!
var delegate: OpenGroupSuggestionGridDelegate?
@ -104,7 +104,7 @@ final class OpenGroupSuggestionGrid : UIView, UICollectionViewDataSource, UIColl
extension OpenGroupSuggestionGrid {
fileprivate final class Cell : UICollectionViewCell {
var room: OpenGroupAPIV2.Info? { didSet { update() } }
var room: OpenGroupAPIV2.RoomInfo? { didSet { update() } }
static let identifier = "OpenGroupSuggestionGridCell"
@ -183,5 +183,5 @@ extension OpenGroupSuggestionGrid {
// MARK: Delegate
protocol OpenGroupSuggestionGridDelegate {
func join(_ room: OpenGroupAPIV2.Info)
func join(_ room: OpenGroupAPIV2.RoomInfo)
}

View File

@ -0,0 +1,23 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
enum Header: String {
case authorization = "Authorization"
case contentType = "Content-Type"
case room = "Room" // TODO: Confirm this is needed
case sogsPubKey = "X-SOGS-Pubkey"
case sogsNonce = "X-SOGS-Nonce"
case sogsTimestamp = "X-SOGS-Timestamp"
case sogsHash = "X-SOGS-Hash"
}
// MARK: - Convenience
extension Dictionary where Key == Header, Value == String {
func toHTTPHeaders() -> [String: String] {
return self.reduce(into: [:]) { result, next in result[next.key.rawValue] = next.value }
}
}

View File

@ -0,0 +1,35 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
struct FileDownloadResponse: Codable {
enum CodingKeys: String, CodingKey {
case base64EncodedData = "result"
}
let data: Data
public func encode(to encoder: Encoder) throws {
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
try container.encode(data.base64EncodedString(), forKey: .base64EncodedData)
}
}
// MARK: - Decoder
extension FileDownloadResponse {
init(from decoder: Decoder) throws {
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
let base64EncodedData: String = try container.decode(String.self, forKey: .base64EncodedData)
guard let data = Data(base64Encoded: base64EncodedData) else {
throw FileServerAPIV2.Error.parsingFailed
}
self = FileDownloadResponse(
data: data
)
}
}

View File

@ -0,0 +1,7 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
struct FileUploadBody: Codable {
let file: String
}

View File

@ -0,0 +1,11 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
struct FileUploadResponse: Codable {
enum CodingKeys: String, CodingKey {
case fileId = "result"
}
public let fileId: UInt64
}

View File

@ -0,0 +1,8 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
enum QueryParam: String {
case publicKey = "public_key"
case fromServerId = "from_server_id"
}

View File

@ -22,16 +22,16 @@ public final class FileServerAPIV2 : NSObject {
private override init() { }
// MARK: Error
public enum Error : LocalizedError {
public enum Error: LocalizedError {
case parsingFailed
case invalidURL
case maxFileSizeExceeded
public var errorDescription: String? {
switch self {
case .parsingFailed: return "Invalid response."
case .invalidURL: return "Invalid URL."
case .maxFileSizeExceeded: return "Maximum file size exceeded."
case .parsingFailed: return "Invalid response."
case .invalidURL: return "Invalid URL."
case .maxFileSizeExceeded: return "Maximum file size exceeded."
}
}
}
@ -40,49 +40,61 @@ public final class FileServerAPIV2 : NSObject {
private struct Request {
let verb: HTTP.Verb
let endpoint: String
let queryParameters: [String:String]
let parameters: JSON
let headers: [String:String]
let queryParameters: [QueryParam: String]
let body: Data?
let headers: [Header: String]
/// Always `true` under normal circumstances. You might want to disable
/// this when running over Lokinet.
let useOnionRouting: Bool
init(verb: HTTP.Verb, endpoint: String, queryParameters: [String:String] = [:], parameters: JSON = [:],
headers: [String:String] = [:], useOnionRouting: Bool = true) {
init(verb: HTTP.Verb, endpoint: String, queryParameters: [QueryParam: String] = [:], body: Data? = nil,
headers: [Header: String] = [:], useOnionRouting: Bool = true) {
self.verb = verb
self.endpoint = endpoint
self.queryParameters = queryParameters
self.parameters = parameters
self.body = body
self.headers = headers
self.useOnionRouting = useOnionRouting
}
}
// MARK: Convenience
private static func send(_ request: Request, useOldServer: Bool) -> Promise<JSON> {
// MARK: - Convenience
private static func send(_ request: Request, useOldServer: Bool) -> Promise<Data> {
let server = useOldServer ? oldServer : server
let serverPublicKey = useOldServer ? oldServerPublicKey : serverPublicKey
let tsRequest: TSRequest
var urlRequest: URLRequest
// TODO: Combine this 'Request' with the the pattern in OpenGroupServerV2?
switch request.verb {
case .get:
var rawURL = "\(server)/\(request.endpoint)"
if !request.queryParameters.isEmpty {
let queryString = request.queryParameters.map { key, value in "\(key)=\(value)" }.joined(separator: "&")
rawURL += "?\(queryString)"
}
guard let url = URL(string: rawURL) else { return Promise(error: Error.invalidURL) }
tsRequest = TSRequest(url: url)
case .post, .put, .delete:
let rawURL = "\(server)/\(request.endpoint)"
guard let url = URL(string: rawURL) else { return Promise(error: Error.invalidURL) }
tsRequest = TSRequest(url: url, method: request.verb.rawValue, parameters: request.parameters)
case .get:
var rawURL = "\(server)/\(request.endpoint)"
if !request.queryParameters.isEmpty {
let queryString = request.queryParameters.map { key, value in "\(key)=\(value)" }.joined(separator: "&")
rawURL += "?\(queryString)"
}
guard let url = URL(string: rawURL) else { return Promise(error: Error.invalidURL) }
urlRequest = URLRequest(url: url)
case .post, .put, .delete:
let rawURL = "\(server)/\(request.endpoint)"
guard let url = URL(string: rawURL) else { return Promise(error: Error.invalidURL) }
urlRequest = URLRequest(url: url)
urlRequest.httpMethod = request.verb.rawValue
urlRequest.httpBody = request.body
}
tsRequest.allHTTPHeaderFields = request.headers
if request.useOnionRouting {
return OnionRequestAPI.sendOnionRequest(tsRequest, to: server, using: serverPublicKey)
} else {
urlRequest.allHTTPHeaderFields = request.headers.toHTTPHeaders()
guard request.useOnionRouting else {
preconditionFailure("It's currently not allowed to send non onion routed requests.")
}
return OnionRequestAPI.sendOnionRequest(urlRequest, to: server, using: serverPublicKey)
}
// MARK: File Storage
@ -92,12 +104,17 @@ public final class FileServerAPIV2 : NSObject {
}
public static func upload(_ file: Data) -> Promise<UInt64> {
let base64EncodedFile = file.base64EncodedString()
let parameters = [ "file" : base64EncodedFile ]
let request = Request(verb: .post, endpoint: "files", parameters: parameters)
return send(request, useOldServer: false).map(on: DispatchQueue.global(qos: .userInitiated)) { json in
guard let fileID = json["result"] as? UInt64 else { throw Error.parsingFailed }
return fileID
let requestBody: FileUploadBody = FileUploadBody(file: file.base64EncodedString())
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
return Promise(error: HTTP.Error.invalidJSON)
}
let request = Request(verb: .post, endpoint: "files", body: body)
return send(request, useOldServer: false).map(on: DispatchQueue.global(qos: .userInitiated)) { data in
let response: FileUploadResponse = try data.decoded(as: FileUploadResponse.self, customError: Error.parsingFailed)
return response.fileId
}
}
@ -109,17 +126,21 @@ public final class FileServerAPIV2 : NSObject {
public static func download(_ file: UInt64, useOldServer: Bool) -> Promise<Data> {
let request = Request(verb: .get, endpoint: "files/\(file)")
return send(request, useOldServer: useOldServer).map(on: DispatchQueue.global(qos: .userInitiated)) { json in
guard let base64EncodedFile = json["result"] as? String, let file = Data(base64Encoded: base64EncodedFile) else { throw Error.parsingFailed }
return file
return send(request, useOldServer: useOldServer).map(on: DispatchQueue.global(qos: .userInitiated)) { data in
let response: FileDownloadResponse = try data.decoded(as: FileDownloadResponse.self, customError: Error.parsingFailed)
return response.data
}
}
public static func getVersion(_ platform: String) -> Promise<String> {
let request = Request(verb: .get, endpoint: "session_version?platform=\(platform)")
return send(request, useOldServer: false).map(on: DispatchQueue.global(qos: .userInitiated)) { json in
guard let version = json["result"] as? String else { throw Error.parsingFailed }
return version
return send(request, useOldServer: false).map(on: DispatchQueue.global(qos: .userInitiated)) { data in
let response: VersionResponse = try data.decoded(as: VersionResponse.self, customError: Error.parsingFailed)
return response.version
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension FileServerAPIV2 {
struct VersionResponse: Codable {
enum CodingKeys: String, CodingKey {
case version = "version"
}
public let version: String
}
}

View File

@ -3,6 +3,16 @@ import SessionSnodeKit
import SessionUtilitiesKit
public final class NotifyPNServerJob : NSObject, Job, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
struct RequestBody: Codable {
enum CodingKeys: String, CodingKey {
case data
case sendTo = "send_to"
}
let data: String
let sendTo: String
}
public let message: SnodeMessage
public var delegate: JobDelegate?
public var id: String?
@ -32,7 +42,8 @@ public final class NotifyPNServerJob : NSObject, Job, NSCoding { // NSObject/NSC
coder.encode(failureCount, forKey: "failureCount")
}
// MARK: Running
// MARK: - Running
public func execute() {
let _: Promise<Void> = execute()
}
@ -42,10 +53,18 @@ public final class NotifyPNServerJob : NSObject, Job, NSCoding { // NSObject/NSC
JobQueue.currentlyExecutingJobs.insert(id)
}
let server = PushNotificationAPI.server
let parameters = [ "data" : message.data.description, "send_to" : message.recipient ]
let url = URL(string: "\(server)/notify")!
let request = TSRequest(url: url, method: "POST", parameters: parameters)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ]
let requestBody: RequestBody = RequestBody(data: message.data.description, sendTo: message.recipient)
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
return Promise(error: HTTP.Error.invalidJSON)
}
var request: URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.allHTTPHeaderFields = [ Header.contentType.rawValue: "application/json" ]
request.httpBody = body
let promise = attempt(maxRetryCount: 4, recoveringOn: DispatchQueue.global()) {
OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: PushNotificationAPI.serverPublicKey).map { _ in }
}

View File

@ -0,0 +1,46 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
struct AuthTokenResponse: Codable {
struct Challenge: Codable {
enum CodingKeys: String, CodingKey {
case ciphertext = "ciphertext"
case ephemeralPublicKey = "ephemeral_public_key"
}
let ciphertext: Data
let ephemeralPublicKey: Data
}
let challenge: Challenge
}
}
// MARK: - Codable
extension OpenGroupAPIV2.AuthTokenResponse.Challenge {
init(from decoder: Decoder) throws {
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
let base64EncodedCiphertext: String = try container.decode(String.self, forKey: .ciphertext)
let base64EncodedEphemeralPublicKey: String = try container.decode(String.self, forKey: .ephemeralPublicKey)
guard let ciphertext = Data(base64Encoded: base64EncodedCiphertext), let ephemeralPublicKey = Data(base64Encoded: base64EncodedEphemeralPublicKey) else {
throw OpenGroupAPIV2.Error.parsingFailed
}
self = OpenGroupAPIV2.AuthTokenResponse.Challenge(
ciphertext: ciphertext,
ephemeralPublicKey: ephemeralPublicKey
)
}
public func encode(to encoder: Encoder) throws {
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
try container.encode(ciphertext.base64EncodedString(), forKey: .ciphertext)
try container.encode(ephemeralPublicKey.base64EncodedString(), forKey: .ephemeralPublicKey)
}
}

View File

@ -0,0 +1,27 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
struct CompactPollBody: Codable {
struct Room: Codable {
enum CodingKeys: String, CodingKey {
case id = "room_id"
case fromMessageServerId = "from_message_server_id"
case fromDeletionServerId = "from_deletion_server_id"
// TODO: Remove this legacy value
case legacyAuthToken = "auth_token"
}
let id: String
let fromMessageServerId: Int64?
let fromDeletionServerId: Int64?
// TODO: This is a legacy value
let legacyAuthToken: String?
}
let requests: [Room]
}
}

View File

@ -0,0 +1,25 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
public struct CompactPollResponse: Codable {
public struct Result: Codable {
enum CodingKeys: String, CodingKey {
case room = "room_id"
case statusCode = "status_code"
case messages
case deletions
case moderators
}
public let room: String
public let statusCode: UInt
public let messages: [OpenGroupMessageV2]?
public let deletions: [Deletion]?
public let moderators: [String]?
}
public let results: [Result]
}
}

View File

@ -0,0 +1,13 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
struct DeletedMessagesResponse: Codable {
enum CodingKeys: String, CodingKey {
case deletions = "ids"
}
let deletions: [Deletion]
}
}

View File

@ -0,0 +1,23 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
public struct Deletion: Codable {
enum CodingKeys: String, CodingKey {
case id
case deletedMessageID = "deleted_message_id"
}
let id: Int64
let deletedMessageID: Int64
public static func from(_ json: JSON) -> Deletion? {
guard let id = json["id"] as? Int64, let deletedMessageID = json["deleted_message_id"] as? Int64 else {
return nil
}
return Deletion(id: id, deletedMessageID: deletedMessageID)
}
}
}

View File

@ -0,0 +1,9 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
struct GetInfoResponse: Codable {
let room: RoomInfo
}
}

View File

@ -0,0 +1,13 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
struct MemberCountResponse: Codable {
enum CodingKeys: String, CodingKey {
case memberCount = "member_count"
}
let memberCount: UInt64
}
}

View File

@ -0,0 +1,9 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
struct ModeratorsResponse: Codable {
let moderators: [String]
}
}

View File

@ -0,0 +1,72 @@
import Foundation
import SessionUtilitiesKit
public struct OpenGroupMessageV2: Codable {
enum CodingKeys: String, CodingKey {
case serverID = "server_id"
case sender = "public_key"
case sentTimestamp = "timestamp"
case base64EncodedData = "data"
case base64EncodedSignature = "signature"
}
public let serverID: Int64?
public let sender: String?
public let sentTimestamp: UInt64
/// The serialized protobuf in base64 encoding.
public let base64EncodedData: String
/// When sending a message, the sender signs the serialized protobuf with their private key so that
/// a receiving user can verify that the message wasn't tampered with.
public let base64EncodedSignature: String?
public func sign(with publicKey: String) -> OpenGroupMessageV2? {
// TODO: Swap to use blinded key
guard let userKeyPair = SNMessagingKitConfiguration.shared.storage.getUserKeyPair() else { return nil }
guard let data = Data(base64Encoded: base64EncodedData) else { return nil }
guard let signature = try? Ed25519.sign(data, with: userKeyPair) else {
SNLog("Failed to sign open group message.")
return nil
}
return OpenGroupMessageV2(
serverID: serverID,
sender: sender,
sentTimestamp: sentTimestamp,
base64EncodedData: base64EncodedData,
base64EncodedSignature: signature.base64EncodedString()
)
}
}
// MARK: - Decoder
extension OpenGroupMessageV2 {
public init(from decoder: Decoder) throws {
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
let sender: String = try container.decode(String.self, forKey: .sender)
let base64EncodedData: String = try container.decode(String.self, forKey: .base64EncodedData)
let base64EncodedSignature: String = try container.decode(String.self, forKey: .base64EncodedSignature)
// Validate the message signature
guard let data = Data(base64Encoded: base64EncodedData), let signature = Data(base64Encoded: base64EncodedSignature) else {
throw OpenGroupAPIV2.Error.parsingFailed
}
let publicKey = Data(hex: sender.removingIdPrefixIfNeeded())
let isValid = (try? Ed25519.verifySignature(signature, publicKey: publicKey, data: data)) ?? false
guard isValid else {
SNLog("Ignoring message with invalid signature.")
throw OpenGroupAPIV2.Error.parsingFailed
}
self = OpenGroupMessageV2(
serverID: try? container.decode(Int64.self, forKey: .serverID),
sender: sender,
sentTimestamp: try container.decode(UInt64.self, forKey: .sentTimestamp),
base64EncodedData: base64EncodedData,
base64EncodedSignature: base64EncodedSignature
)
}
}

View File

@ -1,3 +1,5 @@
import Sodium
import SessionUtilitiesKit
@objc(SNOpenGroupV2)
public final class OpenGroupV2 : NSObject, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility

View File

@ -0,0 +1,13 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
struct PublicKeyBody: Codable {
enum CodingKeys: String, CodingKey {
case publicKey = "public_key"
}
let publicKey: String
}
}

View File

@ -0,0 +1,17 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
public struct RoomInfo: Codable {
enum CodingKeys: String, CodingKey {
case id
case name
case imageID = "image_id"
}
public let id: String
public let name: String
public let imageID: String?
}
}

View File

@ -0,0 +1,9 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
struct RoomsResponse: Codable {
let rooms: [RoomInfo]
}
}

File diff suppressed because it is too large Load Diff

View File

@ -82,6 +82,7 @@ public final class OpenGroupManagerV2 : NSObject {
public func delete(_ openGroup: OpenGroupV2, associatedWith thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) {
let storage = SNMessagingKitConfiguration.shared.storage
// Stop the poller if needed
let openGroups = storage.getAllV2OpenGroups().values.filter { $0.server == openGroup.server }
if openGroups.count == 1 && openGroups.last == openGroup {
@ -89,6 +90,7 @@ public final class OpenGroupManagerV2 : NSObject {
poller?.stop()
pollers[openGroup.server] = nil
}
// Remove all data
var messageIDs: Set<String> = []
var messageTimestamps: Set<UInt64> = []
@ -101,10 +103,14 @@ public final class OpenGroupManagerV2 : NSObject {
Storage.shared.removeLastMessageServerID(for: openGroup.room, on: openGroup.server, using: transaction)
Storage.shared.removeLastDeletionServerID(for: openGroup.room, on: openGroup.server, using: transaction)
let _ = OpenGroupAPIV2.deleteAuthToken(for: openGroup.room, on: openGroup.server)
Storage.shared.removeOpenGroupPublicKey(for: openGroup.server, using: transaction)
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
Storage.shared.removeV2OpenGroup(for: thread.uniqueId!, using: transaction)
// Only remove the open group public key if the user isn't in any other rooms
if openGroups.count <= 1 {
Storage.shared.removeOpenGroupPublicKey(for: openGroup.server, using: transaction)
}
}
// MARK: Convenience

View File

@ -1,38 +0,0 @@
public struct OpenGroupMessageV2 {
public let serverID: Int64?
public let sender: String?
public let sentTimestamp: UInt64
/// The serialized protobuf in base64 encoding.
public let base64EncodedData: String
/// When sending a message, the sender signs the serialized protobuf with their private key so that
/// a receiving user can verify that the message wasn't tampered with.
public let base64EncodedSignature: String?
public func sign() -> OpenGroupMessageV2? {
let userKeyPair = SNMessagingKitConfiguration.shared.storage.getUserKeyPair()!
let data = Data(base64Encoded: base64EncodedData)!
guard let signature = try? Ed25519.sign(data, with: userKeyPair) else {
SNLog("Failed to sign open group message.")
return nil
}
return OpenGroupMessageV2(serverID: serverID, sender: sender, sentTimestamp: sentTimestamp,
base64EncodedData: base64EncodedData, base64EncodedSignature: signature.base64EncodedString())
}
public func toJSON() -> JSON? {
var result: JSON = [ "data" : base64EncodedData, "timestamp" : sentTimestamp ]
if let serverID = serverID { result["server_id"] = serverID }
if let sender = sender { result["public_key"] = sender }
if let base64EncodedSignature = base64EncodedSignature { result["signature"] = base64EncodedSignature }
return result
}
public static func fromJSON(_ json: JSON) -> OpenGroupMessageV2? {
guard let base64EncodedData = json["data"] as? String, let sentTimestamp = json["timestamp"] as? UInt64 else { return nil }
let serverID = json["server_id"] as? Int64
let sender = json["public_key"] as? String
let base64EncodedSignature = json["signature"] as? String
return OpenGroupMessageV2(serverID: serverID, sender: sender, sentTimestamp: sentTimestamp, base64EncodedData: base64EncodedData, base64EncodedSignature: base64EncodedSignature)
}
}

View File

@ -0,0 +1,83 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
enum Endpoint {
case files
case file(UInt64)
case messages
case messagesForServer(Int64)
case deletedMessages
case moderators
case blockList
case blockListIndividual(String)
case banAndDeleteAll
case rooms
case roomInfo(String)
case roomImage(String)
// Legacy endpoints (to be deprecated and removed)
case legacyCompactPoll(legacyAuth: Bool)
case legacyAuthToken(legacyAuth: Bool)
case legacyAuthTokenChallenge(legacyAuth: Bool)
case legacyAuthTokenClaim(legacyAuth: Bool)
case legacyMemberCount(legacyAuth: Bool)
var path: String {
switch self {
case .files: return "files"
case .file(let fileId): return "files/\(fileId)"
case .messages: return "messages"
case .messagesForServer(let serverId): return "messages/\(serverId)"
case .deletedMessages: return "deleted_messages"
case .moderators: return "moderators"
case .blockList: return "block_list"
case .blockListIndividual(let publicKey): return "block_list/\(publicKey)"
case .banAndDeleteAll: return "ban_and_delete_all"
case .rooms: return "rooms"
case .roomInfo(let roomName): return "rooms/\(roomName)"
case .roomImage(let roomName): return "rooms/\(roomName)/image"
// Legacy endpoints (to be deprecated and removed)
// TODO: Look for a nicer way to prepend 'legacy'? (OnionRequestAPI messes with this but the new auth needs it to be correct...)
case .legacyCompactPoll(let useLegacyAuth):
return "\(useLegacyAuth ? "" : "legacy/")compact_poll"
case .legacyAuthToken(let useLegacyAuth):
return "\(useLegacyAuth ? "" : "legacy/")auth_token"
case .legacyAuthTokenChallenge(let useLegacyAuth):
return "\(useLegacyAuth ? "" : "legacy/")auth_token_challenge"
case .legacyAuthTokenClaim(let useLegacyAuth):
return "\(useLegacyAuth ? "" : "legacy/")claim_auth_token"
case .legacyMemberCount(let useLegacyAuth):
return "\(useLegacyAuth ? "" : "legacy/")member_count"
}
}
var useLegacyAuth: Bool {
switch self {
// File upload/download should use legacy auth
case .files, .file: return true
case .legacyCompactPoll(let useLegacyAuth),
.legacyAuthToken(let useLegacyAuth),
.legacyAuthTokenChallenge(let useLegacyAuth),
.legacyAuthTokenClaim(let useLegacyAuth),
.legacyMemberCount(let useLegacyAuth):
return useLegacyAuth
default: return false
}
}
}

View File

@ -0,0 +1,25 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
public enum Error: LocalizedError {
case generic
case parsingFailed
case decryptionFailed
case signingFailed
case invalidURL
case noPublicKey
public var errorDescription: String? {
switch self {
case .generic: return "An error occurred."
case .parsingFailed: return "Invalid response."
case .decryptionFailed: return "Couldn't decrypt response."
case .signingFailed: return "Couldn't sign message."
case .invalidURL: return "Invalid URL."
case .noPublicKey: return "Couldn't find server public key."
}
}
}
}

View File

@ -0,0 +1,9 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Sodium
extension OpenGroupAPIV2 {
class NonceGenerator16Byte: NonceGenerator {
var NonceBytes: Int { 16 }
}
}

View File

@ -0,0 +1,15 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Sodium
extension OpenGroupAPIV2 {
public enum Personalization: String {
case sharedKeys = "sogs.shared_keys"
case authHeader = "sogs.auth_header"
var bytes: Bytes {
return self.rawValue.bytes
}
}
}

View File

@ -0,0 +1,57 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
struct Request {
let verb: HTTP.Verb
let room: String?
let server: String
let endpoint: Endpoint
let queryParameters: [QueryParam: String]
let body: Data?
let headers: [Header: String]
let isAuthRequired: Bool
/// Always `true` under normal circumstances. You might want to disable
/// this when running over Lokinet.
let useOnionRouting: Bool
init(
verb: HTTP.Verb,
room: String?,
server: String,
endpoint: Endpoint,
queryParameters: [QueryParam: String] = [:],
body: Data? = nil,
headers: [Header: String] = [:],
isAuthRequired: Bool = true,
useOnionRouting: Bool = true
) {
self.verb = verb
self.room = room
self.server = server
self.endpoint = endpoint
self.queryParameters = queryParameters
self.body = body
self.headers = headers
self.isAuthRequired = isAuthRequired
self.useOnionRouting = useOnionRouting
}
var url: URL? {
guard verb == .get else { return URL(string: "\(server)/\(endpoint.path)") }
return URL(
string: [
"\(server)/\(endpoint.path)",
queryParameters
.map { key, value in "\(key.rawValue)=\(value)" }
.joined(separator: "&")
]
.compactMap { $0 }
.filter { !$0.isEmpty }
.joined(separator: "?")
)
}
}
}

View File

@ -257,7 +257,8 @@ public final class MessageSender : NSObject {
return promise
}
// MARK: Open Groups
// MARK: - Open Groups
internal static func sendToOpenGroupDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> {
let (promise, seal) = Promise<Void>.pending()
let storage = SNMessagingKitConfiguration.shared.storage
@ -266,7 +267,15 @@ public final class MessageSender : NSObject {
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
message.sentTimestamp = NSDate.millisecondTimestamp()
}
message.sender = storage.getUserPublicKey()
guard let threadId: String = message.threadID, let openGroupV2 = Storage.shared.getV2OpenGroup(for: threadId) else {
preconditionFailure()
}
if let userDerivedKey: ECKeyPair = try? OWSIdentityManager.shared().identityKeyPair()?.convert(to: .blinded, with: openGroupV2.publicKey) {
message.sender = userDerivedKey.hexEncodedPublicKey
}
switch destination {
case .contact(_): preconditionFailure()
case .closedGroup(_): preconditionFailure()
@ -308,9 +317,12 @@ public final class MessageSender : NSObject {
}
// Send the result
guard case .openGroupV2(let room, let server) = destination else { preconditionFailure() }
// TODO: Determine if the 'getV2OpenGroup' call will cause issues
guard let threadId: String = message.threadID, let openGroupV2 = Storage.shared.getV2OpenGroup(for: threadId) else { preconditionFailure() }
let openGroupMessage = OpenGroupMessageV2(serverID: nil, sender: nil, sentTimestamp: message.sentTimestamp!,
base64EncodedData: plaintext.base64EncodedString(), base64EncodedSignature: nil)
OpenGroupAPIV2.send(openGroupMessage, to: room, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in
OpenGroupAPIV2.send(openGroupMessage, to: room, on: server, with: openGroupV2.publicKey).done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in
message.openGroupServerMessageID = given(openGroupMessage.serverID) { UInt64($0) }
storage.write(with: { transaction in
MessageSender.handleSuccessfulMessageSend(message, to: destination, serverTimestamp: openGroupMessage.sentTimestamp, using: transaction)

View File

@ -0,0 +1,11 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension PushNotificationAPI {
struct RegisterResponse: Codable {
let body: String
let code: Int
let message: String?
}
}

View File

@ -0,0 +1,11 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension PushNotificationAPI {
struct UnregisterResponse: Codable {
let body: String
let code: Int
let message: String?
}
}

View File

@ -3,10 +3,20 @@ import PromiseKit
@objc(LKPushNotificationAPI)
public final class PushNotificationAPI : NSObject {
struct RequestBody: Codable {
let token: String
let pubKey: String?
}
struct ClosedGroupRequestBody: Codable {
let token: String
let pubKey: String
}
// MARK: Settings
// MARK: - Settings
public static let server = "https://live.apns.getsession.org"
public static let serverPublicKey = "642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049"
private static let maxRetryCount: UInt = 4
private static let tokenExpirationInterval: TimeInterval = 12 * 60 * 60
@ -15,29 +25,38 @@ public final class PushNotificationAPI : NSObject {
public var endpoint: String {
switch self {
case .subscribe: return "subscribe_closed_group"
case .unsubscribe: return "unsubscribe_closed_group"
case .subscribe: return "subscribe_closed_group"
case .unsubscribe: return "unsubscribe_closed_group"
}
}
}
// MARK: Initialization
// MARK: - Initialization
private override init() { }
// MARK: Registration
// MARK: - Registration
public static func unregister(_ token: Data) -> Promise<Void> {
let hexEncodedToken = token.toHexString()
let parameters = [ "token" : hexEncodedToken ]
let requestBody: RequestBody = RequestBody(token: token.toHexString(), pubKey: nil)
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
return Promise(error: HTTP.Error.invalidJSON)
}
let url = URL(string: "\(server)/unregister")!
let request = TSRequest(url: url, method: "POST", parameters: parameters)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ]
var request: URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.allHTTPHeaderFields = [ Header.contentType.rawValue: "application/json" ]
request.httpBody = body
let promise: Promise<Void> = attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global()) {
OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: serverPublicKey).map2 { response in
guard let json = response["body"] as? JSON else {
guard let response: UnregisterResponse = try? response.decoded(as: UnregisterResponse.self) else {
return SNLog("Couldn't unregister from push notifications.")
}
guard json["code"] as? Int != 0 else {
return SNLog("Couldn't unregister from push notifications due to error: \(json["message"] as? String ?? "nil").")
guard response.code != 0 else {
return SNLog("Couldn't unregister from push notifications due to error: \(response.message ?? "nil").")
}
}
}
@ -57,7 +76,13 @@ public final class PushNotificationAPI : NSObject {
}
public static func register(with token: Data, publicKey: String, isForcedUpdate: Bool) -> Promise<Void> {
let hexEncodedToken = token.toHexString()
let hexEncodedToken: String = token.toHexString()
let requestBody: RequestBody = RequestBody(token: hexEncodedToken, pubKey: publicKey)
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
return Promise(error: HTTP.Error.invalidJSON)
}
let userDefaults = UserDefaults.standard
let oldToken = userDefaults[.deviceToken]
let lastUploadTime = userDefaults[.lastDeviceTokenUpload]
@ -66,18 +91,22 @@ public final class PushNotificationAPI : NSObject {
SNLog("Device token hasn't changed or expired; no need to re-upload.")
return Promise<Void> { $0.fulfill(()) }
}
let parameters = [ "token" : hexEncodedToken, "pubKey" : publicKey ]
let url = URL(string: "\(server)/register")!
let request = TSRequest(url: url, method: "POST", parameters: parameters)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ]
var request: URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.allHTTPHeaderFields = [ Header.contentType.rawValue: "application/json" ]
request.httpBody = body
let promise: Promise<Void> = attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global()) {
OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: serverPublicKey).map2 { response in
guard let json = response["body"] as? JSON else {
guard let response: RegisterResponse = try? response.decoded(as: RegisterResponse.self) else {
return SNLog("Couldn't register device token.")
}
guard json["code"] as? Int != 0 else {
return SNLog("Couldn't register device token due to error: \(json["message"] as? String ?? "nil").")
guard response.code != 0 else {
return SNLog("Couldn't register device token due to error: \(response.message ?? "nil").")
}
userDefaults[.deviceToken] = hexEncodedToken
userDefaults[.lastDeviceTokenUpload] = now
userDefaults[.isUsingFullAPNs] = true
@ -101,18 +130,26 @@ public final class PushNotificationAPI : NSObject {
@discardableResult
public static func performOperation(_ operation: ClosedGroupOperation, for closedGroupPublicKey: String, publicKey: String) -> Promise<Void> {
let isUsingFullAPNs = UserDefaults.standard[.isUsingFullAPNs]
let requestBody: ClosedGroupRequestBody = ClosedGroupRequestBody(token: closedGroupPublicKey, pubKey: publicKey)
guard isUsingFullAPNs else { return Promise<Void> { $0.fulfill(()) } }
let parameters = [ "closedGroupPublicKey" : closedGroupPublicKey, "pubKey" : publicKey ]
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
return Promise(error: HTTP.Error.invalidJSON)
}
let url = URL(string: "\(server)/\(operation.endpoint)")!
let request = TSRequest(url: url, method: "POST", parameters: parameters)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ]
var request: URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.allHTTPHeaderFields = [ Header.contentType.rawValue: "application/json" ]
request.httpBody = body
let promise: Promise<Void> = attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global()) {
OnionRequestAPI.sendOnionRequest(request, to: server, target: "/loki/v2/lsrpc", using: serverPublicKey).map2 { response in
guard let json = response["body"] as? JSON else {
guard let response: RegisterResponse = try? response.decoded(as: RegisterResponse.self) else {
return SNLog("Couldn't subscribe/unsubscribe for closed group: \(closedGroupPublicKey).")
}
guard json["code"] as? Int != 0 else {
return SNLog("Couldn't subscribe/unsubscribe for closed group: \(closedGroupPublicKey) due to error: \(json["message"] as? String ?? "nil").")
guard response.code != 0 else {
return SNLog("Couldn't subscribe/unsubscribe for closed group: \(closedGroupPublicKey) due to error: \(response.message ?? "nil").")
}
}
}

View File

@ -46,25 +46,32 @@ public final class OpenGroupPollerV2 : NSObject {
self.isPolling = true
let (promise, seal) = Promise<Void>.pending()
promise.retainUntilComplete()
OpenGroupAPIV2.compactPoll(server).done(on: OpenGroupAPIV2.workQueue) { [weak self] bodies in
guard let self = self else { return }
self.isPolling = false
bodies.forEach { self.handleCompactPollBody($0, isBackgroundPoll: isBackgroundPoll) }
seal.fulfill(())
}.catch(on: OpenGroupAPIV2.workQueue) { error in
SNLog("Open group polling failed due to error: \(error).")
self.isPolling = false
seal.fulfill(()) // The promise is just used to keep track of when we're done
}
// TODO: Update to use the non-legacy version
// OpenGroupAPIV2.compactPoll(server)
OpenGroupAPIV2.legacyCompactPoll(server)
.done(on: OpenGroupAPIV2.workQueue) { [weak self] response in
guard let self = self else { return }
self.isPolling = false
response.results.forEach { self.handleCompactPollBody($0, isBackgroundPoll: isBackgroundPoll) }
seal.fulfill(())
}
.catch(on: OpenGroupAPIV2.workQueue) { error in
SNLog("Open group polling failed due to error: \(error).")
self.isPolling = false
seal.fulfill(()) // The promise is just used to keep track of when we're done
}
return promise
}
private func handleCompactPollBody(_ body: OpenGroupAPIV2.CompactPollResponseBody, isBackgroundPoll: Bool) {
private func handleCompactPollBody(_ body: OpenGroupAPIV2.CompactPollResponse.Result, isBackgroundPoll: Bool) {
let storage = SNMessagingKitConfiguration.shared.storage
// - Messages
// Sorting the messages by server ID before importing them fixes an issue where messages that quote older messages can't find those older messages
let openGroupID = "\(server).\(body.room)"
let messages = body.messages.sorted { $0.serverID! < $1.serverID! } // Safe because messages with a nil serverID are filtered out
let messages = (body.messages ?? []).sorted { ($0.serverID ?? 0) < ($1.serverID ?? 0) }
storage.write { transaction in
messages.forEach { message in
guard let data = Data(base64Encoded: message.base64EncodedData) else {
@ -82,24 +89,29 @@ public final class OpenGroupPollerV2 : NSObject {
}
}
}
// - Moderators
if var x = OpenGroupAPIV2.moderators[server] {
x[body.room] = Set(body.moderators)
x[body.room] = Set(body.moderators ?? [])
OpenGroupAPIV2.moderators[server] = x
} else {
OpenGroupAPIV2.moderators[server] = [ body.room : Set(body.moderators) ]
}
else {
OpenGroupAPIV2.moderators[server] = [ body.room : Set(body.moderators ?? []) ]
}
// - Deletions
let deletedMessageServerIDs = Set(body.deletions.map { UInt64($0.deletedMessageID) })
let deletedMessageServerIDs = Set((body.deletions ?? []).map { UInt64($0.deletedMessageID) })
storage.write { transaction in
let transaction = transaction as! YapDatabaseReadWriteTransaction
guard let threadID = storage.v2GetThreadID(for: openGroupID),
let thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction) else { return }
var messagesToRemove: [TSMessage] = []
thread.enumerateInteractions(with: transaction) { interaction, stop in
guard let message = interaction as? TSMessage, deletedMessageServerIDs.contains(message.openGroupServerMessageID) else { return }
messagesToRemove.append(message)
}
messagesToRemove.forEach { $0.remove(with: transaction) }
}
}

View File

@ -0,0 +1,27 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
/// The `Atomic<T>` wrapper is a generic wrapper providing a thread-safe way to get and set a value
@propertyWrapper
struct Atomic<Value> {
private let lock = DispatchSemaphore(value: 1)
private var value: Value
init(_ initialValue: Value) {
self.value = initialValue
}
var wrappedValue: Value {
get {
lock.wait()
defer { lock.signal() }
return value
}
set {
lock.wait()
value = newValue
lock.signal()
}
}
}

View File

@ -0,0 +1,34 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Curve25519Kit
import SessionUtilitiesKit
import Sodium
public extension ECKeyPair {
func convert(to targetPrefix: IdPrefix, with otherKey: String, using sodium: Sodium = Sodium()) throws -> ECKeyPair? {
guard let publicKeyPrefix: IdPrefix = IdPrefix(with: hexEncodedPublicKey) else { return nil }
switch (publicKeyPrefix, targetPrefix) {
case (.standard, .blinded): // Only support standard -> blinded conversions
// TODO: Figure out why this is broken...
// guard let otherPubKeyData: Data = otherKey.data(using: .utf8) else { return nil }
guard let otherPubKeyData: Data = otherKey.dataFromHex() else { return nil }
guard let otherPubKeyHashBytes: Bytes = sodium.genericHash.hash(message: [UInt8](otherPubKeyData)) else {
return nil
}
guard let blindedPublicKey: Sodium.SharedSecret = sodium.sharedSecret(otherPubKeyHashBytes, [UInt8](publicKey)) else {
return nil
}
guard let blindedPrivateKey: Sodium.SharedSecret = sodium.sharedSecret(otherPubKeyHashBytes, [UInt8](privateKey)) else {
return nil
}
return try BlindedECKeyPair(publicKeyData: blindedPublicKey, privateKeyData: blindedPrivateKey)
case (.standard, .standard): return self
case (.blinded, .blinded): return self
default: return nil
}
}
}

View File

@ -41,22 +41,27 @@ extension Sign {
}
extension Sodium {
public typealias SOGSDerivedKey = Data
public typealias SharedSecret = Data
private static let publicKeyBytes: Int = Int(crypto_scalarmult_bytes())
private static let sharedSecretBytes: Int = Int(crypto_scalarmult_bytes())
public func derivedKey(serverPublicKeyBytes: [UInt8], userKeyBytes: [UInt8]) -> SOGSDerivedKey? {
guard serverPublicKeyBytes.count == Sodium.publicKeyBytes && userKeyBytes.count == Sodium.publicKeyBytes else { return nil }
public func sharedSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> SharedSecret? {
guard firstKeyBytes.count == Sodium.publicKeyBytes && secondKeyBytes.count == Sodium.publicKeyBytes else {
return nil
}
let sharedSecretPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.sharedSecretBytes)
let result = userKeyBytes.withUnsafeBytes { (userPublicKeyPtr: UnsafeRawBufferPointer) in
return serverPublicKeyBytes.withUnsafeBytes { (serverPublicKeyPtr: UnsafeRawBufferPointer) -> Int32 in
guard let serverKeyBaseAddress: UnsafePointer<UInt8> = serverPublicKeyPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), let userKeyBaseAddress: UnsafePointer<UInt8> = userPublicKeyPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
let result = secondKeyBytes.withUnsafeBytes { (secondKeyPtr: UnsafeRawBufferPointer) -> Int32 in
return firstKeyBytes.withUnsafeBytes { (firstKeyPtr: UnsafeRawBufferPointer) -> Int32 in
guard let firstKeyBaseAddress: UnsafePointer<UInt8> = firstKeyPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return -1
}
guard let secondKeyBaseAddress: UnsafePointer<UInt8> = secondKeyPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return -1
}
return crypto_scalarmult(sharedSecretPtr, serverKeyBaseAddress, userKeyBaseAddress)
return crypto_scalarmult(sharedSecretPtr, firstKeyBaseAddress, secondKeyBaseAddress)
}
}
@ -65,3 +70,30 @@ extension Sodium {
return Data(bytes: sharedSecretPtr, count: Sodium.sharedSecretBytes)
}
}
extension GenericHash {
public func hashSaltPersonal(
message: Bytes,
outputLength: Int,
key: Bytes? = nil,
salt: Bytes,
personal: Bytes
) -> Bytes? {
var output: [UInt8] = [UInt8](repeating: 0, count: outputLength)
let result = crypto_generichash_blake2b_salt_personal(
&output,
outputLength,
message,
UInt64(message.count),
key,
(key?.count ?? 0),
salt,
personal
)
guard result == 0 else { return nil }
return output
}
}

View File

@ -1,3 +1,4 @@
import Foundation
import CryptoSwift
import PromiseKit
import SessionUtilitiesKit
@ -301,54 +302,54 @@ public enum OnionRequestAPI {
// MARK: Public API
/// Sends an onion request to `snode`. Builds new paths as needed.
public static func sendOnionRequest(to snode: Snode, invoking method: Snode.Method, with parameters: JSON, associatedWith publicKey: String? = nil) -> Promise<JSON> {
public static func sendOnionRequest(to snode: Snode, invoking method: Snode.Method, with parameters: JSON, associatedWith publicKey: String? = nil) -> Promise<Data> {
let payload: JSON = [ "method" : method.rawValue, "params" : parameters ]
return sendOnionRequest(with: payload, to: Destination.snode(snode)).recover2 { error -> Promise<JSON> in
return sendOnionRequest(with: payload, to: Destination.snode(snode)).recover2 { error -> Promise<Data> in
guard case OnionRequestAPI.Error.httpRequestFailedAtDestination(let statusCode, let json, _) = error else { throw error }
throw SnodeAPI.handleError(withStatusCode: statusCode, json: json, forSnode: snode, associatedWith: publicKey) ?? error
}
}
/// Sends an onion request to `server`. Builds new paths as needed.
public static func sendOnionRequest(_ request: NSURLRequest, to server: String, target: String = "/loki/v3/lsrpc", using x25519PublicKey: String) -> Promise<JSON> {
var rawHeaders = request.allHTTPHeaderFields ?? [:]
rawHeaders.removeValue(forKey: "User-Agent")
var headers: JSON = rawHeaders.mapValues { value in
switch value.lowercased() {
case "true": return true
case "false": return false
default: return value
}
}
public static func sendOnionRequest(_ request: URLRequest, to server: String, target: String = "/loki/v3/lsrpc", using x25519PublicKey: String) -> Promise<Data> {
guard let url = request.url, let host = request.url?.host else { return Promise(error: Error.invalidURL) }
var endpoint = url.path.removingPrefix("/")
if let query = url.query { endpoint += "?\(query)" }
let scheme = url.scheme
let port = given(url.port) { UInt16($0) }
let parametersAsString: String
if let tsRequest = request as? TSRequest {
headers["Content-Type"] = "application/json"
let tsRequestParameters = tsRequest.parameters
if !tsRequestParameters.isEmpty {
guard let parameters = try? JSONSerialization.data(withJSONObject: tsRequestParameters, options: [ .fragmentsAllowed ]) else {
return Promise(error: HTTP.Error.invalidJSON)
var headers: JSON = (request.allHTTPHeaderFields ?? [:])
.mapValues { value -> Any in
switch value.lowercased() {
case "true": return true
case "false": return false
default: return value
}
parametersAsString = String(bytes: parameters, encoding: .utf8) ?? "null"
} else {
parametersAsString = "null"
}
} else {
headers["Content-Type"] = request.allHTTPHeaderFields!["Content-Type"]
if let parametersAsInputStream = request.httpBodyStream, let parameters = try? Data(from: parametersAsInputStream) {
parametersAsString = "{ \"fileUpload\" : \"\(String(data: parameters.base64EncodedData(), encoding: .utf8) ?? "null")\" }"
} else {
parametersAsString = "null"
}
.removingValue(forKey: "User-Agent")
// Note: We need to remove the leading forward slash unless we are explicitly hitting a legacy
// endpoint (in which case we need it to ensure the request signing works correctly
// TODO: Confirm the 'removingPrefix' isn't going to break the request signing on non-legacy endpoints
let endpoint: String = url.path
.removingPrefix("/", if: !url.path.starts(with: "/legacy"))
.appending(url.query.map { value in "?\(value)" })
let scheme: String? = url.scheme
let port: UInt16? = url.port.map { UInt16($0) }
let bodyAsString: String
if let body: Data = request.httpBody {
headers["Content-Type"] = "application/json" // Assume data is JSON
bodyAsString = (String(data: body, encoding: .utf8) ?? "null")
}
else if let inputStream: InputStream = request.httpBodyStream, let body: Data = try? Data(from: inputStream) {
headers["Content-Type"] = request.allHTTPHeaderFields!["Content-Type"]
bodyAsString = "{ \"fileUpload\" : \"\(String(data: body.base64EncodedData(), encoding: .utf8) ?? "null")\" }"
}
else {
bodyAsString = "null"
}
let payload: JSON = [
"body" : parametersAsString,
"body" : bodyAsString,
"endpoint" : endpoint,
"method" : request.httpMethod!,
"method" : (request.httpMethod ?? "GET"), // Default (if nil) is 'GET'
"headers" : headers
]
let destination = Destination.server(host: host, target: target, x25519PublicKey: x25519PublicKey, scheme: scheme, port: port)
@ -359,8 +360,8 @@ public enum OnionRequestAPI {
return promise
}
public static func sendOnionRequest(with payload: JSON, to destination: Destination) -> Promise<JSON> {
let (promise, seal) = Promise<JSON>.pending()
public static func sendOnionRequest(with payload: JSON, to destination: Destination) -> Promise<Data> {
let (promise, seal) = Promise<Data>.pending()
var guardSnode: Snode?
Threading.workQueue.async { // Avoid race conditions on `guardSnodes` and `paths`
buildOnion(around: payload, targetedAt: destination).done2 { intermediate in
@ -401,12 +402,12 @@ public enum OnionRequestAPI {
guard 200...299 ~= statusCode else {
return seal.reject(Error.httpRequestFailedAtDestination(statusCode: UInt(statusCode), json: body, destination: destination))
}
seal.fulfill(body)
seal.fulfill(data)
} else {
guard 200...299 ~= statusCode else {
return seal.reject(Error.httpRequestFailedAtDestination(statusCode: UInt(statusCode), json: json, destination: destination))
}
seal.fulfill(json)
seal.fulfill(data)
}
} catch {
seal.reject(error)

View File

@ -2,8 +2,18 @@ import Foundation
internal extension String {
func removingPrefix(_ prefix: String) -> String {
func removingPrefix(_ prefix: String, if condition: Bool = true) -> String {
guard condition else { return self }
guard let range = self.range(of: prefix), range.lowerBound == startIndex else { return self }
return String(self[range.upperBound..<endIndex])
}
}
internal extension String {
func appending(_ other: String?) -> String {
guard let value: String = other else { return self }
return self.appending(value)
}
}

View File

@ -20,3 +20,9 @@ public extension ECKeyPair {
return true
}
}
public extension BlindedECKeyPair {
@objc override var hexEncodedPublicKey: String {
return IdPrefix.blinded.rawValue + publicKey.map { String(format: "%02hhx", $0) }.joined()
}
}

View File

@ -1,7 +0,0 @@
public extension Array where Element : CustomStringConvertible {
var prettifiedDescription: String {
return "[ " + map { $0.description }.joined(separator: ", ") + " ]"
}
}

View File

@ -0,0 +1,23 @@
public extension Array where Element : CustomStringConvertible {
var prettifiedDescription: String {
return "[ " + map { $0.description }.joined(separator: ", ") + " ]"
}
}
public extension Array {
func appending(_ other: Element) -> [Element] {
var updatedArray: [Element] = self
updatedArray.append(other)
return updatedArray
}
func appending(_ other: [Element]) -> [Element] {
var updatedArray: [Element] = self
updatedArray.append(contentsOf: other)
return updatedArray
}
}

View File

@ -1,18 +0,0 @@
public extension Data {
func removingIdPrefixIfNeeded() -> Data {
var result = self
if result.count == 33 && IdPrefix(with: result.toHexString()) != nil { result.removeFirst() }
return result
}
}
@objc public extension NSData {
@objc func removingIdPrefixIfNeeded() -> NSData {
var result = self as Data
if result.count == 33 && IdPrefix(with: result.toHexString()) != nil { result.removeFirst() }
return result as NSData
}
}

View File

@ -0,0 +1,48 @@
import Foundation
public extension Data {
func removingIdPrefixIfNeeded() -> Data {
var result = self
if result.count == 33 && IdPrefix(with: result.toHexString()) != nil { result.removeFirst() }
return result
}
func appending(_ other: Data) -> Data {
var mutableData: Data = Data()
mutableData.append(self)
mutableData.append(other)
return mutableData
}
func appending(_ other: [UInt8]) -> Data {
var mutableData: Data = Data()
mutableData.append(self)
mutableData.append(contentsOf: other)
return mutableData
}
}
@objc public extension NSData {
@objc func removingIdPrefixIfNeeded() -> NSData {
var result = self as Data
if result.count == 33 && IdPrefix(with: result.toHexString()) != nil { result.removeFirst() }
return result as NSData
}
}
// MARK: - Decoding
public extension Data {
func decoded<T: Decodable>(as type: T.Type, customError: Error? = nil) throws -> T {
do {
return try JSONDecoder().decode(type, from: self)
}
catch let error {
throw (customError ?? error)
}
}
}

View File

@ -1,13 +0,0 @@
public extension Dictionary {
var prettifiedDescription: String {
return "[ " + map { key, value in
let keyDescription = String(describing: key)
let valueDescription = String(describing: value)
let maxLength = 20
let truncatedValueDescription = valueDescription.count > maxLength ? valueDescription.prefix(maxLength) + "..." : valueDescription
return keyDescription + " : " + truncatedValueDescription
}.joined(separator: ", ") + " ]"
}
}

View File

@ -0,0 +1,42 @@
public extension Dictionary {
var prettifiedDescription: String {
return "[ " + map { key, value in
let keyDescription = String(describing: key)
let valueDescription = String(describing: value)
let maxLength = 20
let truncatedValueDescription = valueDescription.count > maxLength ? valueDescription.prefix(maxLength) + "..." : valueDescription
return keyDescription + " : " + truncatedValueDescription
}.joined(separator: ", ") + " ]"
}
}
// MARK: - Functional Convenience
public extension Dictionary {
func setting(_ key: Key, _ value: Value?) -> [Key: Value] {
var updatedDictionary: [Key: Value] = self
updatedDictionary[key] = value
return updatedDictionary
}
func updated(with other: [Key: Value]) -> [Key: Value] {
var updatedDictionary: [Key: Value] = self
other.forEach { key, value in
updatedDictionary[key] = value
}
return updatedDictionary
}
func removingValue(forKey key: Key) -> [Key: Value] {
var updatedDictionary: [Key: Value] = self
updatedDictionary.removeValue(forKey: key)
return updatedDictionary
}
}

View File

@ -1,8 +1,20 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Curve25519Kit
/// The `BlindedECKeyPair` is essentially the same as the `ECKeyPair` except it allows us to more easily distinguish between the two,
/// additionally when generating the `hexEncodedPublicKey` value it will apply the correct prefix
public class BlindedECKeyPair: ECKeyPair {}
public enum IdPrefix: String, CaseIterable {
case standard = "05" // Used for identified users, open groups, etc.
case blinded = "15" // Used for participants in open groups
public init?(with sessionId: String) {
guard ECKeyPair.isValidHexEncodedPublicKey(candidate: sessionId) else { return nil }
guard let targetPrefix: IdPrefix = IdPrefix(rawValue: String(sessionId.prefix(2))) else { return nil }
self = targetPrefix
}
}

View File

@ -0,0 +1,18 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension String {
public func dataFromHex() -> Data? {
guard (self.count % 2) == 0 else { return nil }
let chars = self.map { $0 }
let bytes: [UInt8] = stride(from: 0, to: chars.count, by: 2)
.map { index -> String in String(chars[index]) + String(chars[index + 1]) }
.compactMap { (str: String) -> UInt8? in UInt8(str, radix: 16) }
guard (self.count / bytes.count) == 2 else { return nil }
return Data(bytes)
}
}

View File

@ -16,7 +16,6 @@ FOUNDATION_EXPORT const unsigned char SessionUtilitiesKitVersionString[];
#import <SessionUtilitiesKit/NSUserDefaults+OWS.h>
#import <SessionUtilitiesKit/OWSFileSystem.h>
#import <SessionUtilitiesKit/OWSMath.h>
#import <SessionUtilitiesKit/TSRequest.h>
#import <SessionUtilitiesKit/TSYapDatabaseObject.h>
#import <SessionUtilitiesKit/UIImage+OWS.h>
#import <SessionUtilitiesKit/UIView+OWS.h>

View File

@ -1,29 +0,0 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#define textSecureHTTPTimeOut 10
@interface TSRequest : NSMutableURLRequest
@property (nonatomic, readonly) NSDictionary<NSString *, id> *parameters;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithURL:(NSURL *)URL;
- (instancetype)initWithURL:(NSURL *)URL
cachePolicy:(NSURLRequestCachePolicy)cachePolicy
timeoutInterval:(NSTimeInterval)timeoutInterval NS_UNAVAILABLE;
- (instancetype)initWithURL:(NSURL *)URL
method:(NSString *)method
parameters:(nullable NSDictionary<NSString *, id> *)parameters;
+ (instancetype)requestWithUrl:(NSURL *)url
method:(NSString *)method
parameters:(nullable NSDictionary<NSString *, id> *)parameters;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,64 +0,0 @@
#import "TSRequest.h"
NS_ASSUME_NONNULL_BEGIN
@implementation TSRequest
- (id)initWithURL:(NSURL *)URL {
self = [super initWithURL:URL
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:textSecureHTTPTimeOut];
if (!self) {
return nil;
}
_parameters = @{};
return self;
}
- (instancetype)init
{
return nil;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
- (instancetype)initWithURL:(NSURL *)URL
cachePolicy:(NSURLRequestCachePolicy)cachePolicy
timeoutInterval:(NSTimeInterval)timeoutInterval
{
return nil;
}
- (instancetype)initWithURL:(NSURL *)URL
method:(NSString *)method
parameters:(nullable NSDictionary<NSString *, id> *)parameters
{
self = [super initWithURL:URL
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:textSecureHTTPTimeOut];
if (!self) {
return nil;
}
_parameters = parameters ?: @{};
[self setHTTPMethod:method];
return self;
}
+ (instancetype)requestWithUrl:(NSURL *)url
method:(NSString *)method
parameters:(nullable NSDictionary<NSString *, id> *)parameters
{
return [[TSRequest alloc] initWithURL:url method:method parameters:parameters];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -45,7 +45,20 @@ extension MessageSender {
let storage = SNMessagingKitConfiguration.shared.storage
if let v2OpenGroup = storage.getV2OpenGroup(for: thread.uniqueId!) {
let (promise, seal) = Promise<Void>.pending()
AttachmentUploadJob.upload(stream, using: { data in return OpenGroupAPIV2.upload(data, to: v2OpenGroup.room, on: v2OpenGroup.server) }, encrypt: false, onSuccess: { seal.fulfill(()) }, onFailure: { seal.reject($0) })
AttachmentUploadJob.upload(
stream,
using: { data in
OpenGroupAPIV2.upload(
data,
to: v2OpenGroup.room,
on: v2OpenGroup.server
)
},
encrypt: false,
onSuccess: { seal.fulfill(()) },
onFailure: { seal.reject($0) }
)
return promise
} else {
let (promise, seal) = Promise<Void>.pending()
@ -78,7 +91,19 @@ extension MessageSender {
let storage = SNMessagingKitConfiguration.shared.storage
if let v2OpenGroup = storage.getV2OpenGroup(for: thread.uniqueId!) {
let (promise, seal) = Promise<Void>.pending()
AttachmentUploadJob.upload(stream, using: { data in return OpenGroupAPIV2.upload(data, to: v2OpenGroup.room, on: v2OpenGroup.server) }, encrypt: false, onSuccess: { seal.fulfill(()) }, onFailure: { seal.reject($0) })
AttachmentUploadJob.upload(
stream,
using: { data in
OpenGroupAPIV2.upload(
data,
to: v2OpenGroup.room,
on: v2OpenGroup.server
)
},
encrypt: false,
onSuccess: { seal.fulfill(()) },
onFailure: { seal.reject($0) }
)
return promise
} else {
let (promise, seal) = Promise<Void>.pending()