More work on getting SOGS V4 integrated

Updated the MessageSendJob to support V4 messages (V2 messages will be upgraded to V4 if they get re-encoded)
Renamed the Message+Destination from 'openGroup' & 'openGroupV2' to 'legacyOpenGroup' and 'openGroup'
Started plugging in more of the V4 APIs
Renamed a number of the V2 APIs to start with 'legacy'
This commit is contained in:
Morgan Pretty 2022-02-11 16:48:16 +11:00
parent 2284375fc0
commit 4f3900771e
39 changed files with 2183 additions and 734 deletions

View File

@ -789,29 +789,43 @@
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 */; };
FDC4381A27B34EBA00C60D73 /* LegacyCompactPollBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381927B34EBA00C60D73 /* LegacyCompactPollBody.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 */; };
FDC4382A27B3802D00C60D73 /* LegacyRoomsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4382927B3802D00C60D73 /* LegacyRoomsResponse.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 */; };
FDC4384027B4746D00C60D73 /* LegacyGetInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4383F27B4746D00C60D73 /* LegacyGetInfoResponse.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 */; };
FDC4384827B47F4D00C60D73 /* LegacyRoomInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384427B47F4D00C60D73 /* LegacyRoomInfo.swift */; };
FDC4384927B47F4D00C60D73 /* LegacyCompactPollResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4384527B47F4D00C60D73 /* LegacyCompactPollResponse.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 */; };
FDC4385727B484B700C60D73 /* LegacyFileUploadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385627B484B700C60D73 /* LegacyFileUploadResponse.swift */; };
FDC4385927B484E800C60D73 /* FileUploadBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385827B484E800C60D73 /* FileUploadBody.swift */; };
FDC4385B27B485DE00C60D73 /* FileDownloadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385A27B485DE00C60D73 /* FileDownloadResponse.swift */; };
FDC4385B27B485DE00C60D73 /* LegacyFileDownloadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385A27B485DE00C60D73 /* LegacyFileDownloadResponse.swift */; };
FDC4385D27B4C18900C60D73 /* Room.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385C27B4C18900C60D73 /* Room.swift */; };
FDC4385F27B4C4A200C60D73 /* PinnedMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4385E27B4C4A200C60D73 /* PinnedMessage.swift */; };
FDC4386127B4CDDF00C60D73 /* FileResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4386027B4CDDF00C60D73 /* FileResponse.swift */; };
FDC4386327B4D94E00C60D73 /* OGMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4386227B4D94E00C60D73 /* OGMessage.swift */; };
FDC4386527B4DE7600C60D73 /* RoomPollInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4386427B4DE7600C60D73 /* RoomPollInfo.swift */; };
FDC4386727B4E10E00C60D73 /* Capabilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4386627B4E10E00C60D73 /* Capabilities.swift */; };
FDC4386927B4E6B800C60D73 /* String+Utlities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4386827B4E6B700C60D73 /* String+Utlities.swift */; };
FDC4386B27B4E88F00C60D73 /* BatchRequestInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4386A27B4E88F00C60D73 /* BatchRequestInfo.swift */; };
FDC4386C27B4E90300C60D73 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; platformFilter = ios; };
FDC4386D27B4E90300C60D73 /* SessionUtilitiesKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
FDC4387227B5BB3B00C60D73 /* FileUploadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4387127B5BB3B00C60D73 /* FileUploadResponse.swift */; };
FDC4387427B5BB9B00C60D73 /* Promise+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4387327B5BB9B00C60D73 /* Promise+Utilities.swift */; };
FDC4387627B5BEF300C60D73 /* FileDownloadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4387527B5BEF300C60D73 /* FileDownloadResponse.swift */; };
FDC4387827B5C35400C60D73 /* SendMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4387727B5C35400C60D73 /* SendMessageRequest.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -927,6 +941,13 @@
remoteGlobalIDString = C33FD9AA255A548A00E217F9;
remoteInfo = SignalUtilitiesKit;
};
FDC4386E27B4E90300C60D73 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D221A080169C9E5E00537ABF /* Project object */;
proxyType = 1;
remoteGlobalIDString = C3C2A678255388CC00C340D1;
remoteInfo = SessionUtilitiesKit;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@ -957,6 +978,17 @@
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
FDC4387027B4E90300C60D73 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
FDC4386D27B4E90300C60D73 /* SessionUtilitiesKit.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
@ -1850,29 +1882,41 @@
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>"; };
FDC4381927B34EBA00C60D73 /* LegacyCompactPollBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyCompactPollBody.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>"; };
FDC4382927B3802D00C60D73 /* LegacyRoomsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyRoomsResponse.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>"; };
FDC4383F27B4746D00C60D73 /* LegacyGetInfoResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyGetInfoResponse.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>"; };
FDC4384427B47F4D00C60D73 /* LegacyRoomInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyRoomInfo.swift; sourceTree = "<group>"; };
FDC4384527B47F4D00C60D73 /* LegacyCompactPollResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyCompactPollResponse.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>"; };
FDC4385627B484B700C60D73 /* LegacyFileUploadResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyFileUploadResponse.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>"; };
FDC4385A27B485DE00C60D73 /* LegacyFileDownloadResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyFileDownloadResponse.swift; sourceTree = "<group>"; };
FDC4385C27B4C18900C60D73 /* Room.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Room.swift; sourceTree = "<group>"; };
FDC4385E27B4C4A200C60D73 /* PinnedMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedMessage.swift; sourceTree = "<group>"; };
FDC4386027B4CDDF00C60D73 /* FileResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileResponse.swift; sourceTree = "<group>"; };
FDC4386227B4D94E00C60D73 /* OGMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OGMessage.swift; sourceTree = "<group>"; };
FDC4386427B4DE7600C60D73 /* RoomPollInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollInfo.swift; sourceTree = "<group>"; };
FDC4386627B4E10E00C60D73 /* Capabilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Capabilities.swift; sourceTree = "<group>"; };
FDC4386827B4E6B700C60D73 /* String+Utlities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Utlities.swift"; sourceTree = "<group>"; };
FDC4386A27B4E88F00C60D73 /* BatchRequestInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchRequestInfo.swift; sourceTree = "<group>"; };
FDC4387127B5BB3B00C60D73 /* FileUploadResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUploadResponse.swift; sourceTree = "<group>"; };
FDC4387327B5BB9B00C60D73 /* Promise+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+Utilities.swift"; sourceTree = "<group>"; };
FDC4387527B5BEF300C60D73 /* FileDownloadResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDownloadResponse.swift; sourceTree = "<group>"; };
FDC4387727B5C35400C60D73 /* SendMessageRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMessageRequest.swift; sourceTree = "<group>"; };
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 */
@ -1944,6 +1988,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FDC4386C27B4E90300C60D73 /* SessionUtilitiesKit.framework in Frameworks */,
C3C2A70B25539E1E00C340D1 /* SessionSnodeKit.framework in Frameworks */,
9B0A583E9B89FEF0916B793A /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionMessagingKit.framework in Frameworks */,
);
@ -3294,7 +3339,9 @@
C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */,
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
C3E7134E251C867C009649BB /* Sodium+Utilities.swift */,
FDC4386827B4E6B700C60D73 /* String+Utlities.swift */,
FD5D202127B1D74F00FEA984 /* ECKeyPair+Conversion.swift */,
FDC4387327B5BB9B00C60D73 /* Promise+Utilities.swift */,
C33FDB31255A580A00E217F9 /* SSKEnvironment.h */,
C33FDAF4255A580600E217F9 /* SSKEnvironment.m */,
C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */,
@ -3707,16 +3754,24 @@
children = (
FDC4384B27B47F7700C60D73 /* OpenGroupV2.swift */,
FDC4381B27B354AC00C60D73 /* PublicKeyBody.swift */,
FDC4381927B34EBA00C60D73 /* CompactPollBody.swift */,
FDC4384527B47F4D00C60D73 /* CompactPollResponse.swift */,
FDC4383F27B4746D00C60D73 /* GetInfoResponse.swift */,
FDC4382927B3802D00C60D73 /* RoomsResponse.swift */,
FDC4384427B47F4D00C60D73 /* RoomInfo.swift */,
FDC4386A27B4E88F00C60D73 /* BatchRequestInfo.swift */,
FDC4385C27B4C18900C60D73 /* Room.swift */,
FDC4386427B4DE7600C60D73 /* RoomPollInfo.swift */,
FDC4385E27B4C4A200C60D73 /* PinnedMessage.swift */,
FDC4386027B4CDDF00C60D73 /* FileResponse.swift */,
FDC4387727B5C35400C60D73 /* SendMessageRequest.swift */,
FDC4386227B4D94E00C60D73 /* OGMessage.swift */,
FDC4382B27B380E300C60D73 /* MemberCountResponse.swift */,
FDC4384327B47F4D00C60D73 /* OpenGroupMessageV2.swift */,
FDC4382527B37F6900C60D73 /* DeletedMessagesResponse.swift */,
FDC4384627B47F4D00C60D73 /* Deletion.swift */,
FDC4386627B4E10E00C60D73 /* Capabilities.swift */,
FDC4382727B37FD300C60D73 /* ModeratorsResponse.swift */,
FDC4381927B34EBA00C60D73 /* LegacyCompactPollBody.swift */,
FDC4384527B47F4D00C60D73 /* LegacyCompactPollResponse.swift */,
FDC4383F27B4746D00C60D73 /* LegacyGetInfoResponse.swift */,
FDC4382927B3802D00C60D73 /* LegacyRoomsResponse.swift */,
FDC4384427B47F4D00C60D73 /* LegacyRoomInfo.swift */,
FDC4384327B47F4D00C60D73 /* OpenGroupMessageV2.swift */,
FDC4383927B4696200C60D73 /* AuthTokenResponse.swift */,
);
path = Models;
@ -3753,8 +3808,10 @@
isa = PBXGroup;
children = (
FDC4385827B484E800C60D73 /* FileUploadBody.swift */,
FDC4385627B484B700C60D73 /* FileUploadResponse.swift */,
FDC4385A27B485DE00C60D73 /* FileDownloadResponse.swift */,
FDC4387127B5BB3B00C60D73 /* FileUploadResponse.swift */,
FDC4387527B5BEF300C60D73 /* FileDownloadResponse.swift */,
FDC4385627B484B700C60D73 /* LegacyFileUploadResponse.swift */,
FDC4385A27B485DE00C60D73 /* LegacyFileDownloadResponse.swift */,
);
path = Models;
sourceTree = "<group>";
@ -4053,10 +4110,12 @@
C3C2A6EC25539DE700C340D1 /* Sources */,
C3C2A6ED25539DE700C340D1 /* Frameworks */,
C3C2A6EE25539DE700C340D1 /* Resources */,
FDC4387027B4E90300C60D73 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
FDC4386F27B4E90300C60D73 /* PBXTargetDependency */,
);
name = SessionMessagingKit;
productName = SessionMessagingKit;
@ -4821,7 +4880,7 @@
buildActionMask = 2147483647;
files = (
B8856D08256F10F1001CE70E /* DeviceSleepManager.swift in Sources */,
FDC4382A27B3802D00C60D73 /* RoomsResponse.swift in Sources */,
FDC4382A27B3802D00C60D73 /* LegacyRoomsResponse.swift in Sources */,
C3471F4C25553AB000297E91 /* MessageReceiver+Decryption.swift in Sources */,
C300A5D32554B05A00555489 /* TypingIndicator.swift in Sources */,
C3A3A156256E1B91004D228D /* ProtoUtils.m in Sources */,
@ -4832,11 +4891,14 @@
C3C2A74D2553A39700C340D1 /* VisibleMessage.swift in Sources */,
C32C5AAD256DBE8F003C73A2 /* TSInfoMessage.m in Sources */,
FD5D201127AA331F00FEA984 /* ConfigurationMessage+Convenience.swift in Sources */,
FDC4386927B4E6B800C60D73 /* String+Utlities.swift in Sources */,
C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */,
FDC4385F27B4C4A200C60D73 /* PinnedMessage.swift in Sources */,
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */,
FDC4384927B47F4D00C60D73 /* CompactPollResponse.swift in Sources */,
FDC4384927B47F4D00C60D73 /* LegacyCompactPollResponse.swift in Sources */,
C352A3932557883D00338F3E /* JobDelegate.swift in Sources */,
C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */,
FDC4386527B4DE7600C60D73 /* RoomPollInfo.swift in Sources */,
C3A3A108256E1A5C004D228D /* OWSIncomingMessageFinder.m in Sources */,
C352A31325574F5200338F3E /* MessageReceiveJob.swift in Sources */,
C32C5BDD256DC88D003C73A2 /* OWSReadReceiptManager.m in Sources */,
@ -4844,7 +4906,7 @@
C3C2A7562553A3AB00C340D1 /* VisibleMessage+Quote.swift in Sources */,
B8B32021258B1A650020074B /* Contact.swift in Sources */,
FDC4380B27B31D7E00C60D73 /* Request.swift in Sources */,
FDC4384027B4746D00C60D73 /* GetInfoResponse.swift in Sources */,
FDC4384027B4746D00C60D73 /* LegacyGetInfoResponse.swift in Sources */,
C32C5C89256DD0D2003C73A2 /* Storage+Jobs.swift in Sources */,
C300A5FC2554B0A000555489 /* MessageReceiver.swift in Sources */,
7B1581E2271E743B00848B49 /* OWSSounds.swift in Sources */,
@ -4853,9 +4915,10 @@
C32C5CA4256DD1DC003C73A2 /* TSAccountManager.m in Sources */,
C352A3892557876500338F3E /* JobQueue.swift in Sources */,
C3BBE0B52554F0E10050F1E3 /* ProofOfWork.swift in Sources */,
FDC4386727B4E10E00C60D73 /* Capabilities.swift in Sources */,
C32C59C1256DB41F003C73A2 /* TSGroupThread.m in Sources */,
C3A3A08F256E1728004D228D /* FullTextSearchFinder.swift in Sources */,
FDC4381A27B34EBA00C60D73 /* CompactPollBody.swift in Sources */,
FDC4381A27B34EBA00C60D73 /* LegacyCompactPollBody.swift in Sources */,
B8856D1A256F114D001CE70E /* ProximityMonitoringManager.swift in Sources */,
C32C5B9F256DC739003C73A2 /* OWSBlockingManager.m in Sources */,
C3D9E52725677DF20040E4F3 /* OWSThumbnailService.swift in Sources */,
@ -4866,6 +4929,7 @@
C379DCF4256735770002D4EB /* VisibleMessage+Attachment.swift in Sources */,
B8856D34256F1192001CE70E /* Environment.m in Sources */,
B8B320B7258C30D70020074B /* HTMLMetadata.swift in Sources */,
FDC4387427B5BB9B00C60D73 /* Promise+Utilities.swift in Sources */,
C32C5AB1256DBE8F003C73A2 /* TSIncomingMessage.m in Sources */,
C3A3A107256E1A5C004D228D /* OWSDisappearingMessagesFinder.m in Sources */,
C32C59C3256DB41F003C73A2 /* TSGroupModel.m in Sources */,
@ -4873,6 +4937,7 @@
C3C2A7842553AAF300C340D1 /* SNProto.swift in Sources */,
C32C5DC0256DD743003C73A2 /* Poller.swift in Sources */,
C3C2A7682553A3D900C340D1 /* VisibleMessage+Contact.swift in Sources */,
FDC4386127B4CDDF00C60D73 /* FileResponse.swift in Sources */,
C3A3A171256E1D25004D228D /* SSKReachabilityManager.swift in Sources */,
C3A71D0B2558989C0043A11F /* MessageWrapper.swift in Sources */,
B8F5F60325EDE16F003BF8D4 /* DataExtractionNotification.swift in Sources */,
@ -4887,6 +4952,7 @@
FD5D202227B1D74F00FEA984 /* ECKeyPair+Conversion.swift in Sources */,
FDC4381C27B354AC00C60D73 /* PublicKeyBody.swift in Sources */,
FDC4382F27B383AF00C60D73 /* UnregisterResponse.swift in Sources */,
FDC4386327B4D94E00C60D73 /* OGMessage.swift in Sources */,
C3C2A7712553A41E00C340D1 /* ControlMessage.swift in Sources */,
C32C5D19256DD493003C73A2 /* OWSLinkPreview.swift in Sources */,
C32C5CF0256DD3E4003C73A2 /* Storage+Shared.swift in Sources */,
@ -4904,9 +4970,11 @@
B8856CEE256F1054001CE70E /* OWSAudioPlayer.m in Sources */,
FD5D200F27AA2B6000FEA984 /* MessageRequestResponse.swift in Sources */,
FDC4381727B32EC700C60D73 /* Personalization.swift in Sources */,
FDC4387827B5C35400C60D73 /* SendMessageRequest.swift in Sources */,
C32C5EDC256DF501003C73A2 /* YapDatabaseConnection+OWS.m in Sources */,
FDC4381527B329CE00C60D73 /* NonceGenerator16Byte.swift in Sources */,
C3BBE0762554CDA60050F1E3 /* Configuration.swift in Sources */,
FDC4387627B5BEF300C60D73 /* FileDownloadResponse.swift in Sources */,
C35D76DB26606304009AA5FB /* ThreadUpdateBatcher.swift in Sources */,
FDC4382C27B380E300C60D73 /* MemberCountResponse.swift in Sources */,
B8B32033258B235D0020074B /* Storage+Contacts.swift in Sources */,
@ -4920,6 +4988,7 @@
C32C5A02256DB658003C73A2 /* MessageSender+Convenience.swift in Sources */,
B8566C6C256F60F50045A0B9 /* OWSUserProfile.m in Sources */,
B8D0A25925E367AC00C1835E /* Notification+MessageReceiver.swift in Sources */,
FDC4387227B5BB3B00C60D73 /* FileUploadResponse.swift in Sources */,
C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */,
FDC4380927B31D4E00C60D73 /* Error.swift in Sources */,
FDC4382027B36ADC00C60D73 /* Endpoint.swift in Sources */,
@ -4946,7 +5015,7 @@
C300A5E72554B07300555489 /* ExpirationTimerUpdate.swift in Sources */,
B88A1AC725C90A4700E6D421 /* TypingIndicatorInteraction.swift in Sources */,
C3D9E3C025676AD70040E4F3 /* TSAttachment.m in Sources */,
FDC4384827B47F4D00C60D73 /* RoomInfo.swift in Sources */,
FDC4384827B47F4D00C60D73 /* LegacyRoomInfo.swift in Sources */,
C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */,
C352A2FF25574B6300338F3E /* MessageSendJob.swift in Sources */,
B8856D11256F112A001CE70E /* OWSAudioSession.swift in Sources */,
@ -4974,16 +5043,18 @@
C32C5A75256DBBCF003C73A2 /* TSAttachmentPointer+Conversion.swift in Sources */,
C32C5AF8256DC051003C73A2 /* OWSDisappearingMessagesConfiguration.m in Sources */,
C32C5EBA256DE130003C73A2 /* OWSQuotedReplyModel.m in Sources */,
FDC4386B27B4E88F00C60D73 /* BatchRequestInfo.swift 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 */,
FDC4385727B484B700C60D73 /* LegacyFileUploadResponse.swift in Sources */,
FDC4385B27B485DE00C60D73 /* LegacyFileDownloadResponse.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 */,
FDC4385D27B4C18900C60D73 /* Room.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -5244,6 +5315,12 @@
target = C33FD9AA255A548A00E217F9 /* SignalUtilitiesKit */;
targetProxy = C3D90A7025773A44002C9DF5 /* PBXContainerItemProxy */;
};
FDC4386F27B4E90300C60D73 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
platformFilter = ios;
target = C3C2A678255388CC00C340D1 /* SessionUtilitiesKit */;
targetProxy = FDC4386E27B4E90300C60D73 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */

View File

@ -371,8 +371,9 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
snInputView.text = draft
}
// Update member count if this is a V2 open group
// TODO: Non-legacy version (I assue this comes through room updates... 'activeUsers'?
if let v2OpenGroup = Storage.shared.getV2OpenGroup(for: thread.uniqueId!) {
OpenGroupAPIV2.getMemberCount(for: v2OpenGroup.room, on: v2OpenGroup.server).retainUntilComplete()
OpenGroupAPIV2.legacyGetMemberCount(for: v2OpenGroup.room, on: v2OpenGroup.server).retainUntilComplete()
}
}

View File

@ -174,7 +174,6 @@ private extension MentionSelectionView {
// MARK: - Delegate
protocol MentionSelectionViewDelegate : class {
protocol MentionSelectionViewDelegate: AnyObject {
func handleMentionSelected(_ mention: Mention, from view: MentionSelectionView)
}

View File

@ -160,7 +160,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
let _ = IP2Country.shared.populateCacheIfNeeded()
}
// Get default open group rooms if needed
OpenGroupAPIV2.getDefaultRoomsIfNeeded()
OpenGroupAPIV2.legacyGetDefaultRoomsIfNeeded()
}
override func viewDidAppear(_ animated: Bool) {

View File

@ -383,7 +383,7 @@ static NSTimeInterval launchStartedAt;
}
if (CurrentAppContext().isMainApp) {
[SNOpenGroupAPIV2 getDefaultRoomsIfNeeded];
[SNOpenGroupAPIV2 legacyGetDefaultRoomsIfNeeded];
}
[[SNSnodeAPI getSnodePool] retainUntilComplete];

View File

@ -238,7 +238,7 @@ private final class EnterURLVC : UIViewController, UIGestureRecognizerDelegate,
return !suggestionGrid.frame.contains(location)
}
func join(_ room: OpenGroupAPIV2.RoomInfo) {
func join(_ room: OpenGroupAPIV2.LegacyRoomInfo) {
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.RoomInfo] = [] { didSet { update() } }
private var rooms: [OpenGroupAPIV2.LegacyRoomInfo] = [] { didSet { update() } }
private var heightConstraint: NSLayoutConstraint!
var delegate: OpenGroupSuggestionGridDelegate?
@ -60,9 +60,10 @@ final class OpenGroupSuggestionGrid : UIView, UICollectionViewDataSource, UIColl
heightConstraint = set(.height, to: OpenGroupSuggestionGrid.cellHeight)
widthAnchor.constraint(greaterThanOrEqualToConstant: OpenGroupSuggestionGrid.cellHeight).isActive = true
if OpenGroupAPIV2.defaultRoomsPromise == nil {
OpenGroupAPIV2.getDefaultRoomsIfNeeded()
OpenGroupAPIV2.legacyGetDefaultRoomsIfNeeded()
}
let _ = OpenGroupAPIV2.defaultRoomsPromise?.done { [weak self] rooms in
let _ = OpenGroupAPIV2.legacyDefaultRoomsPromise?.done { [weak self] rooms in
// TODO: Update this for the new rooms API
self?.rooms = rooms
}
}
@ -104,7 +105,7 @@ final class OpenGroupSuggestionGrid : UIView, UICollectionViewDataSource, UIColl
extension OpenGroupSuggestionGrid {
fileprivate final class Cell : UICollectionViewCell {
var room: OpenGroupAPIV2.RoomInfo? { didSet { update() } }
var room: OpenGroupAPIV2.LegacyRoomInfo? { didSet { update() } }
static let identifier = "OpenGroupSuggestionGridCell"
@ -172,7 +173,7 @@ extension OpenGroupSuggestionGrid {
private func update() {
guard let room = room else { return }
let promise = OpenGroupAPIV2.getGroupImage(for: room.id, on: OpenGroupAPIV2.defaultServer)
let promise = OpenGroupAPIV2.legacyGetGroupImage(for: room.id, on: OpenGroupAPIV2.defaultServer)
imageView.image = given(promise.value) { UIImage(data: $0)! }
imageView.isHidden = (imageView.image == nil)
label.text = room.name
@ -183,5 +184,5 @@ extension OpenGroupSuggestionGrid {
// MARK: Delegate
protocol OpenGroupSuggestionGridDelegate {
func join(_ room: OpenGroupAPIV2.RoomInfo)
func join(_ room: OpenGroupAPIV2.LegacyRoomInfo)
}

View File

@ -2,16 +2,29 @@
import Foundation
struct FileDownloadResponse: Codable {
// TODO: Update this (looks like it's getting changed to just be the data, the properties are send through as headers)
public struct FileDownloadResponse: Codable {
enum CodingKeys: String, CodingKey {
case base64EncodedData = "result"
case fileName = "filename"
case size
case uploaded
case expires
case base64EncodedData = "result" // TODO: Confirm the name of this value
}
let data: Data
public let fileName: String
public let size: Int64
public let uploaded: TimeInterval
public let expires: TimeInterval?
public let data: Data
public func encode(to encoder: Encoder) throws {
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
try container.encode(fileName, forKey: .fileName)
try container.encode(size, forKey: .size)
try container.encode(uploaded, forKey: .uploaded)
try container.encodeIfPresent(expires, forKey: .expires)
try container.encode(data.base64EncodedString(), forKey: .base64EncodedData)
}
}
@ -19,7 +32,7 @@ struct FileDownloadResponse: Codable {
// MARK: - Decoder
extension FileDownloadResponse {
init(from decoder: Decoder) throws {
public 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)
@ -29,6 +42,10 @@ extension FileDownloadResponse {
}
self = FileDownloadResponse(
fileName: try container.decode(String.self, forKey: .fileName),
size: try container.decode(Int64.self, forKey: .size),
uploaded: try container.decode(TimeInterval.self, forKey: .uploaded),
expires: try? container.decode(TimeInterval.self, forKey: .expires),
data: data
)
}

View File

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

View File

@ -0,0 +1,35 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
struct LegacyFileDownloadResponse: 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 LegacyFileDownloadResponse {
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 = LegacyFileDownloadResponse(
data: data
)
}
}

View File

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

View File

@ -5,4 +5,8 @@ import Foundation
enum QueryParam: String {
case publicKey = "public_key"
case fromServerId = "from_server_id"
case required = "required"
case fileName = "X-Filename"
case limit // For messages - number between 1 and 256 (default is 100)
}

View File

@ -112,7 +112,7 @@ public final class FileServerAPIV2 : NSObject {
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)
let response: LegacyFileUploadResponse = try data.decoded(as: LegacyFileUploadResponse.self, customError: Error.parsingFailed)
return response.fileId
}
@ -128,7 +128,7 @@ public final class FileServerAPIV2 : NSObject {
let request = Request(verb: .get, endpoint: "files/\(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)
let response: LegacyFileDownloadResponse = try data.decoded(as: LegacyFileDownloadResponse.self, customError: Error.parsingFailed)
return response.data
}

View File

@ -9,49 +9,111 @@ public final class MessageSendJob : NSObject, Job, NSCoding { // NSObject/NSCodi
public var id: String?
public var failureCount: UInt = 0
// MARK: Settings
// MARK: - Settings
public class var collection: String { return "MessageSendJobCollection" }
public static let maxFailureCount: UInt = 10
// MARK: Initialization
@objc public convenience init(message: Message, publicKey: String) { self.init(message: message, destination: .contact(publicKey: publicKey)) }
@objc public convenience init(message: Message, groupPublicKey: String) { self.init(message: message, destination: .closedGroup(groupPublicKey: groupPublicKey)) }
// MARK: - Initialization
@objc public convenience init(message: Message, publicKey: String) {
self.init(message: message, destination: .contact(publicKey: publicKey))
}
@objc public convenience init(message: Message, groupPublicKey: String) {
self.init(message: message, destination: .closedGroup(groupPublicKey: groupPublicKey))
}
public init(message: Message, destination: Message.Destination) {
self.message = message
self.destination = destination
}
// MARK: Coding
// MARK: - Coding
public init?(coder: NSCoder) {
guard let message = coder.decodeObject(forKey: "message") as! Message?,
var rawDestination = coder.decodeObject(forKey: "destination") as! String?,
let id = coder.decodeObject(forKey: "id") as! String? else { return nil }
self.message = message
if rawDestination.removePrefix("contact(") {
guard rawDestination.removeSuffix(")") else { return nil }
let publicKey = rawDestination
destination = .contact(publicKey: publicKey)
} else if rawDestination.removePrefix("closedGroup(") {
guard rawDestination.removeSuffix(")") else { return nil }
let groupPublicKey = rawDestination
destination = .closedGroup(groupPublicKey: groupPublicKey)
} else if rawDestination.removePrefix("openGroup(") {
guard rawDestination.removeSuffix(")") else { return nil }
let components = rawDestination.split(separator: ",").map { String($0).trimmingCharacters(in: .whitespacesAndNewlines) }
guard components.count == 2, let channel = UInt64(components[0]) else { return nil }
let server = components[1]
destination = .openGroup(channel: channel, server: server)
} else if rawDestination.removePrefix("openGroupV2(") {
guard rawDestination.removeSuffix(")") else { return nil }
let components = rawDestination.split(separator: ",").map { String($0).trimmingCharacters(in: .whitespacesAndNewlines) }
guard components.count == 2 else { return nil }
let room = components[0]
let server = components[1]
destination = .openGroupV2(room: room, server: server)
} else {
guard let message = coder.decodeObject(forKey: "message") as! Message?, var rawDestination = coder.decodeObject(forKey: "destination") as! String?, let id = coder.decodeObject(forKey: "id") as! String? else {
return nil
}
self.message = message
if rawDestination.removePrefix("contact(") {
guard rawDestination.removeSuffix(")") else { return nil }
let publicKey = rawDestination
destination = .contact(publicKey: publicKey)
}
else if rawDestination.removePrefix("closedGroup(") {
guard rawDestination.removeSuffix(")") else { return nil }
let groupPublicKey = rawDestination
destination = .closedGroup(groupPublicKey: groupPublicKey)
}
else if rawDestination.removePrefix("openGroup(") {
guard rawDestination.removeSuffix(")") else { return nil }
let components = rawDestination
.split(separator: ",")
.map { String($0).trimmingCharacters(in: .whitespacesAndNewlines) }
guard components.count == 2, let channel = UInt64(components[0]) else { return nil }
let server = components[1]
destination = .legacyOpenGroup(channel: channel, server: server)
}
else if rawDestination.removePrefix("openGroupV2(") {
guard rawDestination.removeSuffix(")") else { return nil }
let components = rawDestination
.split(separator: ",")
.map { String($0).trimmingCharacters(in: .whitespacesAndNewlines) }
guard components.count == 2 else { return nil }
let roomToken: String = components[0]
let server: String = components[1]
destination = .openGroup(
roomToken: roomToken,
server: server
)
}
else if rawDestination.removePrefix("openGroupV4(") {
guard rawDestination.removeSuffix(")") else { return nil }
let components = rawDestination
.split(separator: ",")
.map { String($0).trimmingCharacters(in: .whitespacesAndNewlines) }
guard components.count == 5 else { return nil }
let roomToken: String = components[0]
let server: String = components[1]
let whisperTo: String? = (!components[2].isEmpty ?
components[2] :
nil
)
let whisperMods: Bool = (components[3] == "true")
let fileIdStrings: [String] = components[4]
.replacingOccurrences(of: "[", with: "")
.replacingOccurrences(of: "]", with: "")
.split(separator: "|")
.map { String($0) }
let fileIds: [Int64]? = (fileIdStrings.isEmpty ? nil : fileIdStrings.compactMap { Int64($0) })
destination = .openGroup(
roomToken: roomToken,
server: server,
whisperTo: whisperTo,
whisperMods: whisperMods,
fileIds: fileIds
)
}
else {
return nil
}
self.id = id
self.failureCount = coder.decodeObject(forKey: "failureCount") as! UInt? ?? 0
}
@ -59,11 +121,28 @@ public final class MessageSendJob : NSObject, Job, NSCoding { // NSObject/NSCodi
public func encode(with coder: NSCoder) {
coder.encode(message, forKey: "message")
switch destination {
case .contact(let publicKey): coder.encode("contact(\(publicKey))", forKey: "destination")
case .closedGroup(let groupPublicKey): coder.encode("closedGroup(\(groupPublicKey))", forKey: "destination")
case .openGroup(let channel, let server): coder.encode("openGroup(\(channel), \(server))", forKey: "destination")
case .openGroupV2(let room, let server): coder.encode("openGroupV2(\(room), \(server))", forKey: "destination")
case .contact(let publicKey):
coder.encode("contact(\(publicKey))", forKey: "destination")
case .closedGroup(let groupPublicKey):
coder.encode("closedGroup(\(groupPublicKey))", forKey: "destination")
case .legacyOpenGroup(let channel, let server):
coder.encode("openGroup(\(channel), \(server))", forKey: "destination")
case .openGroup(let room, let server, let whisperTo, let whisperMods, let fileIds):
let whisperToString: String = (whisperTo ?? "")
let whisperModsString: String = (whisperMods ? "true" : "false")
let fileIdString: String = (fileIds ?? [])
.map { String($0) }
.joined(separator: "|")
coder.encode(
"openGroupV4(\(room), \(server), \(whisperToString), \(whisperModsString), [\(fileIdString)])",
forKey: "destination"
)
}
coder.encode(id, forKey: "id")
coder.encode(failureCount, forKey: "failureCount")
}

View File

@ -4,22 +4,33 @@ public extension Message {
enum Destination {
case contact(publicKey: String)
case closedGroup(groupPublicKey: String)
case openGroup(channel: UInt64, server: String)
case openGroupV2(room: String, server: String)
case legacyOpenGroup(channel: UInt64, server: String)
case openGroup(
roomToken: String,
server: String,
whisperTo: String? = nil,
whisperMods: Bool = false,
fileIds: [Int64]? = nil // TODO: Handle 'fileIds'
)
static func from(_ thread: TSThread) -> Message.Destination {
if let thread = thread as? TSContactThread {
return .contact(publicKey: thread.contactSessionID())
} else if let thread = thread as? TSGroupThread, thread.isClosedGroup {
}
if let thread = thread as? TSGroupThread, thread.isClosedGroup {
let groupID = thread.groupModel.groupId
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
return .closedGroup(groupPublicKey: groupPublicKey)
} else if let thread = thread as? TSGroupThread, thread.isOpenGroup {
let openGroupV2 = Storage.shared.getV2OpenGroup(for: thread.uniqueId!)!
return .openGroupV2(room: openGroupV2.room, server: openGroupV2.server)
} else {
preconditionFailure("TODO: Handle legacy closed groups.")
}
if let thread = thread as? TSGroupThread, thread.isOpenGroup {
let openGroup: OpenGroupV2 = Storage.shared.getV2OpenGroup(for: thread.uniqueId!)!
return .openGroup(roomToken: openGroup.room, server: openGroup.server)
}
preconditionFailure("TODO: Handle legacy closed groups.")
}
}
}

View File

@ -0,0 +1,90 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import PromiseKit
import SessionUtilitiesKit
extension OpenGroupAPIV2 {
// MARK: - BatchSubRequest
struct BatchSubRequest: Codable {
let method: HTTP.Verb
let path: String
let headers: [String: String]?
let json: String?
let b64: String?
init(request: Request) {
self.method = request.method
self.path = request.urlPathAndParamsString
self.headers = (request.headers.isEmpty ? nil : request.headers.toHTTPHeaders())
// TODO: Differentiate between JSON and b64 body
if let body: Data = request.body, let bodyString: String = String(data: body, encoding: .utf8) {
self.json = bodyString
}
else {
self.json = nil
}
self.b64 = nil
}
}
// MARK: - BatchSubResponse<T>
struct BatchSubResponse<T: Codable>: Codable {
let code: Int32
let headers: [String: String]
let body: T
}
// MARK: - BatchRequestInfo<T>
struct BatchRequestInfo {
let request: Request
let responseType: Codable.Type
init<T: Codable>(request: Request, responseType: T.Type) {
self.request = request
self.responseType = BatchSubResponse<T>.self
}
}
// MARK: - BatchRequest
typealias BatchRequest = [BatchSubRequest]
typealias BatchResponseTypes = [Codable.Type]
typealias BatchResponse = [Codable]
}
// MARK: - Convenience
public extension Decodable {
static func decoded(from data: Data) throws -> Self {
return try JSONDecoder().decode(Self.self, from: data)
}
}
extension Promise where T == Data {
func decoded(as types: OpenGroupAPIV2.BatchResponseTypes, on queue: DispatchQueue? = nil, error: Error? = nil) -> Promise<OpenGroupAPIV2.BatchResponse> {
self.map(on: queue) { data -> OpenGroupAPIV2.BatchResponse in
// Need to split the data into an array of data so each item can be Decoded correctly
guard let jsonObject: Any = try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) else {
throw OpenGroupAPIV2.Error.parsingFailed
}
guard let anyArray: [Any] = jsonObject as? [Any] else { throw OpenGroupAPIV2.Error.parsingFailed }
let dataArray: [Data] = anyArray.compactMap { try? JSONSerialization.data(withJSONObject: $0) }
guard dataArray.count == types.count else { throw OpenGroupAPIV2.Error.parsingFailed }
do {
return try zip(dataArray, types)
.map { data, type in try type.decoded(from: data) }
}
catch let thrownError {
throw (error ?? thrownError)
}
}
}
}

View File

@ -0,0 +1,40 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
public struct Capabilities: Codable {
enum Capability: CaseIterable, Codable {
static var allCases: [Capability] {
[.pysogs]
}
case pysogs
/// Fallback case if the capability isn't supported by this version of the app
case unsupported(String)
// MARK: - Convenience
var rawValue: String {
switch self {
case .unsupported(let originalValue): return originalValue
default: return "\(self)"
}
}
// MARK: - Codable
init(from decoder: Decoder) throws {
let container: SingleValueDecodingContainer = try decoder.singleValueContainer()
let valueString: String = try container.decode(String.self)
let maybeValue: Capability? = Capability.allCases.first { $0.rawValue == valueString }
self = (maybeValue ?? .unsupported(valueString))
}
}
let capabilities: [Capability]
let missing: [Capability]?
}
}

View File

@ -0,0 +1,19 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
public struct FileResponse: Codable {
enum CodingKeys: String, CodingKey {
case fileName = "filename"
case size
case uploaded
case expires
}
let fileName: String?
let size: Int64
let uploaded: TimeInterval
let expires: TimeInterval?
}
}

View File

@ -3,7 +3,7 @@
import Foundation
extension OpenGroupAPIV2 {
struct CompactPollBody: Codable {
struct LegacyCompactPollBody: Codable {
struct Room: Codable {
enum CodingKeys: String, CodingKey {
case id = "room_id"

View File

@ -3,7 +3,7 @@
import Foundation
extension OpenGroupAPIV2 {
public struct CompactPollResponse: Codable {
public struct LegacyCompactPollResponse: Codable {
public struct Result: Codable {
enum CodingKeys: String, CodingKey {
case room = "room_id"

View File

@ -3,7 +3,7 @@
import Foundation
extension OpenGroupAPIV2 {
struct RoomsResponse: Codable {
let rooms: [RoomInfo]
struct LegacyGetInfoResponse: Codable {
let room: LegacyRoomInfo
}
}

View File

@ -3,7 +3,7 @@
import Foundation
extension OpenGroupAPIV2 {
public struct RoomInfo: Codable {
public struct LegacyRoomInfo: Codable {
enum CodingKeys: String, CodingKey {
case id
case name

View File

@ -3,7 +3,7 @@
import Foundation
extension OpenGroupAPIV2 {
struct GetInfoResponse: Codable {
let room: RoomInfo
struct LegacyRoomsResponse: Codable {
let rooms: [LegacyRoomInfo]
}
}

View File

@ -0,0 +1,73 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
public struct Message: Codable {
enum CodingKeys: String, CodingKey {
case id
case sender = "session_id"
case posted
case edited
case seqNo = "seqno"
case whisper
case whisperMods = "whisper_mods"
case whisperTo = "whisper_to"
case base64EncodedData = "data"
case base64EncodedSignature = "signature"
}
public let id: Int64
public let sender: String?
public let posted: TimeInterval
public let edited: TimeInterval?
public let seqNo: Int64
public let whisper: Bool
public let whisperMods: Bool
public let whisperTo: String?
public let base64EncodedData: String?
public let base64EncodedSignature: String?
}
}
// MARK: - Decoder
extension OpenGroupAPIV2.Message {
public init(from decoder: Decoder) throws {
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
let maybeSender: String? = try? container.decode(String.self, forKey: .sender)
let maybeBase64EncodedData: String? = try? container.decode(String.self, forKey: .base64EncodedData)
let maybeBase64EncodedSignature: String? = try? container.decode(String.self, forKey: .base64EncodedSignature)
// If we have data and a signature (ie. the message isn't a deletion) then validate the signature
if let base64EncodedData: String = maybeBase64EncodedData, let base64EncodedSignature: String = maybeBase64EncodedSignature {
guard let sender: String = maybeSender, let data = Data(base64Encoded: base64EncodedData), let signature = Data(base64Encoded: base64EncodedSignature) else {
throw OpenGroupAPIV2.Error.parsingFailed
}
let publicKey: Data = Data(hex: sender.removingIdPrefixIfNeeded())
let isValid: Bool = ((try? Ed25519.verifySignature(signature, publicKey: publicKey, data: data)) ?? false)
guard isValid else {
SNLog("Ignoring message with invalid signature.")
throw OpenGroupAPIV2.Error.parsingFailed
}
}
self = OpenGroupAPIV2.Message(
id: try container.decode(Int64.self, forKey: .id),
sender: try? container.decode(String.self, forKey: .sender),
posted: try container.decode(TimeInterval.self, forKey: .posted),
edited: try? container.decode(TimeInterval.self, forKey: .edited),
seqNo: try container.decode(Int64.self, forKey: .seqNo),
whisper: ((try? container.decode(Bool.self, forKey: .whisper)) ?? false),
whisperMods: ((try? container.decode(Bool.self, forKey: .whisperMods)) ?? false),
whisperTo: try? container.decode(String.self, forKey: .whisperTo),
base64EncodedData: maybeBase64EncodedData,
base64EncodedSignature: maybeBase64EncodedSignature
)
}
}

View File

@ -20,7 +20,6 @@ public struct OpenGroupMessageV2: Codable {
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 {

View File

@ -0,0 +1,17 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
public struct PinnedMessage: Codable {
enum CodingKeys: String, CodingKey {
case id
case pinnedAt = "pinned_at"
case pinnedBy = "pinned_by"
}
let id: Int64
let pinnedAt: TimeInterval
let pinnedBy: String
}
}

View File

@ -0,0 +1,107 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
public struct Room: Codable {
enum CodingKeys: String, CodingKey {
case token
case created
case name
case description
case imageId = "image_id"
case infoUpdates = "info_updates"
case messageSequence = "message_sequence"
case activeUsers = "active_users"
case activeUsersCutoff = "active_users_cutoff"
case pinnedMessages = "pinned_messages"
case admin
case globalAdmin = "global_admin"
case admins
case hiddenAdmins = "hidden_admins"
case moderator
case globalModerator = "global_moderator"
case moderators
case hiddenModerators = "hidden_moderators"
case read
case defaultRead = "default_read"
case write
case defaultWrite = "default_write"
case upload
case defaultUpload = "default_upload"
}
public let token: String
public let created: TimeInterval
public let name: String
public let description: String?
public let imageId: Int64?
public let infoUpdates: Int64
public let messageSequence: Int64
public let activeUsers: Int64
public let activeUsersCutoff: Int64
public let pinnedMessages: [PinnedMessage]?
public let admin: Bool
public let globalAdmin: Bool
public let admins: [String]
public let hiddenAdmins: [String]?
public let moderator: Bool
public let globalModerator: Bool
public let moderators: [String]
public let hiddenModerators: [String]?
public let read: Bool
public let defaultRead: Bool
public let write: Bool
public let defaultWrite: Bool
public let upload: Bool
public let defaultUpload: Bool
}
}
// MARK: - Decoding
extension OpenGroupAPIV2.Room {
public init(from decoder: Decoder) throws {
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
self = OpenGroupAPIV2.Room(
token: try container.decode(String.self, forKey: .token),
created: try container.decode(TimeInterval.self, forKey: .created),
name: try container.decode(String.self, forKey: .name),
description: try? container.decode(String.self, forKey: .description),
imageId: try? container.decode(Int64.self, forKey: .imageId),
infoUpdates: try container.decode(Int64.self, forKey: .infoUpdates),
messageSequence: try container.decode(Int64.self, forKey: .messageSequence),
activeUsers: try container.decode(Int64.self, forKey: .activeUsers),
activeUsersCutoff: try container.decode(Int64.self, forKey: .activeUsersCutoff),
pinnedMessages: try? container.decode([OpenGroupAPIV2.PinnedMessage].self, forKey: .pinnedMessages),
admin: ((try? container.decode(Bool.self, forKey: .admin)) ?? false),
globalAdmin: ((try? container.decode(Bool.self, forKey: .globalAdmin)) ?? false),
admins: try container.decode([String].self, forKey: .admins),
hiddenAdmins: try? container.decode([String].self, forKey: .hiddenAdmins),
moderator: ((try? container.decode(Bool.self, forKey: .moderator)) ?? false),
globalModerator: ((try? container.decode(Bool.self, forKey: .globalModerator)) ?? false),
moderators: try container.decode([String].self, forKey: .moderators),
hiddenModerators: try? container.decode([String].self, forKey: .hiddenModerators),
read: try container.decode(Bool.self, forKey: .read),
defaultRead: ((try? container.decode(Bool.self, forKey: .defaultRead)) ?? false),
write: try container.decode(Bool.self, forKey: .write),
defaultWrite: ((try? container.decode(Bool.self, forKey: .defaultWrite)) ?? false),
upload: try container.decode(Bool.self, forKey: .upload),
defaultUpload: ((try? container.decode(Bool.self, forKey: .defaultUpload)) ?? false)
)
}
}

View File

@ -0,0 +1,73 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
/// This only contains ephemeral data
public struct RoomPollInfo: Codable {
enum CodingKeys: String, CodingKey {
case token
case created
case name
case description
case imageId = "image_id"
case infoUpdates = "info_updates"
case messageSequence = "message_sequence"
case activeUsers = "active_users"
case activeUsersCutoff = "active_users_cutoff"
case pinnedMessages = "pinned_messages"
case admin
case globalAdmin = "global_admin"
case admins
case hiddenAdmins = "hidden_admins"
case moderator
case globalModerator = "global_moderator"
case moderators
case hiddenModerators = "hidden_moderators"
case read
case defaultRead = "default_read"
case write
case defaultWrite = "default_write"
case upload
case defaultUpload = "default_upload"
case details
}
public let token: String?
public let created: TimeInterval?
public let name: String?
public let description: String?
public let imageId: Int64?
public let infoUpdates: Int64?
public let messageSequence: Int64?
public let activeUsers: Int64?
public let activeUsersCutoff: Int64?
public let pinnedMessages: [PinnedMessage]?
public let admin: Bool?
public let globalAdmin: Bool?
public let admins: [String]?
public let hiddenAdmins: [String]?
public let moderator: Bool?
public let globalModerator: Bool?
public let moderators: [String]?
public let hiddenModerators: [String]?
public let read: Bool?
public let defaultRead: Bool?
public let write: Bool?
public let defaultWrite: Bool?
public let upload: Bool?
public let defaultUpload: Bool?
/// Only populated and different if the `info_updates` counter differs from the provided `info_updated` value
public let details: Room?
}
}

View File

@ -0,0 +1,67 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPIV2 {
public struct SendMessageRequest: Codable {
enum CodingKeys: String, CodingKey {
case data
case signature
case whisperTo = "whisper_to"
case whisperMods = "whisper_mods"
case fileIds = "files"
}
let data: Data
let signature: Data
let whisperTo: String?
let whisperMods: Bool
let fileIds: [Int64]?
// MARK: - Initialization
init(
data: Data,
signature: Data,
whisperTo: String? = nil,
whisperMods: Bool = false,
fileIds: [Int64]? = nil
) {
self.data = data
self.signature = signature
self.whisperTo = whisperTo
self.whisperMods = whisperMods
self.fileIds = fileIds
}
// MARK: - Encodable
public func encode(to encoder: Encoder) throws {
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
try container.encode(data.base64EncodedString(), forKey: .data)
try container.encode(signature.base64EncodedString(), forKey: .signature)
try container.encodeIfPresent(whisperTo, forKey: .whisperTo)
try container.encode(whisperMods, forKey: .whisperMods)
try container.encodeIfPresent(fileIds, forKey: .fileIds)
}
// MARK: - Signing
public static func sign(message: Data, for idType: IdPrefix, with publicKey: String) -> (data: Data, signature: Data)? {
guard let userKeyPair: ECKeyPair = SNMessagingKitConfiguration.shared.storage.getUserKeyPair() else {
return nil
}
guard let targetKeyPair: ECKeyPair = try? userKeyPair.convert(to: idType, with: publicKey) else {
return nil
}
guard let signature = try? Ed25519.sign(message, with: targetKeyPair) else {
SNLog("Failed to sign open group message.")
return nil
}
return (message, signature)
}
}
}

View File

@ -12,8 +12,8 @@ extension OpenGroupAPIV2 {
return isUserModerator(publicKey, for: room, on: server)
}
@objc(getDefaultRoomsIfNeeded)
public static func objc_getDefaultRoomsIfNeeded() {
return getDefaultRoomsIfNeeded()
@objc(legacyGetDefaultRoomsIfNeeded)
public static func objc_legacyGetDefaultRoomsIfNeeded() {
return legacyGetDefaultRoomsIfNeeded()
}
}

File diff suppressed because it is too large Load Diff

View File

@ -41,42 +41,116 @@ public final class OpenGroupManagerV2 : NSObject {
let transaction = transaction as! YapDatabaseReadWriteTransaction
transaction.addCompletionQueue(DispatchQueue.global(qos: .userInitiated)) {
// Get the group info
OpenGroupAPIV2.getInfo(for: room, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { info in
// Create the open group model and the thread
let openGroup = OpenGroupV2(server: server, room: room, name: info.name, publicKey: publicKey, imageID: info.imageID)
let groupID = LKGroupUtilities.getEncodedOpenGroupIDAsData(openGroup.id)
let model = TSGroupModel(title: openGroup.name, memberIds: [ getUserHexEncodedPublicKey() ], image: nil, groupId: groupID, groupType: .openGroup, adminIds: [])
// Store everything
storage.write(with: { transaction in
let transaction = transaction as! YapDatabaseReadWriteTransaction
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
thread.shouldBeVisible = true
thread.save(with: transaction)
storage.setV2OpenGroup(openGroup, for: thread.uniqueId!, using: transaction)
}, completion: {
// Start the poller if needed
if OpenGroupManagerV2.shared.pollers[server] == nil {
let poller = OpenGroupPollerV2(for: server)
poller.startIfNeeded()
OpenGroupManagerV2.shared.pollers[server] = poller
}
// Fetch the group image
OpenGroupAPIV2.getGroupImage(for: room, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { data in
storage.write { transaction in
// Update the thread
// TODO: Remove this legacy method
// OpenGroupAPIV2.legacyGetRoomInfo(for: room, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { info in
// // Create the open group model and the thread
// let openGroup = OpenGroupV2(server: server, room: room, name: info.name, publicKey: publicKey, imageID: info.imageID)
// let groupID = LKGroupUtilities.getEncodedOpenGroupIDAsData(openGroup.id)
// let model = TSGroupModel(title: openGroup.name, memberIds: [ getUserHexEncodedPublicKey() ], image: nil, groupId: groupID, groupType: .openGroup, adminIds: [])
// // Store everything
// storage.write(with: { transaction in
// let transaction = transaction as! YapDatabaseReadWriteTransaction
// let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
// thread.shouldBeVisible = true
// thread.save(with: transaction)
// storage.setV2OpenGroup(openGroup, for: thread.uniqueId!, using: transaction)
// }, completion: {
// // Start the poller if needed
// if OpenGroupManagerV2.shared.pollers[server] == nil {
// let poller = OpenGroupPollerV2(for: server)
// poller.startIfNeeded()
// OpenGroupManagerV2.shared.pollers[server] = poller
// }
// // Fetch the group image
// OpenGroupAPIV2.legacyGetGroupImage(for: room, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { data in
// storage.write { transaction in
// // Update the thread
// let transaction = transaction as! YapDatabaseReadWriteTransaction
// let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
// thread.groupModel.groupImage = UIImage(data: data)
// thread.save(with: transaction)
// }
// }.retainUntilComplete()
// // Finish
// seal.fulfill(())
// })
// }.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
// seal.reject(error)
// }
OpenGroupAPIV2.room(for: room, on: server)
.done(on: DispatchQueue.global(qos: .userInitiated)) { room in
// Create the open group model and the thread
let openGroup: OpenGroupV2 = OpenGroupV2(
server: server,
room: room.token,
name: room.name,
publicKey: publicKey,
imageID: room.imageId.map { "\($0)" } // TODO: Update this?
)
let groupID: Data = LKGroupUtilities.getEncodedOpenGroupIDAsData(openGroup.id)
let model: TSGroupModel = TSGroupModel(
title: openGroup.name,
memberIds: [ getUserHexEncodedPublicKey() ],
image: nil,
groupId: groupID,
groupType: .openGroup,
adminIds: [] // TODO: This is part of the 'room' object
)
// Store everything
storage.write(
with: { transaction in
let transaction = transaction as! YapDatabaseReadWriteTransaction
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
thread.groupModel.groupImage = UIImage(data: data)
thread.shouldBeVisible = true
thread.save(with: transaction)
storage.setV2OpenGroup(openGroup, for: thread.uniqueId!, using: transaction)
},
completion: {
// Start the poller if needed
if OpenGroupManagerV2.shared.pollers[server] == nil {
let poller = OpenGroupPollerV2(for: server)
poller.startIfNeeded()
OpenGroupManagerV2.shared.pollers[server] = poller
}
// Fetch the group image (if there is one)
// TODO: Need to test this
// TODO: Clean this up (can we avoid the if/else with fancy promise wrangling?)
if let imageId: Int64 = room.imageId {
OpenGroupAPIV2.roomImage(imageId, for: room.token, on: server)
.done(on: DispatchQueue.global(qos: .userInitiated)) { data in
storage.write { transaction in
// Update the thread
let transaction = transaction as! YapDatabaseReadWriteTransaction
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
thread.groupModel.groupImage = UIImage(data: data)
thread.save(with: transaction)
}
}
.retainUntilComplete()
}
else {
storage.write { transaction in
// Update the thread
let transaction = transaction as! YapDatabaseReadWriteTransaction
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
thread.save(with: transaction)
}
}
// Finish
seal.fulfill(())
}
}.retainUntilComplete()
// Finish
seal.fulfill(())
})
}.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
seal.reject(error)
}
)
}
.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
seal.reject(error)
}
}
return promise
}
@ -102,7 +176,7 @@ public final class OpenGroupManagerV2 : NSObject {
Storage.shared.removeReceivedMessageTimestamps(messageTimestamps, using: transaction)
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)
let _ = OpenGroupAPIV2.legacyDeleteAuthToken(for: openGroup.room, on: openGroup.server)
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
Storage.shared.removeV2OpenGroup(for: thread.uniqueId!, using: transaction)

View File

@ -3,51 +3,154 @@
import Foundation
enum Endpoint {
case files
case file(UInt64)
// Utility
case messages
case messagesForServer(Int64)
case deletedMessages
case onion
case batch
case sequence
case capabilities
case moderators
case blockList
case blockListIndividual(String)
case banAndDeleteAll
// Rooms
case rooms
case roomInfo(String)
case roomImage(String)
case room(String)
case roomPollInfo(String, Int64)
// Messages
case roomMessage(String)
case roomMessageIndividual(String, String)
case roomMessagesRecent(String)
case roomMessagesBefore(String, id: Int64)
case roomMessagesSince(String, seqNo: Int64)
// Pinning
case roomPinMessage(String, id: Int64)
case roomUnpinMessage(String, id: Int64)
case roomUnpinAll(String)
// Files
case roomFile(String)
case roomFileJson(String)
case roomFileIndividual(String, Int64)
case roomFileIndividualJson(String, Int64)
// Users
case userBan(String)
case userUnban(String)
case userPermission(String)
case userModerator(String)
case userDeleteMessages(String)
// Legacy endpoints (to be deprecated and removed)
case legacyFiles
case legacyFile(UInt64)
case legacyMessages
case legacyMessagesForServer(Int64)
case legacyDeletedMessages
case legacyModerators
case legacyBlockList
case legacyBlockListIndividual(String)
case legacyBanAndDeleteAll
case legacyCompactPoll(legacyAuth: Bool)
case legacyAuthToken(legacyAuth: Bool)
case legacyAuthTokenChallenge(legacyAuth: Bool)
case legacyAuthTokenClaim(legacyAuth: Bool)
case legacyRooms
case legacyRoomInfo(String)
case legacyRoomImage(String)
case legacyMemberCount(legacyAuth: Bool)
var path: String {
switch self {
case .files: return "files"
case .file(let fileId): return "files/\(fileId)"
// Utility
case .messages: return "messages"
case .messagesForServer(let serverId): return "messages/\(serverId)"
case .deletedMessages: return "deleted_messages"
case .onion: return "oxen/v4/lsrpc"
case .batch: return "batch"
case .sequence: return "sequence"
case .capabilities: return "capabilities"
case .moderators: return "moderators"
case .blockList: return "block_list"
case .blockListIndividual(let publicKey): return "block_list/\(publicKey)"
case .banAndDeleteAll: return "ban_and_delete_all"
// Rooms
case .rooms: return "rooms"
case .roomInfo(let roomName): return "rooms/\(roomName)"
case .roomImage(let roomName): return "rooms/\(roomName)/image"
case .room(let roomToken): return "room/\(roomToken)"
case .roomPollInfo(let roomToken, let infoUpdated): return "room/\(roomToken)/pollInfo/\(infoUpdated)"
// Messages
case .roomMessage(let roomToken):
return "room/\(roomToken)/message"
case .roomMessageIndividual(let roomToken, let messageId):
return "room/\(roomToken)/message/\(messageId)"
case .roomMessagesRecent(let roomToken):
return "room/\(roomToken)/messages/recent"
case .roomMessagesBefore(let roomToken, let messageId):
return "room/\(roomToken)/messages/before/\(messageId)"
case .roomMessagesSince(let roomToken, let seqNo):
return "room/\(roomToken)/messages/since/\(seqNo)"
// Pinning
case .roomPinMessage(let roomToken, let messageId):
return "room/\(roomToken)/pin/\(messageId)"
case .roomUnpinMessage(let roomToken, let messageId):
return "room/\(roomToken)/unpin/\(messageId)"
case .roomUnpinAll(let roomToken):
return "room/\(roomToken)/unpin/all"
// Files
case .roomFile(let roomToken): return "room/\(roomToken)/file"
case .roomFileJson(let roomToken): return "room/\(roomToken)/fileJSON"
case .roomFileIndividual(let roomToken, let fileId):
// Note: The 'fileName' value is ignored by the server and is only used to distinguish
// this from the 'Json' variant
let fileName: String = ""
return "room/\(roomToken)/file/\(fileId)/\(fileName)"
case .roomFileIndividualJson(let roomToken, let fileId):
return "room/\(roomToken)/file/\(fileId)"
// Users
case .userBan(let sessionId): return "user/\(sessionId)/ban"
case .userUnban(let sessionId): return "user/\(sessionId)/unban"
case .userPermission(let sessionId): return "user/\(sessionId)/permission"
case .userModerator(let sessionId): return "user/\(sessionId)/moderator"
case .userDeleteMessages(let sessionId): return "user/\(sessionId)/deleteMessages"
// Legacy endpoints (to be deprecated and removed)
// TODO: Look for a nicer way to prepend 'legacy'? (OnionRequestAPI messes with this but the new auth needs it to be correct...)
// TODO: Look for a nicer way to prepend 'legacy'? (OnionRequestAPI messes with this but the new auth needs it to be correct... )
case .legacyFiles: return "legacy/files"
case .legacyFile(let fileId): return "legacy/files/\(fileId)"
case .legacyMessages: return "legacy/messages"
case .legacyMessagesForServer(let serverId): return "legacy/messages/\(serverId)"
case .legacyDeletedMessages: return "legacy/deleted_messages"
case .legacyModerators: return "legacy/moderators"
case .legacyBlockList: return "legacy/block_list"
case .legacyBlockListIndividual(let publicKey): return "legacy/block_list/\(publicKey)"
case .legacyBanAndDeleteAll: return "legacy/ban_and_delete_all"
case .legacyCompactPoll(let useLegacyAuth):
return "\(useLegacyAuth ? "" : "legacy/")compact_poll"
@ -60,6 +163,10 @@ enum Endpoint {
case .legacyAuthTokenClaim(let useLegacyAuth):
return "\(useLegacyAuth ? "" : "legacy/")claim_auth_token"
case .legacyRooms: return "legacy/rooms"
case .legacyRoomInfo(let roomName): return "legacy/rooms/\(roomName)"
case .legacyRoomImage(let roomName): return "legacy/rooms/\(roomName)/image"
case .legacyMemberCount(let useLegacyAuth):
return "\(useLegacyAuth ? "" : "legacy/")member_count"
}
@ -68,7 +175,11 @@ enum Endpoint {
var useLegacyAuth: Bool {
switch self {
// File upload/download should use legacy auth
case .files, .file: return true
case .legacyFiles, .legacyFile, .legacyMessages,
.legacyMessagesForServer, .legacyDeletedMessages,
.legacyModerators, .legacyBlockList,
.legacyBlockListIndividual, .legacyBanAndDeleteAll:
return true
case .legacyCompactPoll(let useLegacyAuth),
.legacyAuthToken(let useLegacyAuth),
@ -76,6 +187,9 @@ enum Endpoint {
.legacyAuthTokenClaim(let useLegacyAuth),
.legacyMemberCount(let useLegacyAuth):
return useLegacyAuth
case .legacyRooms, .legacyRoomInfo, .legacyRoomImage:
return true
default: return false
}

View File

@ -1,12 +1,13 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import SessionUtilitiesKit
extension OpenGroupAPIV2 {
struct Request {
let verb: HTTP.Verb
let room: String?
let method: HTTP.Verb
let server: String
let room: String? // TODO: Remove this?
let endpoint: Endpoint
let queryParameters: [QueryParam: String]
let body: Data?
@ -17,9 +18,9 @@ extension OpenGroupAPIV2 {
let useOnionRouting: Bool
init(
verb: HTTP.Verb,
room: String?,
method: HTTP.Verb = .get,
server: String,
room: String? = nil,
endpoint: Endpoint,
queryParameters: [QueryParam: String] = [:],
body: Data? = nil,
@ -27,9 +28,9 @@ extension OpenGroupAPIV2 {
isAuthRequired: Bool = true,
useOnionRouting: Bool = true
) {
self.verb = verb
self.room = room
self.method = method
self.server = server
self.room = room
self.endpoint = endpoint
self.queryParameters = queryParameters
self.body = body
@ -39,19 +40,21 @@ extension OpenGroupAPIV2 {
}
var url: URL? {
guard verb == .get else { return URL(string: "\(server)/\(endpoint.path)") }
return URL(string: "\(server)\(urlPathAndParamsString)")
}
var urlPathAndParamsString: String {
guard method == .get else { return "/\(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: "?")
)
return [
"/\(endpoint.path)",
queryParameters
.map { key, value in "\(key.rawValue)=\(value)" }
.joined(separator: "&")
]
.compactMap { $0 }
.filter { !$0.isEmpty }
.joined(separator: "?")
}
}
}

View File

@ -97,8 +97,11 @@ public final class MessageSender : NSObject {
// MARK: Convenience
public static func send(_ message: Message, to destination: Message.Destination, using transaction: Any) -> Promise<Void> {
switch destination {
case .contact(_), .closedGroup(_): return sendToSnodeDestination(destination, message: message, using: transaction)
case .openGroup(_, _), .openGroupV2(_, _): return sendToOpenGroupDestination(destination, message: message, using: transaction)
case .contact, .closedGroup:
return sendToSnodeDestination(destination, message: message, using: transaction)
case .legacyOpenGroup, .openGroup:
return sendToOpenGroupDestination(destination, message: message, using: transaction)
}
}
@ -117,11 +120,13 @@ public final class MessageSender : NSObject {
message.sentTimestamp = NSDate.millisecondTimestamp()
}
message.sender = userPublicKey
switch destination {
case .contact(let publicKey): message.recipient = publicKey
case .closedGroup(let groupPublicKey): message.recipient = groupPublicKey
case .openGroup(_, _), .openGroupV2(_, _): preconditionFailure()
case .contact(let publicKey): message.recipient = publicKey
case .closedGroup(let groupPublicKey): message.recipient = groupPublicKey
case .legacyOpenGroup, .openGroup: preconditionFailure()
}
let isSelfSend = (message.recipient == userPublicKey)
// Set the failure handler (need it here already for precondition failure handling)
func handleFailure(with error: Swift.Error, using transaction: YapDatabaseReadWriteTransaction) {
@ -167,29 +172,41 @@ public final class MessageSender : NSObject {
let ciphertext: Data
do {
switch destination {
case .contact(let publicKey): ciphertext = try encryptWithSessionProtocol(plaintext, for: publicKey)
case .closedGroup(let groupPublicKey):
guard let encryptionKeyPair = Storage.shared.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey) else { throw Error.noKeyPair }
ciphertext = try encryptWithSessionProtocol(plaintext, for: encryptionKeyPair.hexEncodedPublicKey)
case .openGroup(_, _), .openGroupV2(_, _): preconditionFailure()
case .contact(let publicKey):
ciphertext = try encryptWithSessionProtocol(plaintext, for: publicKey)
case .closedGroup(let groupPublicKey):
guard let encryptionKeyPair = Storage.shared.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey) else {
throw Error.noKeyPair
}
ciphertext = try encryptWithSessionProtocol(plaintext, for: encryptionKeyPair.hexEncodedPublicKey)
case .legacyOpenGroup, .openGroup: preconditionFailure()
}
} catch {
}
catch {
SNLog("Couldn't encrypt message for destination: \(destination) due to error: \(error).")
handleFailure(with: error, using: transaction)
return promise
}
// Wrap the result
let kind: SNProtoEnvelope.SNProtoEnvelopeType
let senderPublicKey: String
switch destination {
case .contact(_):
kind = .sessionMessage
senderPublicKey = ""
case .closedGroup(let groupPublicKey):
kind = .closedGroupMessage
senderPublicKey = groupPublicKey
case .openGroup(_, _), .openGroupV2(_, _): preconditionFailure()
case .contact:
kind = .sessionMessage
senderPublicKey = ""
case .closedGroup(let groupPublicKey):
kind = .closedGroupMessage
senderPublicKey = groupPublicKey
case .legacyOpenGroup, .openGroup: preconditionFailure()
}
let wrappedMessage: Data
do {
wrappedMessage = try MessageWrapper.wrap(type: kind, timestamp: message.sentTimestamp!,
@ -277,16 +294,27 @@ public final class MessageSender : NSObject {
}
switch destination {
case .contact(_): preconditionFailure()
case .closedGroup(_): preconditionFailure()
case .openGroup(let channel, let server): message.recipient = "\(server).\(channel)"
case .openGroupV2(let room, let server): message.recipient = "\(server).\(room)"
case .contact(_): preconditionFailure()
case .closedGroup(_): preconditionFailure()
case .legacyOpenGroup(let channel, let server): message.recipient = "\(server).\(channel)"
case .openGroup(let room, let server, let whisperTo, let whisperMods, _):
message.recipient = [
server,
room,
whisperTo,
(whisperTo == nil && whisperMods ? "mods" : nil)
]
.compactMap { $0 }
.joined(separator: ".")
}
// Set the failure handler (need it here already for precondition failure handling)
func handleFailure(with error: Swift.Error, using transaction: YapDatabaseReadWriteTransaction) {
MessageSender.handleFailedMessageSend(message, with: error, using: transaction)
seal.reject(error)
}
// Validate the message
guard let message = message as? VisibleMessage else {
#if DEBUG
@ -296,45 +324,84 @@ public final class MessageSender : NSObject {
return promise
#endif
}
guard message.isValid else { handleFailure(with: Error.invalidMessage, using: transaction); return promise }
guard message.isValid else {
handleFailure(with: Error.invalidMessage, using: transaction)
return promise
}
// Attach the user's profile
guard let name = storage.getUser()?.name else { handleFailure(with: Error.noUsername, using: transaction); return promise }
guard let name = storage.getUser()?.name else {
handleFailure(with: Error.noUsername, using: transaction)
return promise
}
if let profileKey = storage.getUser()?.profileEncryptionKey?.keyData, let profilePictureURL = storage.getUser()?.profilePictureURL {
message.profile = VisibleMessage.Profile(displayName: name, profileKey: profileKey, profilePictureURL: profilePictureURL)
} else {
}
else {
message.profile = VisibleMessage.Profile(displayName: name)
}
// Convert it to protobuf
guard let proto = message.toProto(using: transaction) else { handleFailure(with: Error.protoConversionFailed, using: transaction); return promise }
guard let proto = message.toProto(using: transaction) else {
handleFailure(with: Error.protoConversionFailed, using: transaction)
return promise
}
// Serialize the protobuf
let plaintext: Data
do {
plaintext = (try proto.serializedData() as NSData).paddedMessageBody()
} catch {
}
catch {
SNLog("Couldn't serialize proto due to error: \(error).")
handleFailure(with: error, using: transaction)
return promise
}
// 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, 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)
seal.fulfill(())
}, completion: { })
}.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
storage.write(with: { transaction in
handleFailure(with: error, using: transaction as! YapDatabaseReadWriteTransaction)
}, completion: { })
// Send the result
guard case .openGroup(let room, let server, let whisperTo, let whisperMods, _) = destination else {
preconditionFailure()
}
// Return
return promise
// 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, with: openGroupV2.publicKey)
return promise // TODO: Remove!!!
// OpenGroupAPIV2
// .send(
// plaintext,
// to: room,
// on: server,
// whisperTo: whisperTo,
// whisperMods: whisperMods,
// with: openGroupV2.publicKey
// )
// .done(on: DispatchQueue.global(qos: .userInitiated)) { response in
// print("RAWR")
//// message.openGroupServerMessageID = given(response.serverID) { UInt64($0) }
//// storage.write(with: { transaction in
//// Storage.shared.addReceivedMessageTimestamp(message.sentTimestamp, using: transaction)
////
//// MessageSender.handleSuccessfulMessageSend(message, to: destination, serverTimestamp: response.sentTimestamp, using: transaction)
//// seal.fulfill(())
//// }, completion: { })
// }
// .catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
// storage.write(with: { transaction in
// handleFailure(with: error, using: transaction as! YapDatabaseReadWriteTransaction)
// }, completion: { })
// }
// // Return
// return promise
}
// MARK: Success & Failure Handling

View File

@ -0,0 +1,12 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import PromiseKit
extension Promise where T == Data {
func decoded<R: Decodable>(as type: R.Type, on queue: DispatchQueue? = nil, error: Error? = nil) -> Promise<R> {
self.map(on: queue) { data -> R in
try data.decoded(as: type, customError: error)
}
}
}

View File

@ -0,0 +1,11 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
internal extension String {
func appending(_ other: String?) -> String {
guard let value: String = other else { return self }
return self.appending(value)
}
}

View File

@ -328,7 +328,7 @@ public enum OnionRequestAPI {
// 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"))
// .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) }
@ -382,90 +382,148 @@ public enum OnionRequestAPI {
return seal.reject(error)
}
let destinationSymmetricKey = intermediate.destinationSymmetricKey
HTTP.execute(.post, url, body: body).done2 { json in
guard let base64EncodedIVAndCiphertext = json["result"] as? String,
let ivAndCiphertext = Data(base64Encoded: base64EncodedIVAndCiphertext), ivAndCiphertext.count >= AESGCM.ivSize else { return seal.reject(HTTP.Error.invalidJSON) }
do {
let data = try AESGCM.decrypt(ivAndCiphertext, with: destinationSymmetricKey)
guard let json = try JSONSerialization.jsonObject(with: data, options: [ .fragmentsAllowed ]) as? JSON,
let statusCode = json["status_code"] as? Int ?? json["status"] as? Int else { return seal.reject(HTTP.Error.invalidJSON) }
if statusCode == 406 { // Clock out of sync
SNLog("The user's clock is out of sync with the service node network.")
seal.reject(SnodeAPI.Error.clockOutOfSync)
} else if let bodyAsString = json["body"] as? String {
guard let bodyAsData = bodyAsString.data(using: .utf8),
let body = try JSONSerialization.jsonObject(with: bodyAsData, options: [ .fragmentsAllowed ]) as? JSON else { return seal.reject(HTTP.Error.invalidJSON) }
if let timestamp = body["t"] as? Int64 {
let offset = timestamp - Int64(NSDate.millisecondTimestamp())
SnodeAPI.clockOffset = offset
HTTP.execute(.post, url, body: body)
.done2 { json in
guard let base64EncodedIVAndCiphertext = json["result"] as? String, let ivAndCiphertext = Data(base64Encoded: base64EncodedIVAndCiphertext), ivAndCiphertext.count >= AESGCM.ivSize else {
return seal.reject(HTTP.Error.invalidJSON)
}
do {
let data = try AESGCM.decrypt(ivAndCiphertext, with: destinationSymmetricKey)
// The JSON data can be either an array or an object so can't cast to 'JSON' here
// TODO: Would be nice to ditch this 'JSONSerialization' behaviour if we can
guard let jsonObject = try? JSONSerialization.jsonObject(with: data, options: [ .fragmentsAllowed ]) else {
return seal.reject(HTTP.Error.invalidJSON)
}
guard 200...299 ~= statusCode else {
return seal.reject(Error.httpRequestFailedAtDestination(statusCode: UInt(statusCode), json: body, destination: destination))
// TODO: How do we now handle this case when the `status_code` is out of sync now that the value isn't provided?
// TODO: Upgrade to V4?
var customStatusCode: Int = 200
if let json: JSON = jsonObject as? JSON, let bodyStatusCode: Int = (((json["status_code"] as? Int) ?? json["status"] as? Int) ?? json["code"] as? Int) {
guard bodyStatusCode != 406 else {
SNLog("The user's clock is out of sync with the service node network.")
return seal.reject(SnodeAPI.Error.clockOutOfSync)
}
customStatusCode = bodyStatusCode
}
seal.fulfill(data)
} else {
guard 200...299 ~= statusCode else {
return seal.reject(Error.httpRequestFailedAtDestination(statusCode: UInt(statusCode), json: json, destination: destination))
if let json: JSON = jsonObject as? JSON, let bodyAsString: String = json["body"] as? String {
guard let bodyAsData = bodyAsString.data(using: .utf8), let body = try JSONSerialization.jsonObject(with: bodyAsData, options: [ .fragmentsAllowed ]) as? JSON else {
return seal.reject(HTTP.Error.invalidJSON)
}
if let timestamp = body["t"] as? Int64 {
let offset = timestamp - Int64(NSDate.millisecondTimestamp())
SnodeAPI.clockOffset = offset
}
guard 200...299 ~= customStatusCode else {
return seal.reject(
Error.httpRequestFailedAtDestination(
statusCode: UInt(customStatusCode),
json: body,
destination: destination
)
)
}
return seal.fulfill(data)
}
guard 200...299 ~= customStatusCode else {
return seal.reject(
Error.httpRequestFailedAtDestination(
statusCode: UInt(customStatusCode),
json: json,
destination: destination
)
)
}
seal.fulfill(data)
}
} catch {
catch {
seal.reject(error)
}
}
.catch2 { error in
seal.reject(error)
}
}.catch2 { error in
seal.reject(error)
}
}.catch2 { error in
seal.reject(error)
}
}
promise.catch2 { error in // Must be invoked on Threading.workQueue
guard case HTTP.Error.httpRequestFailed(let statusCode, let json) = error, let guardSnode = guardSnode else { return }
guard case HTTP.Error.httpRequestFailed(let statusCode, let json) = error, let guardSnode = guardSnode else {
return
}
let path = paths.first { $0.contains(guardSnode) }
func handleUnspecificError() {
guard let path = path else { return }
var pathFailureCount = OnionRequestAPI.pathFailureCount[path] ?? 0
pathFailureCount += 1
if pathFailureCount >= pathFailureThreshold {
dropGuardSnode(guardSnode)
path.forEach { snode in
SnodeAPI.handleError(withStatusCode: statusCode, json: json, forSnode: snode) // Intentionally don't throw
}
drop(path)
} else {
}
else {
OnionRequestAPI.pathFailureCount[path] = pathFailureCount
}
}
let prefix = "Next node not found: "
if let message = json?["result"] as? String, message.hasPrefix(prefix) {
let ed25519PublicKey = message[message.index(message.startIndex, offsetBy: prefix.count)..<message.endIndex]
if let path = path, let snode = path.first(where: { $0.publicKeySet.ed25519Key == ed25519PublicKey }) {
var snodeFailureCount = OnionRequestAPI.snodeFailureCount[snode] ?? 0
snodeFailureCount += 1
if snodeFailureCount >= snodeFailureThreshold {
SnodeAPI.handleError(withStatusCode: statusCode, json: json, forSnode: snode) // Intentionally don't throw
do {
try drop(snode)
} catch {
}
catch {
handleUnspecificError()
}
} else {
}
else {
OnionRequestAPI.snodeFailureCount[snode] = snodeFailureCount
}
} else {
// Do nothing
}
} else if let message = json?["result"] as? String, message == "Loki Server error" {
}
else if let message = json?["result"] as? String, message == "Loki Server error" {
// Do nothing
} else if case .server(let host, _, _, _, _) = destination, host == "116.203.70.33" && statusCode == 0 {
}
else if case .server(let host, _, _, _, _) = destination, host == "116.203.70.33" && statusCode == 0 {
// FIXME: Temporary thing to kick out nodes that can't talk to the V2 OGS yet
handleUnspecificError()
} else if statusCode == 0 { // Timeout
}
else if statusCode == 0 { // Timeout
// Do nothing
} else {
}
else {
handleUnspecificError()
}
}
return promise
}
}

View File

@ -68,7 +68,7 @@ public enum HTTP {
}
// MARK: Verb
public enum Verb : String {
public enum Verb: String, Codable {
case get = "GET"
case put = "PUT"
case post = "POST"