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:
parent
2284375fc0
commit
4f3900771e
|
@ -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 */
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -174,7 +174,6 @@ private extension MentionSelectionView {
|
|||
|
||||
// MARK: - Delegate
|
||||
|
||||
protocol MentionSelectionViewDelegate : class {
|
||||
|
||||
protocol MentionSelectionViewDelegate: AnyObject {
|
||||
func handleMentionSelected(_ mention: Mention, from view: MentionSelectionView)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -383,7 +383,7 @@ static NSTimeInterval launchStartedAt;
|
|||
}
|
||||
|
||||
if (CurrentAppContext().isMainApp) {
|
||||
[SNOpenGroupAPIV2 getDefaultRoomsIfNeeded];
|
||||
[SNOpenGroupAPIV2 legacyGetDefaultRoomsIfNeeded];
|
||||
}
|
||||
|
||||
[[SNSnodeAPI getSnodePool] retainUntilComplete];
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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]?
|
||||
}
|
||||
}
|
|
@ -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?
|
||||
}
|
||||
}
|
|
@ -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"
|
|
@ -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"
|
|
@ -3,7 +3,7 @@
|
|||
import Foundation
|
||||
|
||||
extension OpenGroupAPIV2 {
|
||||
struct RoomsResponse: Codable {
|
||||
let rooms: [RoomInfo]
|
||||
struct LegacyGetInfoResponse: Codable {
|
||||
let room: LegacyRoomInfo
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
import Foundation
|
||||
|
||||
extension OpenGroupAPIV2 {
|
||||
public struct RoomInfo: Codable {
|
||||
public struct LegacyRoomInfo: Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case name
|
|
@ -3,7 +3,7 @@
|
|||
import Foundation
|
||||
|
||||
extension OpenGroupAPIV2 {
|
||||
struct GetInfoResponse: Codable {
|
||||
let room: RoomInfo
|
||||
struct LegacyRoomsResponse: Codable {
|
||||
let rooms: [LegacyRoomInfo]
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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?
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
@ -77,6 +188,9 @@ enum Endpoint {
|
|||
.legacyMemberCount(let useLegacyAuth):
|
||||
return useLegacyAuth
|
||||
|
||||
case .legacyRooms, .legacyRoomInfo, .legacyRoomImage:
|
||||
return true
|
||||
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)")
|
||||
}
|
||||
|
||||
return URL(
|
||||
string: [
|
||||
"\(server)/\(endpoint.path)",
|
||||
queryParameters
|
||||
.map { key, value in "\(key.rawValue)=\(value)" }
|
||||
.joined(separator: "&")
|
||||
]
|
||||
.compactMap { $0 }
|
||||
.filter { !$0.isEmpty }
|
||||
.joined(separator: "?")
|
||||
)
|
||||
var urlPathAndParamsString: String {
|
||||
guard method == .get else { return "/\(endpoint.path)" }
|
||||
|
||||
return [
|
||||
"/\(endpoint.path)",
|
||||
queryParameters
|
||||
.map { key, value in "\(key.rawValue)=\(value)" }
|
||||
.joined(separator: "&")
|
||||
]
|
||||
.compactMap { $0 }
|
||||
.filter { !$0.isEmpty }
|
||||
.joined(separator: "?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue