From a4692499c4c87fa096138c55a263824b6b2e6cf1 Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Wed, 28 Feb 2018 01:19:26 +0400 Subject: [PATCH 1/2] no message --- TelegramCore.xcodeproj/project.pbxproj | 28 +- TelegramCore/BotPaymentForm.swift | 4 +- TelegramCore/CallSessionManager.swift | 60 +- .../CkeckPeerChatServiceActions.swift | 21 + .../DeleteMessagesInteractively.swift | 4 +- TelegramCore/ForwardGame.swift | 27 + .../ManagedSecretChatOutgoingOperations.swift | 167 +- ...essageContentAsConsumedInteractively.swift | 2 +- TelegramCore/PendingMessageManager.swift | 144 +- .../PendingMessageUploadedContent.swift | 16 +- ...ecretChatIncomingDecryptedOperations.swift | 242 ++- ...ecretChatIncomingEncryptedOperations.swift | 193 ++- TelegramCore/SecretApiLayer73.swift | 1524 +++++++++++++++++ TelegramCore/SecretChatEncryption.swift | 426 +++-- TelegramCore/SecretChatLayerNegotiation.swift | 63 + .../SecretChatOutgoingOperation.swift | 10 + TelegramCore/SecretChatRekeySession.swift | 11 +- TelegramCore/SecretChatState.swift | 17 +- TelegramCore/SecretChatStateBridge.swift | 29 - TelegramCore/Serialization.swift | 13 +- TelegramCore/UpdateSecretChat.swift | 2 +- 21 files changed, 2624 insertions(+), 379 deletions(-) create mode 100644 TelegramCore/CkeckPeerChatServiceActions.swift create mode 100644 TelegramCore/ForwardGame.swift create mode 100644 TelegramCore/SecretApiLayer73.swift create mode 100644 TelegramCore/SecretChatLayerNegotiation.swift delete mode 100644 TelegramCore/SecretChatStateBridge.swift diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index 3a3517f29f..ace902b5e3 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -32,8 +32,6 @@ C25638021E79E7FC00311607 /* TwoStepVerification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA0ABC1E76C908005BB9B7 /* TwoStepVerification.swift */; }; C26A37EF1E5E0C41006977AC /* ChannelParticipants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BB7C591E5C8074001527C3 /* ChannelParticipants.swift */; }; C27982511E72C97800262BFD /* MacosLegacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C27982501E72C97800262BFD /* MacosLegacy.swift */; }; - C27982531E73077800262BFD /* SecretChatStateBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = C27982521E73077800262BFD /* SecretChatStateBridge.swift */; }; - C27982541E73078400262BFD /* SecretChatStateBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = C27982521E73077800262BFD /* SecretChatStateBridge.swift */; }; C28725421EF967E700613564 /* NotificationInfoMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = C28725411EF967E700613564 /* NotificationInfoMessageAttribute.swift */; }; C28725431EF967E700613564 /* NotificationInfoMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = C28725411EF967E700613564 /* NotificationInfoMessageAttribute.swift */; }; C29340F31F5080FA0074991E /* UpdateGroupSpecificStickerset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C29340F21F5080FA0074991E /* UpdateGroupSpecificStickerset.swift */; }; @@ -103,6 +101,11 @@ D0177B7B1DF8A16C00A5083A /* SecretChatState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0177B7A1DF8A16C00A5083A /* SecretChatState.swift */; }; D018D3371E648ACF00C5E089 /* CreateChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018D3361E648ACF00C5E089 /* CreateChannel.swift */; }; D018D3381E648ACF00C5E089 /* CreateChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018D3361E648ACF00C5E089 /* CreateChannel.swift */; }; + D018EE002044939F00CBB130 /* SecretApiLayer73.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018EDFF2044939F00CBB130 /* SecretApiLayer73.swift */; }; + D018EE0220458E1E00CBB130 /* SecretChatLayerNegotiation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018EE0120458E1E00CBB130 /* SecretChatLayerNegotiation.swift */; }; + D018EE0320458E1E00CBB130 /* SecretChatLayerNegotiation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018EE0120458E1E00CBB130 /* SecretChatLayerNegotiation.swift */; }; + D018EE052045E95000CBB130 /* CkeckPeerChatServiceActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018EE042045E95000CBB130 /* CkeckPeerChatServiceActions.swift */; }; + D018EE062045E95000CBB130 /* CkeckPeerChatServiceActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018EE042045E95000CBB130 /* CkeckPeerChatServiceActions.swift */; }; D019B1CC1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D019B1CB1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift */; }; D019B1CD1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D019B1CB1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift */; }; D01A21A61F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A21A51F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift */; }; @@ -556,6 +559,8 @@ D0E652201E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */; }; D0E817492010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */; }; D0E8174A2010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */; }; + D0E8B8B32044706300605593 /* ForwardGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8B8B22044706300605593 /* ForwardGame.swift */; }; + D0E8B8B42044706300605593 /* ForwardGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8B8B22044706300605593 /* ForwardGame.swift */; }; D0F02CE51E9926C40065DEE2 /* ManagedConfigurationUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */; }; D0F02CE61E9926C50065DEE2 /* ManagedConfigurationUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */; }; D0F3A89F1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */; }; @@ -644,7 +649,6 @@ C23BC3861E9BE3CA00D79F92 /* ImportContact.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportContact.swift; sourceTree = ""; }; C251D7421E65E50500283EDE /* StickerSetInstallation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerSetInstallation.swift; sourceTree = ""; }; C27982501E72C97800262BFD /* MacosLegacy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MacosLegacy.swift; sourceTree = ""; }; - C27982521E73077800262BFD /* SecretChatStateBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatStateBridge.swift; sourceTree = ""; }; C28725411EF967E700613564 /* NotificationInfoMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationInfoMessageAttribute.swift; sourceTree = ""; }; C29340F21F5080FA0074991E /* UpdateGroupSpecificStickerset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateGroupSpecificStickerset.swift; sourceTree = ""; }; C2E064671ECEEF0A00387BB8 /* TelegramMediaInvoice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramMediaInvoice.swift; sourceTree = ""; }; @@ -674,6 +678,9 @@ D017495F1E118FC30057C89A /* AccountIntermediateState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountIntermediateState.swift; sourceTree = ""; }; D0177B7A1DF8A16C00A5083A /* SecretChatState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatState.swift; sourceTree = ""; }; D018D3361E648ACF00C5E089 /* CreateChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateChannel.swift; sourceTree = ""; }; + D018EDFF2044939F00CBB130 /* SecretApiLayer73.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretApiLayer73.swift; sourceTree = ""; }; + D018EE0120458E1E00CBB130 /* SecretChatLayerNegotiation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretChatLayerNegotiation.swift; sourceTree = ""; }; + D018EE042045E95000CBB130 /* CkeckPeerChatServiceActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CkeckPeerChatServiceActions.swift; sourceTree = ""; }; D019B1CB1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatRekeySession.swift; sourceTree = ""; }; D01A21A51F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeSavedStickersOperation.swift; sourceTree = ""; }; D01A21A81F38CDDC00DDA104 /* ManagedSynchronizeSavedStickersOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeSavedStickersOperations.swift; sourceTree = ""; }; @@ -938,6 +945,7 @@ D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingChatContextResultMessageAttribute.swift; sourceTree = ""; }; D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateAccountPeerName.swift; sourceTree = ""; }; D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelAdminEventLogContext.swift; sourceTree = ""; }; + D0E8B8B22044706300605593 /* ForwardGame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardGame.swift; sourceTree = ""; }; D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedConfigurationUpdates.swift; sourceTree = ""; }; D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeChatInputStateOperation.swift; sourceTree = ""; }; D0F3A8A11E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeChatInputStateOperations.swift; sourceTree = ""; }; @@ -1300,6 +1308,7 @@ children = ( D0FA8BB51E223C16001E855B /* SecretApiLayer8.swift */, D0448C981E268F9A005A61A7 /* SecretApiLayer46.swift */, + D018EDFF2044939F00CBB130 /* SecretApiLayer73.swift */, D03B0D541D631A6900955575 /* Api.swift */, D03B0D551D631A6900955575 /* Buffer.swift */, D03B0D561D631A6900955575 /* Download.swift */, @@ -1364,6 +1373,7 @@ D0642EF81F3E05D700792790 /* EarliestUnseenPersonalMentionMessage.swift */, D0DB7F021F43030C00591D48 /* InstallInteractiveReadMessagesAction.swift */, D0B85AC41F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift */, + D0E8B8B22044706300605593 /* ForwardGame.swift */, ); name = Messages; sourceTree = ""; @@ -1595,6 +1605,7 @@ D02395D51F8D09A50070F5C2 /* ChannelHistoryAvailabilitySettings.swift */, D0C44B601FC616E200227BE0 /* SearchGroupMembers.swift */, D0F8C39C20178B9B00236FC5 /* GroupFeedPeers.swift */, + D018EE042045E95000CBB130 /* CkeckPeerChatServiceActions.swift */, ); name = Peers; sourceTree = ""; @@ -1615,7 +1626,7 @@ D0FA8BA91E1FB76E001E855B /* ManagedSecretChatOutgoingOperations.swift */, D019B1CB1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift */, D00C7CEA1E37A8540080C3D5 /* SetSecretChatMessageAutoremoveTimeoutInteractively.swift */, - C27982521E73077800262BFD /* SecretChatStateBridge.swift */, + D018EE0120458E1E00CBB130 /* SecretChatLayerNegotiation.swift */, ); name = "Secret Chats"; sourceTree = ""; @@ -1827,11 +1838,13 @@ D03B0CBD1D62234300955575 /* Regex.swift in Sources */, D00BDA191EE593D600C64C5E /* TelegramChannelAdminRights.swift in Sources */, D0B843B91DA7FF30005F29E1 /* NBMetadataCoreTest.m in Sources */, + D018EE002044939F00CBB130 /* SecretApiLayer73.swift in Sources */, D09A2FE61D7CD4940018FB72 /* TelegramChannel.swift in Sources */, D03B0D0E1D62255C00955575 /* UpdateGroup.swift in Sources */, D053B4181F18DE4F00E2D58A /* AuthorSignatureMessageAttribute.swift in Sources */, D0F3A89F1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift in Sources */, D01AC9231DD5E9A200E8160F /* ApplyUpdateMessage.swift in Sources */, + D0E8B8B32044706300605593 /* ForwardGame.swift in Sources */, D01A21AC1F38D10E00DDA104 /* SavedStickerItem.swift in Sources */, D0642EF91F3E05D700792790 /* EarliestUnseenPersonalMentionMessage.swift in Sources */, D03B0CF71D62250800955575 /* TelegramMediaImage.swift in Sources */, @@ -2042,6 +2055,7 @@ D05A32E41E6F0B2E002760B4 /* RecentAccountSessions.swift in Sources */, D01A21A61F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift in Sources */, D03E5E0C1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */, + D018EE052045E95000CBB130 /* CkeckPeerChatServiceActions.swift in Sources */, D0F3A8A51E82C94C00B4C64C /* SynchronizeableChatInputState.swift in Sources */, D03B0CD71D62245300955575 /* TelegramGroup.swift in Sources */, D0B8438C1DA7CF50005F29E1 /* BotInfo.swift in Sources */, @@ -2053,12 +2067,12 @@ D0F3A8A81E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift in Sources */, D03B0CE21D62249B00955575 /* InlineBotMessageAttribute.swift in Sources */, D0AB0B9A1D666520002C78E7 /* ManagedSynchronizePeerReadStates.swift in Sources */, - C27982531E73077800262BFD /* SecretChatStateBridge.swift in Sources */, D03B0D5B1D631A6900955575 /* Buffer.swift in Sources */, D0B843891DA7AB96005F29E1 /* ExportedInvitation.swift in Sources */, D03B0E441D631E6600955575 /* NetworkLogging.m in Sources */, D0528E651E65C82400E2FEF5 /* UpdateContactName.swift in Sources */, D03121021DA57E93006A2A60 /* TelegramPeerNotificationSettings.swift in Sources */, + D018EE0220458E1E00CBB130 /* SecretChatLayerNegotiation.swift in Sources */, D0C48F391E8138DF0075317D /* ArchivedStickerPacksInfo.swift in Sources */, C239BE971E62EE1E00C2C453 /* LoadMessagesIfNecessary.swift in Sources */, D03B0CC11D62235000955575 /* StringFormat.swift in Sources */, @@ -2106,7 +2120,6 @@ files = ( C29340F41F5081280074991E /* UpdateGroupSpecificStickerset.swift in Sources */, C25638021E79E7FC00311607 /* TwoStepVerification.swift in Sources */, - C27982541E73078400262BFD /* SecretChatStateBridge.swift in Sources */, D00DBBD81E64E41100DB5485 /* CreateSecretChat.swift in Sources */, C2FD33EC1E696C79008D13D4 /* GroupsInCommon.swift in Sources */, C239BE9D1E630CB300C2C453 /* UpdatePinnedMessage.swift in Sources */, @@ -2144,6 +2157,7 @@ D0B418B81D7E05A6004562A4 /* ContactManagement.swift in Sources */, D0E23DE01E8082A400B9B6D2 /* ArchivedStickerPacks.swift in Sources */, D050F2521E4A59C200988324 /* JoinLink.swift in Sources */, + D018EE0320458E1E00CBB130 /* SecretChatLayerNegotiation.swift in Sources */, D0F7B1E91E045C87007EB8A5 /* PeerCommands.swift in Sources */, D00D97C81E32901700E5C2B6 /* PeerInputActivity.swift in Sources */, D0754D2B1EEE10FC00884F6E /* BotPaymentForm.swift in Sources */, @@ -2215,6 +2229,7 @@ D0FA35091EA632E400E56FFA /* CollectCacheUsageStats.swift in Sources */, D001F3F31E128A1C007A8C60 /* UpdateMessageService.swift in Sources */, D0C50E351E93A86600F62E39 /* CallSessionManager.swift in Sources */, + D018EE062045E95000CBB130 /* CkeckPeerChatServiceActions.swift in Sources */, D0B8442D1DAB91E0005F29E1 /* NBMetadataCoreTest.m in Sources */, D0C27B431F4B58C000A4E170 /* PeerSpecificStickerPack.swift in Sources */, D0B844131DAB91CD005F29E1 /* StringFormat.swift in Sources */, @@ -2292,6 +2307,7 @@ D03C536E1DAD5CA9004C17B3 /* PhoneNumber.swift in Sources */, D0BC387C1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */, D0528E6B1E65DD2100E2FEF5 /* WebpagePreview.swift in Sources */, + D0E8B8B42044706300605593 /* ForwardGame.swift in Sources */, D0B844111DAB91CD005F29E1 /* Regex.swift in Sources */, D0B844321DAB91E0005F29E1 /* NBPhoneMetaDataGenerator.m in Sources */, D0BEAF5E1E54941B00BD963D /* Authorization.swift in Sources */, diff --git a/TelegramCore/BotPaymentForm.swift b/TelegramCore/BotPaymentForm.swift index 8b46d8448a..2ed3a8ff7c 100644 --- a/TelegramCore/BotPaymentForm.swift +++ b/TelegramCore/BotPaymentForm.swift @@ -138,7 +138,7 @@ public struct BotPaymentForm { public let canSaveCredentials: Bool public let passwordMissing: Bool public let invoice: BotPaymentInvoice - public let providerId: Int32 + public let providerId: PeerId public let url: String public let nativeProvider: BotPaymentNativeProvider? public let savedInfo: BotPaymentRequestedInfo? @@ -229,7 +229,7 @@ public func fetchBotPaymentForm(postbox: Postbox, network: Network, messageId: M parsedSavedCredentials = .card(id: id, title: title) } } - return BotPaymentForm(canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, providerId: providerId, url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials) + return BotPaymentForm(canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, providerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: providerId), url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials) } } |> mapError { _ -> BotPaymentFormRequestError in return .generic } } diff --git a/TelegramCore/CallSessionManager.swift b/TelegramCore/CallSessionManager.swift index c2c77e924d..475c75845e 100644 --- a/TelegramCore/CallSessionManager.swift +++ b/TelegramCore/CallSessionManager.swift @@ -9,12 +9,47 @@ import Foundation import SwiftSignalKit #endif -public enum CallSessionError { +public enum CallSessionError: Equatable { case generic case privacyRestricted case notSupportedByPeer case serverProvided(String) case disconnected + + public static func ==(lhs: CallSessionError, rhs: CallSessionError) -> Bool { + switch lhs { + case .generic: + if case .generic = rhs { + return true + } else { + return false + } + case .privacyRestricted: + if case .privacyRestricted = rhs { + return true + } else { + return false + } + case .notSupportedByPeer: + if case .notSupportedByPeer = rhs { + return true + } else { + return false + } + case let .serverProvided(text): + if case .serverProvided(text) = rhs { + return true + } else { + return false + } + case .disconnected: + if case .disconnected = rhs { + return true + } else { + return false + } + } + } } public enum CallSessionEndedType { @@ -23,14 +58,31 @@ public enum CallSessionEndedType { case missed } -public enum CallSessionTerminationReason { +public enum CallSessionTerminationReason: Equatable { case ended(CallSessionEndedType) case error(CallSessionError) + + public static func ==(lhs: CallSessionTerminationReason, rhs: CallSessionTerminationReason) -> Bool { + switch lhs { + case let .ended(type): + if case .ended(type) = rhs { + return true + } else { + return false + } + case let .error(error): + if case .error(error) = rhs { + return true + } else { + return false + } + } + } } public struct ReportCallRating { - public let id:Int64 - public let accessHash:Int64 + public let id: Int64 + public let accessHash: Int64 } enum CallSessionInternalState { diff --git a/TelegramCore/CkeckPeerChatServiceActions.swift b/TelegramCore/CkeckPeerChatServiceActions.swift new file mode 100644 index 0000000000..e16e98e3fa --- /dev/null +++ b/TelegramCore/CkeckPeerChatServiceActions.swift @@ -0,0 +1,21 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +public func checkPeerChatServiceActions(postbox: Postbox, peerId: PeerId) -> Signal { + return postbox.modify { modifier -> Void in + if peerId.namespace == Namespaces.Peer.SecretChat { + if let state = modifier.getPeerChatState(peerId) as? SecretChatState { + let updatedState = secretChatCheckLayerNegotiationIfNeeded(modifier: modifier, peerId: peerId, state: state) + if state != updatedState { + modifier.setPeerChatState(peerId, state: updatedState) + } + } + } + } +} diff --git a/TelegramCore/DeleteMessagesInteractively.swift b/TelegramCore/DeleteMessagesInteractively.swift index ef90af35fb..20f28fdc5e 100644 --- a/TelegramCore/DeleteMessagesInteractively.swift +++ b/TelegramCore/DeleteMessagesInteractively.swift @@ -36,7 +36,7 @@ public func deleteMessagesInteractively(postbox: Postbox, messageIds: [MessageId case .basicLayer: layer = .layer8 case let .sequenceBasedLayer(sequenceState): - layer = SecretChatLayer(rawValue: sequenceState.layerNegotiationState.activeLayer) + layer = sequenceState.layerNegotiationState.activeLayer.secretChatLayer } if let layer = layer { var globallyUniqueIds: [Int64] = [] @@ -84,7 +84,7 @@ public func clearHistoryInteractively(postbox: Postbox, peerId: PeerId) -> Signa case .basicLayer: layer = .layer8 case let .sequenceBasedLayer(sequenceState): - layer = SecretChatLayer(rawValue: sequenceState.layerNegotiationState.activeLayer) + layer = sequenceState.layerNegotiationState.activeLayer.secretChatLayer } if let layer = layer { diff --git a/TelegramCore/ForwardGame.swift b/TelegramCore/ForwardGame.swift new file mode 100644 index 0000000000..b4ffd27ed3 --- /dev/null +++ b/TelegramCore/ForwardGame.swift @@ -0,0 +1,27 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +public func forwardGameWithScore(account: Account, messageId: MessageId, to peerId: PeerId) -> Signal { + return account.postbox.modify { modifier -> Signal in + if let message = modifier.getMessage(messageId), let fromPeer = modifier.getPeer(messageId.peerId), let fromInputPeer = apiInputPeer(fromPeer), let toPeer = modifier.getPeer(peerId), let toInputPeer = apiInputPeer(toPeer) { + return account.network.request(Api.functions.messages.forwardMessages(flags: 1 << 8, fromPeer: fromInputPeer, id: [messageId.id], randomId: [arc4random64()], toPeer: toInputPeer)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { updates -> Signal in + if let updates = updates { + account.stateManager.addUpdates(updates) + } + return .complete() + } + } + return .complete() + } |> switchToLatest +} diff --git a/TelegramCore/ManagedSecretChatOutgoingOperations.swift b/TelegramCore/ManagedSecretChatOutgoingOperations.swift index c9924f53de..8618b805ec 100644 --- a/TelegramCore/ManagedSecretChatOutgoingOperations.swift +++ b/TelegramCore/ManagedSecretChatOutgoingOperations.swift @@ -234,7 +234,7 @@ private func initialHandshakeAccept(postbox: Postbox, network: Network, peerId: case .basicLayer: layer = .layer8 case let .sequenceBasedLayer(sequenceState): - layer = SecretChatLayer(rawValue: sequenceState.layerNegotiationState.activeLayer) + layer = sequenceState.layerNegotiationState.activeLayer.secretChatLayer } if let layer = layer { updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: .reportLayerSupport(layer: layer, actionGloballyUniqueId: arc4random64(), layerSupport: 46), state: updatedState) @@ -326,6 +326,7 @@ private func pfsAcceptKey(postbox: Postbox, network: Network, peerId: PeerId, la private enum BoxedDecryptedMessage { case layer8(SecretApi8.DecryptedMessage) case layer46(SecretApi46.DecryptedMessage) + case layer73(SecretApi73.DecryptedMessage) func serialize(_ buffer: Buffer, role: SecretChatRole, sequenceInfo: SecretChatOperationSequenceInfo?) { switch self { @@ -350,6 +351,26 @@ private enum BoxedDecryptedMessage { assertionFailure() } + let _ = message.serialize(buffer, true) + case let .layer73(message): + buffer.appendInt32(0x1be31789) + let randomBytes = malloc(15)! + arc4random_buf(randomBytes, 15) + serializeBytes(Buffer(memory: randomBytes, size: 15, capacity: 15, freeWhenDone: false), buffer: buffer, boxed: false) + free(randomBytes) + buffer.appendInt32(73) + + if let sequenceInfo = sequenceInfo { + let inSeqNo = sequenceInfo.topReceivedOperationIndex * 2 + (role == .creator ? 0 : 1) + let outSeqNo = sequenceInfo.operationIndex * 2 + (role == .creator ? 1 : 0) + buffer.appendInt32(inSeqNo) + buffer.appendInt32(outSeqNo) + } else { + buffer.appendInt32(0) + buffer.appendInt32(0) + assertionFailure() + } + let _ = message.serialize(buffer, true) } } @@ -450,11 +471,57 @@ private func decryptedAttributes46(_ attributes: [TelegramMediaFileAttribute]) - return result } +private func decryptedAttributes73(_ attributes: [TelegramMediaFileAttribute]) -> [SecretApi73.DocumentAttribute] { + var result: [SecretApi73.DocumentAttribute] = [] + for attribute in attributes { + switch attribute { + case let .FileName(fileName): + result.append(.documentAttributeFilename(fileName: fileName)) + case .Animated: + result.append(.documentAttributeAnimated) + case let .Sticker(displayText, packReference, _): + var stickerSet: SecretApi73.InputStickerSet = .inputStickerSetEmpty + if let packReference = packReference, case let .name(name) = packReference { + stickerSet = .inputStickerSetShortName(shortName: name) + } + result.append(.documentAttributeSticker(alt: displayText, stickerset: stickerSet)) + case let .ImageSize(size): + result.append(.documentAttributeImageSize(w: Int32(size.width), h: Int32(size.height))) + case let .Video(duration, size, videoFlags): + var flags: Int32 = 0 + if videoFlags.contains(.instantRoundVideo) { + flags |= 1 << 0 + } + result.append(.documentAttributeVideo(flags: flags, duration: Int32(duration), w: Int32(size.width), h: Int32(size.height))) + case let .Audio(isVoice, duration, title, performer, waveform): + var flags: Int32 = 0 + if isVoice { + flags |= (1 << 10) + } + if let _ = title { + flags |= Int32(1 << 0) + } + if let _ = performer { + flags |= Int32(1 << 1) + } + var waveformBuffer: Buffer? + if let waveform = waveform { + flags |= Int32(1 << 2) + waveformBuffer = Buffer(data: waveform.makeData()) + } + result.append(.documentAttributeAudio(flags: flags, duration: Int32(duration), title: title, performer: performer, waveform: waveformBuffer)) + case .HasLinkedStickers: + break + } + } + return result +} + private func boxedDecryptedMessage(message: Message, globallyUniqueId: Int64, uploadedFile: SecretChatOutgoingFile?, layer: SecretChatLayer) -> BoxedDecryptedMessage { - var media: Media? = message.media.first + let media: Media? = message.media.first var messageAutoremoveTimeout: Int32 = 0 - var replyGlobalId:Int64? = nil - var flags:Int32 = 0 + var replyGlobalId: Int64? = nil + var flags: Int32 = 0 for attribute in message.attributes { if let attribute = attribute as? ReplyMessageAttribute { if let message = message.associatedMessages[attribute.messageId] { @@ -487,6 +554,13 @@ private func boxedDecryptedMessage(message: Message, globallyUniqueId: Int64, up let decryptedMedia = SecretApi46.DecryptedMessageMedia.decryptedMessageMediaPhoto(thumb: Buffer(), thumbW: 90, thumbH: 90, w: Int32(largestRepresentation.dimensions.width), h: Int32(largestRepresentation.dimensions.height), size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), caption: "") flags |= (1 << 9) return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId)) + case .layer73: + let decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaPhoto(thumb: Buffer(), thumbW: 90, thumbH: 90, w: Int32(largestRepresentation.dimensions.width), h: Int32(largestRepresentation.dimensions.height), size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), caption: "") + flags |= (1 << 9) + if message.groupingKey != nil { + flags |= (1 << 17) + } + return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId, groupedId: message.groupingKey)) } } else if let file = media as? TelegramMediaFile { switch layer { @@ -529,6 +603,35 @@ private func boxedDecryptedMessage(message: Message, globallyUniqueId: Int64, up flags |= (1 << 9) return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId)) } + case .layer73: + var decryptedMedia: SecretApi73.DecryptedMessageMedia? + + if let uploadedFile = uploadedFile { + var voiceDuration: Int32? + for attribute in file.attributes { + if case let .Audio(isVoice, duration, _, _, _) = attribute { + if isVoice { + voiceDuration = Int32(duration) + } + break + } + } + + if let voiceDuration = voiceDuration { + decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaAudio(duration: voiceDuration, mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv)) + } else { + decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaDocument(thumb: Buffer(), thumbW: 0, thumbH: 0, mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), attributes: decryptedAttributes73(file.attributes), caption: "") + } + } else { + if let resource = file.resource as? CloudDocumentMediaResource, let size = file.size { + decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaExternalDocument(id: resource.fileId, accessHash: resource.accessHash, date: 0, mimeType: file.mimeType, size: Int32(size), thumb: SecretApi73.PhotoSize.photoSizeEmpty(type: "s"), dcId: Int32(resource.datacenterId), attributes: decryptedAttributes73(file.attributes)) + } + } + + if let decryptedMedia = decryptedMedia { + flags |= (1 << 9) + return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId, groupedId: message.groupingKey)) + } } } } @@ -542,6 +645,8 @@ private func boxedDecryptedMessage(message: Message, globallyUniqueId: Int64, up return .layer8(.decryptedMessage(randomId: globallyUniqueId, randomBytes: randomBytes, message: message.text, media: .decryptedMessageMediaEmpty)) case .layer46: return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId)) + case .layer73: + return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId, groupedId: message.groupingKey)) } } @@ -557,6 +662,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B return .layer8(.decryptedMessageService(randomId: actionGloballyUniqueId, randomBytes: randomBytes, action: .decryptedMessageActionDeleteMessages(randomIds: globallyUniqueIds))) case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionDeleteMessages(randomIds: globallyUniqueIds))) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionDeleteMessages(randomIds: globallyUniqueIds))) } case let .screenshotMessages(layer, actionGloballyUniqueId, globallyUniqueIds): switch layer { @@ -568,6 +675,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B return .layer8(.decryptedMessageService(randomId: actionGloballyUniqueId, randomBytes: randomBytes, action: .decryptedMessageActionScreenshotMessages(randomIds: globallyUniqueIds))) case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionScreenshotMessages(randomIds: globallyUniqueIds))) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionScreenshotMessages(randomIds: globallyUniqueIds))) } case let .clearHistory(layer, actionGloballyUniqueId): switch layer { @@ -578,11 +687,15 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B return .layer8(.decryptedMessageService(randomId: actionGloballyUniqueId, randomBytes: randomBytes, action: .decryptedMessageActionFlushHistory)) case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionFlushHistory)) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionFlushHistory)) } case let .resendOperations(layer, actionGloballyUniqueId, fromSeqNo, toSeqNo): switch layer { case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionResend(startSeqNo: fromSeqNo, endSeqNo: toSeqNo))) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionResend(startSeqNo: fromSeqNo, endSeqNo: toSeqNo))) } case let .reportLayerSupport(layer, actionGloballyUniqueId, layerSupport): switch layer { @@ -594,31 +707,43 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B return .layer8(.decryptedMessageService(randomId: actionGloballyUniqueId, randomBytes: randomBytes, action: .decryptedMessageActionNotifyLayer(layer: layerSupport))) case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionNotifyLayer(layer: layerSupport))) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionNotifyLayer(layer: layerSupport))) } case let .pfsRequestKey(layer, actionGloballyUniqueId, rekeySessionId, gA): switch layer { case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionRequestKey(exchangeId: rekeySessionId, gA: Buffer(buffer: gA)))) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionRequestKey(exchangeId: rekeySessionId, gA: Buffer(buffer: gA)))) } case let .pfsAcceptKey(layer, actionGloballyUniqueId, rekeySessionId, gB, keyFingerprint): switch layer { case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionAcceptKey(exchangeId: rekeySessionId, gB: Buffer(buffer: gB), keyFingerprint: keyFingerprint))) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionAcceptKey(exchangeId: rekeySessionId, gB: Buffer(buffer: gB), keyFingerprint: keyFingerprint))) } case let .pfsAbortSession(layer, actionGloballyUniqueId, rekeySessionId): switch layer { case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionAbortKey(exchangeId: rekeySessionId))) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionAbortKey(exchangeId: rekeySessionId))) } case let .pfsCommitKey(layer, actionGloballyUniqueId, rekeySessionId, keyFingerprint): switch layer { case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionCommitKey(exchangeId: rekeySessionId, keyFingerprint: keyFingerprint))) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionCommitKey(exchangeId: rekeySessionId, keyFingerprint: keyFingerprint))) } case let .noop(layer, actionGloballyUniqueId): switch layer { case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionNoop)) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionNoop)) } case let .readMessageContents(layer, actionGloballyUniqueId, globallyUniqueIds): switch layer { @@ -630,6 +755,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B return .layer8(.decryptedMessageService(randomId: actionGloballyUniqueId, randomBytes: randomBytes, action: .decryptedMessageActionReadMessages(randomIds: globallyUniqueIds))) case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionReadMessages(randomIds: globallyUniqueIds))) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionReadMessages(randomIds: globallyUniqueIds))) } case let .setMessageAutoremoveTimeout(layer, actionGloballyUniqueId, timeout, _): switch layer { @@ -641,6 +768,8 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B return .layer8(.decryptedMessageService(randomId: actionGloballyUniqueId, randomBytes: randomBytes, action: .decryptedMessageActionSetMessageTTL(ttlSeconds: timeout))) case .layer46: return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionSetMessageTTL(ttlSeconds: timeout))) + case .layer73: + return .layer73(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionSetMessageTTL(ttlSeconds: timeout))) } } } @@ -681,7 +810,7 @@ private func replaceOutgoingOperationWithEmptyMessage(modifier: Modifier, peerId case .basicLayer: layer = .layer8 case let .sequenceBasedLayer(sequenceState): - layer = SecretChatLayer(rawValue: sequenceState.layerNegotiationState.activeLayer) + layer = sequenceState.layerNegotiationState.activeLayer.secretChatLayer } } if let layer = layer { @@ -719,7 +848,7 @@ private func sendMessage(postbox: Postbox, network: Network, messageId: MessageI switch result { case let .sentEncryptedMessage(date): timestamp = date - case let .sentEncryptedFile(date, file): + case let .sentEncryptedFile(date, _): timestamp = date } flags.remove(.Unsent) @@ -731,6 +860,9 @@ private func sendMessage(postbox: Postbox, network: Network, messageId: MessageI if let forwardInfo = currentMessage.forwardInfo { storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) } + + modifier.offsetPendingMessagesTimestamps(lowerBound: currentMessage.id, timestamp: timestamp) + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } @@ -791,12 +923,14 @@ private func sendServiceActionMessage(postbox: Postbox, network: Network, peerId private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer: TelegramSecretChat, state: SecretChatState, operationIndex: Int32, decryptedMessage: BoxedDecryptedMessage, globallyUniqueId: Int64, file: SecretChatOutgoingFile?, asService: Bool, wasDelivered: Bool) -> Signal { let payload = Buffer() var sequenceInfo: SecretChatOperationSequenceInfo? - var maybeKey: SecretChatKey? + var maybeParameters: SecretChatEncryptionParameters? switch state.embeddedState { case .terminated, .handshake: break case .basicLayer: - maybeKey = state.keychain.indefinitelyValidKey() + if let key = state.keychain.indefinitelyValidKey() { + maybeParameters = SecretChatEncryptionParameters(key: key, mode: .v1) + } case let .sequenceBasedLayer(sequenceState): let topReceivedOperationIndex: Int32 if let topProcessedCanonicalIncomingOperationIndex = sequenceState.topProcessedCanonicalIncomingOperationIndex { @@ -805,18 +939,27 @@ private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer: topReceivedOperationIndex = -1 } let canonicalOperationIndex = sequenceState.canonicalOutgoingOperationIndex(operationIndex) - maybeKey = state.keychain.latestKey(validForSequenceBasedCanonicalIndex: canonicalOperationIndex) - Logger.shared.log("SecretChat", "sending message with index \(canonicalOperationIndex) key \(String(describing: maybeKey?.fingerprint))") + if let key = state.keychain.latestKey(validForSequenceBasedCanonicalIndex: canonicalOperationIndex) { + let mode: SecretChatEncryptionMode + switch sequenceState.layerNegotiationState.activeLayer { + case .layer73: + mode = .v2(role: state.role) + default: + mode = .v1 + } + maybeParameters = SecretChatEncryptionParameters(key: key, mode: .v1) + } + Logger.shared.log("SecretChat", "sending message with index \(canonicalOperationIndex) key \(String(describing: maybeParameters?.key.fingerprint))") sequenceInfo = SecretChatOperationSequenceInfo(topReceivedOperationIndex: topReceivedOperationIndex, operationIndex: canonicalOperationIndex) } - guard let key = maybeKey else { + guard let parameters = maybeParameters else { Logger.shared.log("SecretChat", "no valid key found") return .single(nil) } decryptedMessage.serialize(payload, role: state.role, sequenceInfo: sequenceInfo) - let encryptedPayload = encryptedMessageContents(key: key, data: MemoryBuffer(payload)) + let encryptedPayload = encryptedMessageContents(parameters: parameters, data: MemoryBuffer(payload)) let sendMessage: Signal let inputPeer = Api.InputEncryptedChat.inputEncryptedChat(chatId: peer.id.id, accessHash: peer.accessHash) diff --git a/TelegramCore/MarkMessageContentAsConsumedInteractively.swift b/TelegramCore/MarkMessageContentAsConsumedInteractively.swift index 8807fb7a12..e5a755d714 100644 --- a/TelegramCore/MarkMessageContentAsConsumedInteractively.swift +++ b/TelegramCore/MarkMessageContentAsConsumedInteractively.swift @@ -46,7 +46,7 @@ public func markMessageContentAsConsumedInteractively(postbox: Postbox, messageI case .basicLayer: layer = .layer8 case let .sequenceBasedLayer(sequenceState): - layer = SecretChatLayer(rawValue: sequenceState.layerNegotiationState.activeLayer) + layer = sequenceState.layerNegotiationState.activeLayer.secretChatLayer } } diff --git a/TelegramCore/PendingMessageManager.swift b/TelegramCore/PendingMessageManager.swift index 272ca0111d..5be3856c76 100644 --- a/TelegramCore/PendingMessageManager.swift +++ b/TelegramCore/PendingMessageManager.swift @@ -509,9 +509,11 @@ public final class PendingMessageManager { messages.sort { MessageIndex($0.0) < MessageIndex($1.0) } if peerId.namespace == Namespaces.Peer.SecretChat { - //assertionFailure() + for (message, content) in messages { + PendingMessageManager.sendSecretMessageContent(modifier: modifier, message: message, content: content) + } - return failMessages(postbox: postbox, ids: group.map { $0.0 }) + return .complete() } else if let peer = modifier.getPeer(peerId), let inputPeer = apiInputPeer(peer) { var isForward = false var replyMessageId: Int32? @@ -637,68 +639,41 @@ public final class PendingMessageManager { } |> switchToLatest } - private func sendMessageContent(network: Network, postbox: Postbox, stateManager: AccountStateManager, messageId: MessageId, content: PendingMessageUploadedContent) -> Signal { - return postbox.modify { [weak self] modifier -> Signal in - guard let message = modifier.getMessage(messageId) else { - return .complete() + private static func sendSecretMessageContent(modifier: Modifier, message: Message, content: PendingMessageUploadedContent) { + var secretFile: SecretChatOutgoingFile? + switch content { + case let .secretMedia(file, size, key): + if let fileReference = SecretChatOutgoingFileReference(file) { + secretFile = SecretChatOutgoingFile(reference: fileReference, size: size, key: key) + } + default: + break + } + + var layer: SecretChatLayer? + let state = modifier.getPeerChatState(message.id.peerId) as? SecretChatState + if let state = state { + switch state.embeddedState { + case .terminated, .handshake: + break + case .basicLayer: + layer = .layer8 + case let .sequenceBasedLayer(sequenceState): + layer = sequenceState.layerNegotiationState.activeLayer.secretChatLayer } - - if messageId.peerId.namespace == Namespaces.Peer.SecretChat { - var secretFile: SecretChatOutgoingFile? - switch content { - case let .secretMedia(file, size, key): - if let fileReference = SecretChatOutgoingFileReference(file) { - secretFile = SecretChatOutgoingFile(reference: fileReference, size: size, key: key) - } - default: - break - } - - var layer: SecretChatLayer? - let state = modifier.getPeerChatState(messageId.peerId) as? SecretChatState - if let state = state { - switch state.embeddedState { - case .terminated, .handshake: - break - case .basicLayer: - layer = .layer8 - case let .sequenceBasedLayer(sequenceState): - layer = SecretChatLayer(rawValue: sequenceState.layerNegotiationState.activeLayer) - } - } - - if let state = state, let layer = layer { - var sentAsAction = false - for media in message.media { - if let media = media as? TelegramMediaAction { - if case let .messageAutoremoveTimeoutUpdated(value) = media.action { - sentAsAction = true - let updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: message.id.peerId, operation: .setMessageAutoremoveTimeout(layer: layer, actionGloballyUniqueId: message.globallyUniqueId!, timeout: value, messageId: message.id), state: state) - if updatedState != state { - modifier.setPeerChatState(message.id.peerId, state: updatedState) - } - modifier.updateMessage(message.id, update: { currentMessage in - var flags = StoreMessageFlags(message.flags) - if !flags.contains(.Failed) { - flags.insert(.Sending) - } - var storeForwardInfo: StoreMessageForwardInfo? - if let forwardInfo = currentMessage.forwardInfo { - storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) - } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) - }) - } - break - } - } - - if !sentAsAction { - let updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: messageId.peerId, operation: .sendMessage(layer: layer, id: messageId, file: secretFile), state: state) + } + + if let state = state, let layer = layer { + var sentAsAction = false + for media in message.media { + if let media = media as? TelegramMediaAction { + if case let .messageAutoremoveTimeoutUpdated(value) = media.action { + sentAsAction = true + let updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: message.id.peerId, operation: .setMessageAutoremoveTimeout(layer: layer, actionGloballyUniqueId: message.globallyUniqueId!, timeout: value, messageId: message.id), state: state) if updatedState != state { - modifier.setPeerChatState(messageId.peerId, state: updatedState) + modifier.setPeerChatState(message.id.peerId, state: updatedState) } - modifier.updateMessage(messageId, update: { currentMessage in + modifier.updateMessage(message.id, update: { currentMessage in var flags = StoreMessageFlags(message.flags) if !flags.contains(.Failed) { flags.insert(.Sending) @@ -710,15 +685,46 @@ public final class PendingMessageManager { return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) } - } else { - modifier.updateMessage(messageId, update: { currentMessage in - var storeForwardInfo: StoreMessageForwardInfo? - if let forwardInfo = currentMessage.forwardInfo { - storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) - } - return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) - }) + break } + } + + if !sentAsAction { + let updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: message.id.peerId, operation: .sendMessage(layer: layer, id: message.id, file: secretFile), state: state) + if updatedState != state { + modifier.setPeerChatState(message.id.peerId, state: updatedState) + } + modifier.updateMessage(message.id, update: { currentMessage in + var flags = StoreMessageFlags(message.flags) + if !flags.contains(.Failed) { + flags.insert(.Sending) + } + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) + } + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + }) + } + } else { + modifier.updateMessage(message.id, update: { currentMessage in + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) + } + return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + }) + } + } + + private func sendMessageContent(network: Network, postbox: Postbox, stateManager: AccountStateManager, messageId: MessageId, content: PendingMessageUploadedContent) -> Signal { + return postbox.modify { [weak self] modifier -> Signal in + guard let message = modifier.getMessage(messageId) else { + return .complete() + } + + if messageId.peerId.namespace == Namespaces.Peer.SecretChat { + PendingMessageManager.sendSecretMessageContent(modifier: modifier, message: message, content: content) return .complete() } else if let peer = modifier.getPeer(messageId.peerId), let inputPeer = apiInputPeer(peer) { var uniqueId: Int64 = 0 diff --git a/TelegramCore/PendingMessageUploadedContent.swift b/TelegramCore/PendingMessageUploadedContent.swift index 2b45a60ee1..5fe66e32d2 100644 --- a/TelegramCore/PendingMessageUploadedContent.swift +++ b/TelegramCore/PendingMessageUploadedContent.swift @@ -105,7 +105,11 @@ private enum PredownloadedResource { case none } -private func maybePredownloadedImageResource(postbox: Postbox, resource: MediaResource) -> Signal { +private func maybePredownloadedImageResource(postbox: Postbox, peerId: PeerId, resource: MediaResource) -> Signal { + if peerId.namespace == Namespaces.Peer.SecretChat { + return .single(.none) + } + return Signal, PendingMessageUploadError> { subscriber in let data = postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)).start(next: { data in if data.complete { @@ -154,7 +158,11 @@ private func maybePredownloadedImageResource(postbox: Postbox, resource: MediaRe } |> switchToLatest } -private func maybePredownloadedFileResource(postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, resource: MediaResource) -> Signal { +private func maybePredownloadedFileResource(postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, peerId: PeerId, resource: MediaResource) -> Signal { + if peerId.namespace == Namespaces.Peer.SecretChat { + return .single(.none) + } + return auxiliaryMethods.fetchResourceMediaReferenceHash(resource) |> mapToSignal { hash -> Signal in if let hash = hash { @@ -185,7 +193,7 @@ private func maybeCacheUploadedResource(postbox: Postbox, key: CachedSentMediaRe private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerId: PeerId, image: TelegramMediaImage, text: String, autoremoveAttribute: AutoremoveTimeoutMessageAttribute?) -> Signal { if let largestRepresentation = largestImageRepresentation(image.representations) { - let predownloadedResource: Signal = maybePredownloadedImageResource(postbox: postbox, resource: largestRepresentation.resource) + let predownloadedResource: Signal = maybePredownloadedImageResource(postbox: postbox, peerId: peerId, resource: largestRepresentation.resource) return predownloadedResource |> mapToSignal { result -> Signal in var referenceKey: CachedSentMediaReferenceKey? @@ -359,7 +367,7 @@ public func statsCategoryForFileWithAttributes(_ attributes: [TelegramMediaFileA } private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, peerId: PeerId, messageId: MessageId?, text: String, attributes: [MessageAttribute], file: TelegramMediaFile) -> Signal { - return maybePredownloadedFileResource(postbox: postbox, auxiliaryMethods: auxiliaryMethods, resource: file.resource) |> mapToSignal { result -> Signal in + return maybePredownloadedFileResource(postbox: postbox, auxiliaryMethods: auxiliaryMethods, peerId: peerId, resource: file.resource) |> mapToSignal { result -> Signal in var referenceKey: CachedSentMediaReferenceKey? switch result { case let .media(media): diff --git a/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift b/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift index 1fd912cd6d..f758131336 100644 --- a/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift @@ -45,6 +45,10 @@ private func parsedServiceAction(_ operation: SecretChatIncomingDecryptedOperati if let parsedObject = SecretApi46.parse(Buffer(bufferNoCopy: operation.contents)), let apiMessage = parsedObject as? SecretApi46.DecryptedMessage { return SecretChatServiceAction(apiMessage) } + case .layer73: + if let parsedObject = SecretApi73.parse(Buffer(bufferNoCopy: operation.contents)), let apiMessage = parsedObject as? SecretApi73.DecryptedMessage { + return SecretChatServiceAction(apiMessage) + } } return nil } @@ -134,6 +138,18 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, modifier: } else { throw MessageParsingError.contentParsingError } + case .layer73: + if let parsedObject = SecretApi73.parse(Buffer(bufferNoCopy: operation.contents)), let apiMessage = parsedObject as? SecretApi73.DecryptedMessage { + if let (parsedMessage, parsedResources) = parseMessage(peerId: peerId, authorId: updatedPeer.regularPeerId, tagLocalIndex: entry.tagLocalIndex, timestamp: operation.timestamp, apiMessage: apiMessage, file: operation.file, messageIdForGloballyUniqueMessageId: { id in + return modifier.messageIdForGloballyUniqueMessageId(peerId: peerId, id: id) + }) { + message = parsedMessage + resources = parsedResources + } + serviceAction = SecretChatServiceAction(apiMessage) + } else { + throw MessageParsingError.contentParsingError + } } switch updatedState.embeddedState { @@ -195,10 +211,14 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, modifier: case .handshake: throw MessageParsingError.invalidChatState case .basicLayer: - if layerSupport >= 46 { - let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: 46, locallyRequestedLayer: 46, remotelyRequestedLayer: layerSupport), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: modifier.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil) + if layerSupport >= 73 { + let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: .layer73, locallyRequestedLayer: 73, remotelyRequestedLayer: layerSupport), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: modifier.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil) updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState)) - updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: .reportLayerSupport(layer: .layer46, actionGloballyUniqueId: arc4random64(), layerSupport: 46), state: updatedState) + updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: .reportLayerSupport(layer: .layer73, actionGloballyUniqueId: arc4random64(), layerSupport: 73), state: updatedState) + } else if layerSupport >= 46 { + let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: .layer46, locallyRequestedLayer: 46, remotelyRequestedLayer: layerSupport), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: modifier.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil) + updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState)) + updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: .reportLayerSupport(layer: .layer46, actionGloballyUniqueId: arc4random64(), layerSupport: 73), state: updatedState) } else { throw MessageParsingError.contentParsingError } @@ -206,7 +226,8 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, modifier: if sequenceState.layerNegotiationState.remotelyRequestedLayer != layerSupport { let updatedNegotiationState = sequenceState.layerNegotiationState.withUpdatedRemotelyRequestedLayer(layerSupport) updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedLayerNegotiationState(updatedNegotiationState))) - updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: .reportLayerSupport(layer: .layer46, actionGloballyUniqueId: arc4random64(), layerSupport: 46), state: updatedState) + + updatedState = secretChatCheckLayerNegotiationIfNeeded(modifier: modifier, peerId: peerId, state: updatedState) } } case let .setMessageAutoremoveTimeout(timeout): @@ -378,6 +399,42 @@ extension SecretChatServiceAction { } } +extension SecretChatServiceAction { + init?(_ apiMessage: SecretApi73.DecryptedMessage) { + switch apiMessage { + case .decryptedMessage: + return nil + case let .decryptedMessageService(_, action): + switch action { + case let .decryptedMessageActionDeleteMessages(randomIds): + self = .deleteMessages(globallyUniqueIds: randomIds) + case .decryptedMessageActionFlushHistory: + self = .clearHistory + case let .decryptedMessageActionNotifyLayer(layer): + self = .reportLayerSupport(layer) + case let .decryptedMessageActionReadMessages(randomIds): + self = .markMessagesContentAsConsumed(globallyUniqueIds: randomIds) + case .decryptedMessageActionScreenshotMessages: + return nil + case let .decryptedMessageActionSetMessageTTL(ttlSeconds): + self = .setMessageAutoremoveTimeout(ttlSeconds) + case let .decryptedMessageActionResend(startSeqNo, endSeqNo): + self = .resendOperations(fromSeq: startSeqNo, toSeq: endSeqNo) + case let .decryptedMessageActionRequestKey(exchangeId, gA): + self = .rekeyAction(.pfsRequestKey(rekeySessionId: exchangeId, gA: MemoryBuffer(gA))) + case let .decryptedMessageActionAcceptKey(exchangeId, gB, keyFingerprint): + self = .rekeyAction(.pfsAcceptKey(rekeySessionId: exchangeId, gB: MemoryBuffer(gB), keyFingerprint: keyFingerprint)) + case let .decryptedMessageActionCommitKey(exchangeId, keyFingerprint): + self = .rekeyAction(.pfsCommitKey(rekeySessionId: exchangeId, keyFingerprint: keyFingerprint)) + case let .decryptedMessageActionAbortKey(exchangeId): + self = .rekeyAction(.pfsAbortSession(rekeySessionId: exchangeId)) + case .decryptedMessageActionNoop: + return nil + } + } + } +} + extension StoreMessage { convenience init?(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi8.DecryptedMessage, file: SecretChatFileReference?) { switch apiMessage { @@ -437,6 +494,42 @@ extension TelegramMediaFileAttribute { } } +extension TelegramMediaFileAttribute { + init?(_ apiAttribute: SecretApi73.DocumentAttribute) { + switch apiAttribute { + case .documentAttributeAnimated: + self = .Animated + case let .documentAttributeAudio(flags, duration, title, performer, waveform): + let isVoice = (flags & (1 << 10)) != 0 + var waveformBuffer: MemoryBuffer? + if let waveform = waveform { + let memory = malloc(waveform.size)! + memcpy(memory, waveform.data, waveform.size) + waveformBuffer = MemoryBuffer(memory: memory, capacity: waveform.size, length: waveform.size, freeWhenDone: true) + } + self = .Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: performer, waveform: waveformBuffer) + case let .documentAttributeFilename(fileName): + self = .FileName(fileName: fileName) + case let .documentAttributeImageSize(w, h): + self = .ImageSize(size: CGSize(width: CGFloat(w), height: CGFloat(h))) + case let .documentAttributeSticker(alt, stickerset): + let packReference: StickerPackReference? + switch stickerset { + case .inputStickerSetEmpty: + packReference = nil + case let .inputStickerSetShortName(shortName): + packReference = .name(shortName) + } + self = .Sticker(displayText: alt, packReference: packReference, maskData: nil) + case let .documentAttributeVideo(flags, duration, w, h): + var videoFlags: TelegramMediaVideoFlags = [] + if (flags & (1 << 0)) != 0 { + videoFlags.insert(.instantRoundVideo) + } + self = .Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)), flags: videoFlags) + } + } +} private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi46.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? { switch apiMessage { @@ -564,3 +657,144 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } } } + +private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi73.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? { + switch apiMessage { + case let .decryptedMessage(flags, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId): + var text = message + var parsedMedia: [Media] = [] + var attributes: [MessageAttribute] = [] + var resources: [(MediaResource, Data)] = [] + + if ttl > 0 { + attributes.append(AutoremoveTimeoutMessageAttribute(timeout: ttl, countdownBeginTime: nil)) + } + + if let media = media { + switch media { + case let .decryptedMessageMediaPhoto(thumb, thumbW, thumbH, w, h, size, key, iv, caption): + if !caption.isEmpty { + text = caption + } + if let file = file { + var representations: [TelegramMediaImageRepresentation] = [] + if thumb.size != 0 { + let resource = LocalFileMediaResource(fileId: arc4random64()) + representations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(thumbW), height: CGFloat(thumbH)), resource: resource)) + resources.append((resource, thumb.makeData())) + } + representations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size))) + let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, reference: nil) + parsedMedia.append(image) + } + case let .decryptedMessageMediaAudio(duration, mimeType, size, key, iv): + if let file = file { + let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: [TelegramMediaFileAttribute.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)]) + parsedMedia.append(fileMedia) + } + case let .decryptedMessageMediaDocument(thumb, thumbW, thumbH, mimeType, size, key, iv, attributes, caption): + if !caption.isEmpty { + text = caption + } + if let file = file { + var parsedAttributes: [TelegramMediaFileAttribute] = [] + for attribute in attributes { + if let parsedAttribute = TelegramMediaFileAttribute(attribute) { + parsedAttributes.append(parsedAttribute) + } + } + var previewRepresentations: [TelegramMediaImageRepresentation] = [] + if thumb.size != 0 { + let resource = LocalFileMediaResource(fileId: arc4random64()) + previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(thumbW), height: CGFloat(thumbH)), resource: resource)) + resources.append((resource, thumb.makeData())) + } + let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes) + parsedMedia.append(fileMedia) + } + case let .decryptedMessageMediaVideo(thumb, thumbW, thumbH, duration, mimeType, w, h, size, key, iv, caption): + if !caption.isEmpty { + text = caption + } + if let file = file { + var parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)), flags: []), .FileName(fileName: "video.mov")] + var previewRepresentations: [TelegramMediaImageRepresentation] = [] + if thumb.size != 0 { + let resource = LocalFileMediaResource(fileId: arc4random64()) + previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(thumbW), height: CGFloat(thumbH)), resource: resource)) + resources.append((resource, thumb.makeData())) + } + let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes) + parsedMedia.append(fileMedia) + } + case let .decryptedMessageMediaExternalDocument(id, accessHash, date, mimeType, size, thumb, dcId, attributes): + var parsedAttributes: [TelegramMediaFileAttribute] = [] + for attribute in attributes { + if let parsedAttribute = TelegramMediaFileAttribute(attribute) { + parsedAttributes.append(parsedAttribute) + } + } + let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size)), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: parsedAttributes) + parsedMedia.append(fileMedia) + default: + break + } + } + + var groupingKey: Int64? + if let groupedId = groupedId { + inner: for media in parsedMedia { + if let _ = media as? TelegramMediaImage { + groupingKey = groupedId + break inner + } else if let _ = media as? TelegramMediaFile { + groupingKey = groupedId + break inner + } + } + } + + if let replyToRandomId = replyToRandomId, let replyMessageId = messageIdForGloballyUniqueMessageId(replyToRandomId) { + attributes.append(ReplyMessageAttribute(messageId: replyMessageId)) + } + + var entitiesAttribute: TextEntitiesMessageAttribute? + for attribute in attributes { + if let attribute = attribute as? TextEntitiesMessageAttribute { + entitiesAttribute = attribute + break + } + } + + let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities) + + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) + case let .decryptedMessageService(randomId, action): + switch action { + case let .decryptedMessageActionDeleteMessages(randomIds): + return nil + case .decryptedMessageActionFlushHistory: + return nil + case .decryptedMessageActionNotifyLayer: + return nil + case .decryptedMessageActionReadMessages: + return nil + case .decryptedMessageActionScreenshotMessages: + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), []) + case let .decryptedMessageActionSetMessageTTL(ttlSeconds): + return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(ttlSeconds))]), []) + case .decryptedMessageActionResend: + return nil + case .decryptedMessageActionRequestKey: + return nil + case .decryptedMessageActionAcceptKey: + return nil + case .decryptedMessageActionCommitKey: + return nil + case .decryptedMessageActionAbortKey: + return nil + case .decryptedMessageActionNoop: + return nil + } + } +} diff --git a/TelegramCore/ProcessSecretChatIncomingEncryptedOperations.swift b/TelegramCore/ProcessSecretChatIncomingEncryptedOperations.swift index b8206980af..78ed075127 100644 --- a/TelegramCore/ProcessSecretChatIncomingEncryptedOperations.swift +++ b/TelegramCore/ProcessSecretChatIncomingEncryptedOperations.swift @@ -19,114 +19,117 @@ func processSecretChatIncomingEncryptedOperations(modifier: Modifier, peerId: Pe modifier.operationLogEnumerateEntries(peerId: peerId, tag: OperationLogTags.SecretIncomingEncrypted, { entry in if let operation = entry.contents as? SecretChatIncomingEncryptedOperation { if let key = updatedState.keychain.key(fingerprint: operation.keyFingerprint) { - withDecryptedMessageContents(key: key, data: operation.contents, { decryptedContents in - if let decryptedContents = decryptedContents { - withExtendedLifetime(decryptedContents, { - let buffer = BufferReader(Buffer(bufferNoCopy: decryptedContents)) + var decryptedContents = withDecryptedMessageContents(parameters: SecretChatEncryptionParameters(key: key, mode: .v2(role: updatedState.role)), data: operation.contents) + if decryptedContents == nil { + decryptedContents = withDecryptedMessageContents(parameters: SecretChatEncryptionParameters(key: key, mode: .v1), data: operation.contents) + } + if let decryptedContents = decryptedContents { + withExtendedLifetime(decryptedContents, { + let buffer = BufferReader(Buffer(bufferNoCopy: decryptedContents)) + + do { + guard let topLevelSignature = buffer.readInt32() else { + throw MessagePreParsingError.malformedData + } + let parsedLayer: Int32 + let sequenceInfo: SecretChatOperationSequenceInfo? - do { - guard let topLevelSignature = buffer.readInt32() else { - throw MessagePreParsingError.malformedData - } - let parsedLayer: Int32 - let sequenceInfo: SecretChatOperationSequenceInfo? - - if topLevelSignature == 0x1be31789 { - guard let _ = parseBytes(buffer) else { - throw MessagePreParsingError.malformedData - } - - guard let layerValue = buffer.readInt32() else { - throw MessagePreParsingError.malformedData - } - - guard let seqInValue = buffer.readInt32() else { - throw MessagePreParsingError.malformedData - } - - guard let seqOutValue = buffer.readInt32() else { - throw MessagePreParsingError.malformedData - } - - switch updatedState.role { - case .creator: - if seqOutValue < 0 || (seqInValue >= 0 && (seqInValue & 1) == 0) || (seqOutValue & 1) != 0 { - throw MessagePreParsingError.protocolViolation - } - case .participant: - if seqOutValue < 0 || (seqInValue >= 0 && (seqInValue & 1) != 0) || (seqOutValue & 1) == 0 { - throw MessagePreParsingError.protocolViolation - } - } - - sequenceInfo = SecretChatOperationSequenceInfo(topReceivedOperationIndex: seqInValue / 2, operationIndex: seqOutValue / 2) - - if layerValue == 17 { - parsedLayer = 46 - } else { - parsedLayer = layerValue - } - } else { - parsedLayer = 8 - sequenceInfo = nil - buffer.reset() - } - - guard let messageContents = buffer.readBuffer(decryptedContents.length - Int(buffer.offset)) else { + if topLevelSignature == 0x1be31789 { + guard let _ = parseBytes(buffer) else { throw MessagePreParsingError.malformedData } - let entryTagLocalIndex: StorePeerOperationLogEntryTagLocalIndex + guard let layerValue = buffer.readInt32() else { + throw MessagePreParsingError.malformedData + } - switch updatedState.embeddedState { - case .terminated: - throw MessagePreParsingError.invalidChatState - case .handshake: - throw MessagePreParsingError.invalidChatState - case .basicLayer: - if parsedLayer >= 46 { - guard let sequenceInfo = sequenceInfo else { - throw MessagePreParsingError.protocolViolation - } - let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: parsedLayer, locallyRequestedLayer: nil, remotelyRequestedLayer: nil), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: modifier.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil) - updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState)) - modifier.setPeerChatState(peerId, state: updatedState) - entryTagLocalIndex = .manual(sequenceBasedLayerState.baseIncomingOperationIndex + sequenceInfo.operationIndex) - } else { - if parsedLayer != 8 && parsedLayer != 17 { - throw MessagePreParsingError.protocolViolation - } - entryTagLocalIndex = .automatic - } - case let .sequenceBasedLayer(sequenceState): - if parsedLayer < 46 { + guard let seqInValue = buffer.readInt32() else { + throw MessagePreParsingError.malformedData + } + + guard let seqOutValue = buffer.readInt32() else { + throw MessagePreParsingError.malformedData + } + + switch updatedState.role { + case .creator: + if seqOutValue < 0 || (seqInValue >= 0 && (seqInValue & 1) == 0) || (seqOutValue & 1) != 0 { + throw MessagePreParsingError.protocolViolation + } + case .participant: + if seqOutValue < 0 || (seqInValue >= 0 && (seqInValue & 1) != 0) || (seqOutValue & 1) == 0 { throw MessagePreParsingError.protocolViolation } - - entryTagLocalIndex = .manual(sequenceState.baseIncomingOperationIndex + sequenceInfo!.operationIndex) } - modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretIncomingDecrypted, tagLocalIndex: entryTagLocalIndex, tagMergedIndex: .none, contents: SecretChatIncomingDecryptedOperation(timestamp: operation.timestamp, layer: parsedLayer, sequenceInfo: sequenceInfo, contents: MemoryBuffer(messageContents), file: operation.mediaFileReference)) - addedDecryptedOperations = true - } catch let error { - if let error = error as? MessagePreParsingError { - switch error { - case .invalidChatState: - break - case .malformedData, .protocolViolation: - break - } + sequenceInfo = SecretChatOperationSequenceInfo(topReceivedOperationIndex: seqInValue / 2, operationIndex: seqOutValue / 2) + + if layerValue == 17 { + parsedLayer = 46 + } else { + parsedLayer = layerValue } - Logger.shared.log("SecretChat", "peerId \(peerId) malformed data after decryption") + } else { + parsedLayer = 8 + sequenceInfo = nil + buffer.reset() } - removeTagLocalIndices.append(entry.tagLocalIndex) - }) - } else { - Logger.shared.log("SecretChat", "peerId \(peerId) couldn't decrypt message content") + guard let messageContents = buffer.readBuffer(decryptedContents.length - Int(buffer.offset)) else { + throw MessagePreParsingError.malformedData + } + + let entryTagLocalIndex: StorePeerOperationLogEntryTagLocalIndex + + switch updatedState.embeddedState { + case .terminated: + throw MessagePreParsingError.invalidChatState + case .handshake: + throw MessagePreParsingError.invalidChatState + case .basicLayer: + if parsedLayer >= 46 { + guard let sequenceInfo = sequenceInfo else { + throw MessagePreParsingError.protocolViolation + } + + let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: secretChatCommonSupportedLayer(remoteLayer: parsedLayer), locallyRequestedLayer: nil, remotelyRequestedLayer: nil), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: modifier.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil) + updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState)) + modifier.setPeerChatState(peerId, state: updatedState) + entryTagLocalIndex = .manual(sequenceBasedLayerState.baseIncomingOperationIndex + sequenceInfo.operationIndex) + } else { + if parsedLayer != 8 && parsedLayer != 17 { + throw MessagePreParsingError.protocolViolation + } + entryTagLocalIndex = .automatic + } + case let .sequenceBasedLayer(sequenceState): + if parsedLayer < 46 { + throw MessagePreParsingError.protocolViolation + } + + entryTagLocalIndex = .manual(sequenceState.baseIncomingOperationIndex + sequenceInfo!.operationIndex) + } + + modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretIncomingDecrypted, tagLocalIndex: entryTagLocalIndex, tagMergedIndex: .none, contents: SecretChatIncomingDecryptedOperation(timestamp: operation.timestamp, layer: parsedLayer, sequenceInfo: sequenceInfo, contents: MemoryBuffer(messageContents), file: operation.mediaFileReference)) + addedDecryptedOperations = true + } catch let error { + if let error = error as? MessagePreParsingError { + switch error { + case .invalidChatState: + break + case .malformedData, .protocolViolation: + break + } + } + Logger.shared.log("SecretChat", "peerId \(peerId) malformed data after decryption") + } + removeTagLocalIndices.append(entry.tagLocalIndex) - } - }) + }) + } else { + Logger.shared.log("SecretChat", "peerId \(peerId) couldn't decrypt message content") + removeTagLocalIndices.append(entry.tagLocalIndex) + } } else { Logger.shared.log("SecretChat", "peerId \(peerId) key \(operation.keyFingerprint) doesn't exist") } diff --git a/TelegramCore/SecretApiLayer73.swift b/TelegramCore/SecretApiLayer73.swift new file mode 100644 index 0000000000..2c887b7739 --- /dev/null +++ b/TelegramCore/SecretApiLayer73.swift @@ -0,0 +1,1524 @@ + +fileprivate final class FunctionDescription: CustomStringConvertible { + let generator: () -> String + init(_ generator: @escaping () -> String) { + self.generator = generator + } + + var description: String { + return self.generator() + } +} + +fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { + var dict: [Int32 : (BufferReader) -> Any?] = [:] + dict[-1471112230] = { return $0.readInt32() } + dict[570911930] = { return $0.readInt64() } + dict[571523412] = { return $0.readDouble() } + dict[-1255641564] = { return parseString($0) } + dict[-1586283796] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionSetMessageTTL($0) } + dict[206520510] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionReadMessages($0) } + dict[1700872964] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionDeleteMessages($0) } + dict[-1967000459] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionScreenshotMessages($0) } + dict[1729750108] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionFlushHistory($0) } + dict[-217806717] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionNotifyLayer($0) } + dict[1360072880] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionResend($0) } + dict[-204906213] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionRequestKey($0) } + dict[1877046107] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionAcceptKey($0) } + dict[-586814357] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionAbortKey($0) } + dict[-332526693] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionCommitKey($0) } + dict[-1473258141] = { return SecretApi73.DecryptedMessageAction.parse_decryptedMessageActionNoop($0) } + dict[236446268] = { return SecretApi73.PhotoSize.parse_photoSizeEmpty($0) } + dict[2009052699] = { return SecretApi73.PhotoSize.parse_photoSize($0) } + dict[-374917894] = { return SecretApi73.PhotoSize.parse_photoCachedSize($0) } + dict[2086234950] = { return SecretApi73.FileLocation.parse_fileLocationUnavailable($0) } + dict[1406570614] = { return SecretApi73.FileLocation.parse_fileLocation($0) } + dict[467867529] = { return SecretApi73.DecryptedMessageLayer.parse_decryptedMessageLayer($0) } + dict[1930838368] = { return SecretApi73.DecryptedMessage.parse_decryptedMessageService($0) } + dict[-1848883596] = { return SecretApi73.DecryptedMessage.parse_decryptedMessage($0) } + dict[1815593308] = { return SecretApi73.DocumentAttribute.parse_documentAttributeImageSize($0) } + dict[297109817] = { return SecretApi73.DocumentAttribute.parse_documentAttributeAnimated($0) } + dict[358154344] = { return SecretApi73.DocumentAttribute.parse_documentAttributeFilename($0) } + dict[978674434] = { return SecretApi73.DocumentAttribute.parse_documentAttributeSticker($0) } + dict[-1739392570] = { return SecretApi73.DocumentAttribute.parse_documentAttributeAudio($0) } + dict[250621158] = { return SecretApi73.DocumentAttribute.parse_documentAttributeVideo($0) } + dict[-2044933984] = { return SecretApi73.InputStickerSet.parse_inputStickerSetShortName($0) } + dict[-4838507] = { return SecretApi73.InputStickerSet.parse_inputStickerSetEmpty($0) } + dict[-1148011883] = { return SecretApi73.MessageEntity.parse_messageEntityUnknown($0) } + dict[-100378723] = { return SecretApi73.MessageEntity.parse_messageEntityMention($0) } + dict[1868782349] = { return SecretApi73.MessageEntity.parse_messageEntityHashtag($0) } + dict[1827637959] = { return SecretApi73.MessageEntity.parse_messageEntityBotCommand($0) } + dict[1859134776] = { return SecretApi73.MessageEntity.parse_messageEntityUrl($0) } + dict[1692693954] = { return SecretApi73.MessageEntity.parse_messageEntityEmail($0) } + dict[-1117713463] = { return SecretApi73.MessageEntity.parse_messageEntityBold($0) } + dict[-2106619040] = { return SecretApi73.MessageEntity.parse_messageEntityItalic($0) } + dict[681706865] = { return SecretApi73.MessageEntity.parse_messageEntityCode($0) } + dict[1938967520] = { return SecretApi73.MessageEntity.parse_messageEntityPre($0) } + dict[1990644519] = { return SecretApi73.MessageEntity.parse_messageEntityTextUrl($0) } + dict[144661578] = { return SecretApi73.DecryptedMessageMedia.parse_decryptedMessageMediaEmpty($0) } + dict[893913689] = { return SecretApi73.DecryptedMessageMedia.parse_decryptedMessageMediaGeoPoint($0) } + dict[1485441687] = { return SecretApi73.DecryptedMessageMedia.parse_decryptedMessageMediaContact($0) } + dict[1474341323] = { return SecretApi73.DecryptedMessageMedia.parse_decryptedMessageMediaAudio($0) } + dict[-90853155] = { return SecretApi73.DecryptedMessageMedia.parse_decryptedMessageMediaExternalDocument($0) } + dict[-235238024] = { return SecretApi73.DecryptedMessageMedia.parse_decryptedMessageMediaPhoto($0) } + dict[2063502050] = { return SecretApi73.DecryptedMessageMedia.parse_decryptedMessageMediaDocument($0) } + dict[-1760785394] = { return SecretApi73.DecryptedMessageMedia.parse_decryptedMessageMediaVideo($0) } + dict[-1978796689] = { return SecretApi73.DecryptedMessageMedia.parse_decryptedMessageMediaVenue($0) } + dict[-452652584] = { return SecretApi73.DecryptedMessageMedia.parse_decryptedMessageMediaWebPage($0) } + return dict +}() + +public struct SecretApi73 { + public static func parse(_ buffer: Buffer) -> Any? { + let reader = BufferReader(buffer) + if let signature = reader.readInt32() { + return parse(reader, signature: signature) + } + return nil + } + + fileprivate static func parse(_ reader: BufferReader, signature: Int32) -> Any? { + if let parser = parsers[signature] { + return parser(reader) + } + else { + Logger.shared.log("TL", "Type constructor \(String(signature, radix: 16, uppercase: false)) not found") + return nil + } + } + + fileprivate static func parseVector(_ reader: BufferReader, elementSignature: Int32, elementType: T.Type) -> [T]? { + if let count = reader.readInt32() { + var array = [T]() + var i: Int32 = 0 + while i < count { + var signature = elementSignature + if elementSignature == 0 { + if let unboxedSignature = reader.readInt32() { + signature = unboxedSignature + } + else { + return nil + } + } + if let item = SecretApi73.parse(reader, signature: signature) as? T { + array.append(item) + } + else { + return nil + } + i += 1 + } + return array + } + return nil + } + + public static func serializeObject(_ object: Any, buffer: Buffer, boxed: Swift.Bool) { + switch object { + case let _1 as SecretApi73.DecryptedMessageAction: + _1.serialize(buffer, boxed) + case let _1 as SecretApi73.PhotoSize: + _1.serialize(buffer, boxed) + case let _1 as SecretApi73.FileLocation: + _1.serialize(buffer, boxed) + case let _1 as SecretApi73.DecryptedMessageLayer: + _1.serialize(buffer, boxed) + case let _1 as SecretApi73.DecryptedMessage: + _1.serialize(buffer, boxed) + case let _1 as SecretApi73.DocumentAttribute: + _1.serialize(buffer, boxed) + case let _1 as SecretApi73.InputStickerSet: + _1.serialize(buffer, boxed) + case let _1 as SecretApi73.MessageEntity: + _1.serialize(buffer, boxed) + case let _1 as SecretApi73.DecryptedMessageMedia: + _1.serialize(buffer, boxed) + default: + break + } + } + + public enum DecryptedMessageAction { + case decryptedMessageActionSetMessageTTL(ttlSeconds: Int32) + case decryptedMessageActionReadMessages(randomIds: [Int64]) + case decryptedMessageActionDeleteMessages(randomIds: [Int64]) + case decryptedMessageActionScreenshotMessages(randomIds: [Int64]) + case decryptedMessageActionFlushHistory + case decryptedMessageActionNotifyLayer(layer: Int32) + case decryptedMessageActionResend(startSeqNo: Int32, endSeqNo: Int32) + case decryptedMessageActionRequestKey(exchangeId: Int64, gA: Buffer) + case decryptedMessageActionAcceptKey(exchangeId: Int64, gB: Buffer, keyFingerprint: Int64) + case decryptedMessageActionAbortKey(exchangeId: Int64) + case decryptedMessageActionCommitKey(exchangeId: Int64, keyFingerprint: Int64) + case decryptedMessageActionNoop + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .decryptedMessageActionSetMessageTTL(let ttlSeconds): + if boxed { + buffer.appendInt32(-1586283796) + } + serializeInt32(ttlSeconds, buffer: buffer, boxed: false) + break + case .decryptedMessageActionReadMessages(let randomIds): + if boxed { + buffer.appendInt32(206520510) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(randomIds.count)) + for item in randomIds { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + case .decryptedMessageActionDeleteMessages(let randomIds): + if boxed { + buffer.appendInt32(1700872964) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(randomIds.count)) + for item in randomIds { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + case .decryptedMessageActionScreenshotMessages(let randomIds): + if boxed { + buffer.appendInt32(-1967000459) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(randomIds.count)) + for item in randomIds { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + case .decryptedMessageActionFlushHistory: + if boxed { + buffer.appendInt32(1729750108) + } + + break + case .decryptedMessageActionNotifyLayer(let layer): + if boxed { + buffer.appendInt32(-217806717) + } + serializeInt32(layer, buffer: buffer, boxed: false) + break + case .decryptedMessageActionResend(let startSeqNo, let endSeqNo): + if boxed { + buffer.appendInt32(1360072880) + } + serializeInt32(startSeqNo, buffer: buffer, boxed: false) + serializeInt32(endSeqNo, buffer: buffer, boxed: false) + break + case .decryptedMessageActionRequestKey(let exchangeId, let gA): + if boxed { + buffer.appendInt32(-204906213) + } + serializeInt64(exchangeId, buffer: buffer, boxed: false) + serializeBytes(gA, buffer: buffer, boxed: false) + break + case .decryptedMessageActionAcceptKey(let exchangeId, let gB, let keyFingerprint): + if boxed { + buffer.appendInt32(1877046107) + } + serializeInt64(exchangeId, buffer: buffer, boxed: false) + serializeBytes(gB, buffer: buffer, boxed: false) + serializeInt64(keyFingerprint, buffer: buffer, boxed: false) + break + case .decryptedMessageActionAbortKey(let exchangeId): + if boxed { + buffer.appendInt32(-586814357) + } + serializeInt64(exchangeId, buffer: buffer, boxed: false) + break + case .decryptedMessageActionCommitKey(let exchangeId, let keyFingerprint): + if boxed { + buffer.appendInt32(-332526693) + } + serializeInt64(exchangeId, buffer: buffer, boxed: false) + serializeInt64(keyFingerprint, buffer: buffer, boxed: false) + break + case .decryptedMessageActionNoop: + if boxed { + buffer.appendInt32(-1473258141) + } + + break + } + } + fileprivate static func parse_decryptedMessageActionSetMessageTTL(_ reader: BufferReader) -> DecryptedMessageAction? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionSetMessageTTL(ttlSeconds: _1!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageActionReadMessages(_ reader: BufferReader) -> DecryptedMessageAction? { + var _1: [Int64]? + if let _ = reader.readInt32() { + _1 = SecretApi73.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + let _c1 = _1 != nil + if _c1 { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionReadMessages(randomIds: _1!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageActionDeleteMessages(_ reader: BufferReader) -> DecryptedMessageAction? { + var _1: [Int64]? + if let _ = reader.readInt32() { + _1 = SecretApi73.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + let _c1 = _1 != nil + if _c1 { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionDeleteMessages(randomIds: _1!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageActionScreenshotMessages(_ reader: BufferReader) -> DecryptedMessageAction? { + var _1: [Int64]? + if let _ = reader.readInt32() { + _1 = SecretApi73.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + let _c1 = _1 != nil + if _c1 { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionScreenshotMessages(randomIds: _1!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageActionFlushHistory(_ reader: BufferReader) -> DecryptedMessageAction? { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionFlushHistory + } + fileprivate static func parse_decryptedMessageActionNotifyLayer(_ reader: BufferReader) -> DecryptedMessageAction? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionNotifyLayer(layer: _1!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageActionResend(_ reader: BufferReader) -> DecryptedMessageAction? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionResend(startSeqNo: _1!, endSeqNo: _2!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageActionRequestKey(_ reader: BufferReader) -> DecryptedMessageAction? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Buffer? + _2 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionRequestKey(exchangeId: _1!, gA: _2!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageActionAcceptKey(_ reader: BufferReader) -> DecryptedMessageAction? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Buffer? + _2 = parseBytes(reader) + var _3: Int64? + _3 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionAcceptKey(exchangeId: _1!, gB: _2!, keyFingerprint: _3!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageActionAbortKey(_ reader: BufferReader) -> DecryptedMessageAction? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionAbortKey(exchangeId: _1!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageActionCommitKey(_ reader: BufferReader) -> DecryptedMessageAction? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionCommitKey(exchangeId: _1!, keyFingerprint: _2!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageActionNoop(_ reader: BufferReader) -> DecryptedMessageAction? { + return SecretApi73.DecryptedMessageAction.decryptedMessageActionNoop + } + + + } + + public enum PhotoSize { + case photoSizeEmpty(type: String) + case photoSize(type: String, location: SecretApi73.FileLocation, w: Int32, h: Int32, size: Int32) + case photoCachedSize(type: String, location: SecretApi73.FileLocation, w: Int32, h: Int32, bytes: Buffer) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .photoSizeEmpty(let type): + if boxed { + buffer.appendInt32(236446268) + } + serializeString(type, buffer: buffer, boxed: false) + break + case .photoSize(let type, let location, let w, let h, let size): + if boxed { + buffer.appendInt32(2009052699) + } + serializeString(type, buffer: buffer, boxed: false) + location.serialize(buffer, true) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + break + case .photoCachedSize(let type, let location, let w, let h, let bytes): + if boxed { + buffer.appendInt32(-374917894) + } + serializeString(type, buffer: buffer, boxed: false) + location.serialize(buffer, true) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + serializeBytes(bytes, buffer: buffer, boxed: false) + break + } + } + fileprivate static func parse_photoSizeEmpty(_ reader: BufferReader) -> PhotoSize? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return SecretApi73.PhotoSize.photoSizeEmpty(type: _1!) + } + else { + return nil + } + } + fileprivate static func parse_photoSize(_ reader: BufferReader) -> PhotoSize? { + var _1: String? + _1 = parseString(reader) + var _2: SecretApi73.FileLocation? + if let signature = reader.readInt32() { + _2 = SecretApi73.parse(reader, signature: signature) as? SecretApi73.FileLocation + } + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return SecretApi73.PhotoSize.photoSize(type: _1!, location: _2!, w: _3!, h: _4!, size: _5!) + } + else { + return nil + } + } + fileprivate static func parse_photoCachedSize(_ reader: BufferReader) -> PhotoSize? { + var _1: String? + _1 = parseString(reader) + var _2: SecretApi73.FileLocation? + if let signature = reader.readInt32() { + _2 = SecretApi73.parse(reader, signature: signature) as? SecretApi73.FileLocation + } + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Buffer? + _5 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return SecretApi73.PhotoSize.photoCachedSize(type: _1!, location: _2!, w: _3!, h: _4!, bytes: _5!) + } + else { + return nil + } + } + + + } + + public enum FileLocation { + case fileLocationUnavailable(volumeId: Int64, localId: Int32, secret: Int64) + case fileLocation(dcId: Int32, volumeId: Int64, localId: Int32, secret: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .fileLocationUnavailable(let volumeId, let localId, let secret): + if boxed { + buffer.appendInt32(2086234950) + } + serializeInt64(volumeId, buffer: buffer, boxed: false) + serializeInt32(localId, buffer: buffer, boxed: false) + serializeInt64(secret, buffer: buffer, boxed: false) + break + case .fileLocation(let dcId, let volumeId, let localId, let secret): + if boxed { + buffer.appendInt32(1406570614) + } + serializeInt32(dcId, buffer: buffer, boxed: false) + serializeInt64(volumeId, buffer: buffer, boxed: false) + serializeInt32(localId, buffer: buffer, boxed: false) + serializeInt64(secret, buffer: buffer, boxed: false) + break + } + } + fileprivate static func parse_fileLocationUnavailable(_ reader: BufferReader) -> FileLocation? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return SecretApi73.FileLocation.fileLocationUnavailable(volumeId: _1!, localId: _2!, secret: _3!) + } + else { + return nil + } + } + fileprivate static func parse_fileLocation(_ reader: BufferReader) -> FileLocation? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int64? + _4 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return SecretApi73.FileLocation.fileLocation(dcId: _1!, volumeId: _2!, localId: _3!, secret: _4!) + } + else { + return nil + } + } + + + } + + public enum DecryptedMessageLayer { + case decryptedMessageLayer(randomBytes: Buffer, layer: Int32, inSeqNo: Int32, outSeqNo: Int32, message: SecretApi73.DecryptedMessage) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .decryptedMessageLayer(let randomBytes, let layer, let inSeqNo, let outSeqNo, let message): + if boxed { + buffer.appendInt32(467867529) + } + serializeBytes(randomBytes, buffer: buffer, boxed: false) + serializeInt32(layer, buffer: buffer, boxed: false) + serializeInt32(inSeqNo, buffer: buffer, boxed: false) + serializeInt32(outSeqNo, buffer: buffer, boxed: false) + message.serialize(buffer, true) + break + } + } + fileprivate static func parse_decryptedMessageLayer(_ reader: BufferReader) -> DecryptedMessageLayer? { + var _1: Buffer? + _1 = parseBytes(reader) + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: SecretApi73.DecryptedMessage? + if let signature = reader.readInt32() { + _5 = SecretApi73.parse(reader, signature: signature) as? SecretApi73.DecryptedMessage + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return SecretApi73.DecryptedMessageLayer.decryptedMessageLayer(randomBytes: _1!, layer: _2!, inSeqNo: _3!, outSeqNo: _4!, message: _5!) + } + else { + return nil + } + } + + + } + + public enum DecryptedMessage { + case decryptedMessageService(randomId: Int64, action: SecretApi73.DecryptedMessageAction) + case decryptedMessage(flags: Int32, randomId: Int64, ttl: Int32, message: String, media: SecretApi73.DecryptedMessageMedia?, entities: [SecretApi73.MessageEntity]?, viaBotName: String?, replyToRandomId: Int64?, groupedId: Int64?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .decryptedMessageService(let randomId, let action): + if boxed { + buffer.appendInt32(1930838368) + } + serializeInt64(randomId, buffer: buffer, boxed: false) + action.serialize(buffer, true) + break + case .decryptedMessage(let flags, let randomId, let ttl, let message, let media, let entities, let viaBotName, let replyToRandomId, let groupedId): + if boxed { + buffer.appendInt32(-1848883596) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(randomId, buffer: buffer, boxed: false) + serializeInt32(ttl, buffer: buffer, boxed: false) + serializeString(message, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 9) != 0 {media!.serialize(buffer, true)} + if Int(flags) & Int(1 << 7) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 11) != 0 {serializeString(viaBotName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt64(replyToRandomId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 17) != 0 {serializeInt64(groupedId!, buffer: buffer, boxed: false)} + break + } + } + fileprivate static func parse_decryptedMessageService(_ reader: BufferReader) -> DecryptedMessage? { + var _1: Int64? + _1 = reader.readInt64() + var _2: SecretApi73.DecryptedMessageAction? + if let signature = reader.readInt32() { + _2 = SecretApi73.parse(reader, signature: signature) as? SecretApi73.DecryptedMessageAction + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.DecryptedMessage.decryptedMessageService(randomId: _1!, action: _2!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessage(_ reader: BufferReader) -> DecryptedMessage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + var _4: String? + _4 = parseString(reader) + var _5: SecretApi73.DecryptedMessageMedia? + if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { + _5 = SecretApi73.parse(reader, signature: signature) as? SecretApi73.DecryptedMessageMedia + } } + var _6: [SecretApi73.MessageEntity]? + if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() { + _6 = SecretApi73.parseVector(reader, elementSignature: 0, elementType: SecretApi73.MessageEntity.self) + } } + var _7: String? + if Int(_1!) & Int(1 << 11) != 0 {_7 = parseString(reader) } + var _8: Int64? + if Int(_1!) & Int(1 << 3) != 0 {_8 = reader.readInt64() } + var _9: Int64? + if Int(_1!) & Int(1 << 17) != 0 {_9 = reader.readInt64() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 9) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 7) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 17) == 0) || _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return SecretApi73.DecryptedMessage.decryptedMessage(flags: _1!, randomId: _2!, ttl: _3!, message: _4!, media: _5, entities: _6, viaBotName: _7, replyToRandomId: _8, groupedId: _9) + } + else { + return nil + } + } + + + } + + public enum DocumentAttribute { + case documentAttributeImageSize(w: Int32, h: Int32) + case documentAttributeAnimated + case documentAttributeFilename(fileName: String) + case documentAttributeSticker(alt: String, stickerset: SecretApi73.InputStickerSet) + case documentAttributeAudio(flags: Int32, duration: Int32, title: String?, performer: String?, waveform: Buffer?) + case documentAttributeVideo(flags: Int32, duration: Int32, w: Int32, h: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .documentAttributeImageSize(let w, let h): + if boxed { + buffer.appendInt32(1815593308) + } + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + break + case .documentAttributeAnimated: + if boxed { + buffer.appendInt32(297109817) + } + + break + case .documentAttributeFilename(let fileName): + if boxed { + buffer.appendInt32(358154344) + } + serializeString(fileName, buffer: buffer, boxed: false) + break + case .documentAttributeSticker(let alt, let stickerset): + if boxed { + buffer.appendInt32(978674434) + } + serializeString(alt, buffer: buffer, boxed: false) + stickerset.serialize(buffer, true) + break + case .documentAttributeAudio(let flags, let duration, let title, let performer, let waveform): + if boxed { + buffer.appendInt32(-1739392570) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(duration, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(performer!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeBytes(waveform!, buffer: buffer, boxed: false)} + break + case .documentAttributeVideo(let flags, let duration, let w, let h): + if boxed { + buffer.appendInt32(250621158) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(duration, buffer: buffer, boxed: false) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + break + } + } + fileprivate static func parse_documentAttributeImageSize(_ reader: BufferReader) -> DocumentAttribute? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.DocumentAttribute.documentAttributeImageSize(w: _1!, h: _2!) + } + else { + return nil + } + } + fileprivate static func parse_documentAttributeAnimated(_ reader: BufferReader) -> DocumentAttribute? { + return SecretApi73.DocumentAttribute.documentAttributeAnimated + } + fileprivate static func parse_documentAttributeFilename(_ reader: BufferReader) -> DocumentAttribute? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return SecretApi73.DocumentAttribute.documentAttributeFilename(fileName: _1!) + } + else { + return nil + } + } + fileprivate static func parse_documentAttributeSticker(_ reader: BufferReader) -> DocumentAttribute? { + var _1: String? + _1 = parseString(reader) + var _2: SecretApi73.InputStickerSet? + if let signature = reader.readInt32() { + _2 = SecretApi73.parse(reader, signature: signature) as? SecretApi73.InputStickerSet + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.DocumentAttribute.documentAttributeSticker(alt: _1!, stickerset: _2!) + } + else { + return nil + } + } + fileprivate static func parse_documentAttributeAudio(_ reader: BufferReader) -> DocumentAttribute? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + var _4: String? + if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) } + var _5: Buffer? + if Int(_1!) & Int(1 << 2) != 0 {_5 = parseBytes(reader) } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return SecretApi73.DocumentAttribute.documentAttributeAudio(flags: _1!, duration: _2!, title: _3, performer: _4, waveform: _5) + } + else { + return nil + } + } + fileprivate static func parse_documentAttributeVideo(_ reader: BufferReader) -> DocumentAttribute? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return SecretApi73.DocumentAttribute.documentAttributeVideo(flags: _1!, duration: _2!, w: _3!, h: _4!) + } + else { + return nil + } + } + + + } + + public enum InputStickerSet { + case inputStickerSetShortName(shortName: String) + case inputStickerSetEmpty + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputStickerSetShortName(let shortName): + if boxed { + buffer.appendInt32(-2044933984) + } + serializeString(shortName, buffer: buffer, boxed: false) + break + case .inputStickerSetEmpty: + if boxed { + buffer.appendInt32(-4838507) + } + + break + } + } + fileprivate static func parse_inputStickerSetShortName(_ reader: BufferReader) -> InputStickerSet? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return SecretApi73.InputStickerSet.inputStickerSetShortName(shortName: _1!) + } + else { + return nil + } + } + fileprivate static func parse_inputStickerSetEmpty(_ reader: BufferReader) -> InputStickerSet? { + return SecretApi73.InputStickerSet.inputStickerSetEmpty + } + + + } + + public enum MessageEntity { + case messageEntityUnknown(offset: Int32, length: Int32) + case messageEntityMention(offset: Int32, length: Int32) + case messageEntityHashtag(offset: Int32, length: Int32) + case messageEntityBotCommand(offset: Int32, length: Int32) + case messageEntityUrl(offset: Int32, length: Int32) + case messageEntityEmail(offset: Int32, length: Int32) + case messageEntityBold(offset: Int32, length: Int32) + case messageEntityItalic(offset: Int32, length: Int32) + case messageEntityCode(offset: Int32, length: Int32) + case messageEntityPre(offset: Int32, length: Int32, language: String) + case messageEntityTextUrl(offset: Int32, length: Int32, url: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .messageEntityUnknown(let offset, let length): + if boxed { + buffer.appendInt32(-1148011883) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .messageEntityMention(let offset, let length): + if boxed { + buffer.appendInt32(-100378723) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .messageEntityHashtag(let offset, let length): + if boxed { + buffer.appendInt32(1868782349) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .messageEntityBotCommand(let offset, let length): + if boxed { + buffer.appendInt32(1827637959) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .messageEntityUrl(let offset, let length): + if boxed { + buffer.appendInt32(1859134776) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .messageEntityEmail(let offset, let length): + if boxed { + buffer.appendInt32(1692693954) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .messageEntityBold(let offset, let length): + if boxed { + buffer.appendInt32(-1117713463) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .messageEntityItalic(let offset, let length): + if boxed { + buffer.appendInt32(-2106619040) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .messageEntityCode(let offset, let length): + if boxed { + buffer.appendInt32(681706865) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break + case .messageEntityPre(let offset, let length, let language): + if boxed { + buffer.appendInt32(1938967520) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + serializeString(language, buffer: buffer, boxed: false) + break + case .messageEntityTextUrl(let offset, let length, let url): + if boxed { + buffer.appendInt32(1990644519) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + break + } + } + fileprivate static func parse_messageEntityUnknown(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.MessageEntity.messageEntityUnknown(offset: _1!, length: _2!) + } + else { + return nil + } + } + fileprivate static func parse_messageEntityMention(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.MessageEntity.messageEntityMention(offset: _1!, length: _2!) + } + else { + return nil + } + } + fileprivate static func parse_messageEntityHashtag(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.MessageEntity.messageEntityHashtag(offset: _1!, length: _2!) + } + else { + return nil + } + } + fileprivate static func parse_messageEntityBotCommand(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.MessageEntity.messageEntityBotCommand(offset: _1!, length: _2!) + } + else { + return nil + } + } + fileprivate static func parse_messageEntityUrl(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.MessageEntity.messageEntityUrl(offset: _1!, length: _2!) + } + else { + return nil + } + } + fileprivate static func parse_messageEntityEmail(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.MessageEntity.messageEntityEmail(offset: _1!, length: _2!) + } + else { + return nil + } + } + fileprivate static func parse_messageEntityBold(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.MessageEntity.messageEntityBold(offset: _1!, length: _2!) + } + else { + return nil + } + } + fileprivate static func parse_messageEntityItalic(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.MessageEntity.messageEntityItalic(offset: _1!, length: _2!) + } + else { + return nil + } + } + fileprivate static func parse_messageEntityCode(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.MessageEntity.messageEntityCode(offset: _1!, length: _2!) + } + else { + return nil + } + } + fileprivate static func parse_messageEntityPre(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return SecretApi73.MessageEntity.messageEntityPre(offset: _1!, length: _2!, language: _3!) + } + else { + return nil + } + } + fileprivate static func parse_messageEntityTextUrl(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return SecretApi73.MessageEntity.messageEntityTextUrl(offset: _1!, length: _2!, url: _3!) + } + else { + return nil + } + } + + + } + + public enum DecryptedMessageMedia { + case decryptedMessageMediaEmpty + case decryptedMessageMediaGeoPoint(lat: Double, long: Double) + case decryptedMessageMediaContact(phoneNumber: String, firstName: String, lastName: String, userId: Int32) + case decryptedMessageMediaAudio(duration: Int32, mimeType: String, size: Int32, key: Buffer, iv: Buffer) + case decryptedMessageMediaExternalDocument(id: Int64, accessHash: Int64, date: Int32, mimeType: String, size: Int32, thumb: SecretApi73.PhotoSize, dcId: Int32, attributes: [SecretApi73.DocumentAttribute]) + case decryptedMessageMediaPhoto(thumb: Buffer, thumbW: Int32, thumbH: Int32, w: Int32, h: Int32, size: Int32, key: Buffer, iv: Buffer, caption: String) + case decryptedMessageMediaDocument(thumb: Buffer, thumbW: Int32, thumbH: Int32, mimeType: String, size: Int32, key: Buffer, iv: Buffer, attributes: [SecretApi73.DocumentAttribute], caption: String) + case decryptedMessageMediaVideo(thumb: Buffer, thumbW: Int32, thumbH: Int32, duration: Int32, mimeType: String, w: Int32, h: Int32, size: Int32, key: Buffer, iv: Buffer, caption: String) + case decryptedMessageMediaVenue(lat: Double, long: Double, title: String, address: String, provider: String, venueId: String) + case decryptedMessageMediaWebPage(url: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .decryptedMessageMediaEmpty: + if boxed { + buffer.appendInt32(144661578) + } + + break + case .decryptedMessageMediaGeoPoint(let lat, let long): + if boxed { + buffer.appendInt32(893913689) + } + serializeDouble(lat, buffer: buffer, boxed: false) + serializeDouble(long, buffer: buffer, boxed: false) + break + case .decryptedMessageMediaContact(let phoneNumber, let firstName, let lastName, let userId): + if boxed { + buffer.appendInt32(1485441687) + } + serializeString(phoneNumber, buffer: buffer, boxed: false) + serializeString(firstName, buffer: buffer, boxed: false) + serializeString(lastName, buffer: buffer, boxed: false) + serializeInt32(userId, buffer: buffer, boxed: false) + break + case .decryptedMessageMediaAudio(let duration, let mimeType, let size, let key, let iv): + if boxed { + buffer.appendInt32(1474341323) + } + serializeInt32(duration, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + serializeBytes(key, buffer: buffer, boxed: false) + serializeBytes(iv, buffer: buffer, boxed: false) + break + case .decryptedMessageMediaExternalDocument(let id, let accessHash, let date, let mimeType, let size, let thumb, let dcId, let attributes): + if boxed { + buffer.appendInt32(-90853155) + } + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + thumb.serialize(buffer, true) + serializeInt32(dcId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(attributes.count)) + for item in attributes { + item.serialize(buffer, true) + } + break + case .decryptedMessageMediaPhoto(let thumb, let thumbW, let thumbH, let w, let h, let size, let key, let iv, let caption): + if boxed { + buffer.appendInt32(-235238024) + } + serializeBytes(thumb, buffer: buffer, boxed: false) + serializeInt32(thumbW, buffer: buffer, boxed: false) + serializeInt32(thumbH, buffer: buffer, boxed: false) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + serializeBytes(key, buffer: buffer, boxed: false) + serializeBytes(iv, buffer: buffer, boxed: false) + serializeString(caption, buffer: buffer, boxed: false) + break + case .decryptedMessageMediaDocument(let thumb, let thumbW, let thumbH, let mimeType, let size, let key, let iv, let attributes, let caption): + if boxed { + buffer.appendInt32(2063502050) + } + serializeBytes(thumb, buffer: buffer, boxed: false) + serializeInt32(thumbW, buffer: buffer, boxed: false) + serializeInt32(thumbH, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + serializeBytes(key, buffer: buffer, boxed: false) + serializeBytes(iv, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(attributes.count)) + for item in attributes { + item.serialize(buffer, true) + } + serializeString(caption, buffer: buffer, boxed: false) + break + case .decryptedMessageMediaVideo(let thumb, let thumbW, let thumbH, let duration, let mimeType, let w, let h, let size, let key, let iv, let caption): + if boxed { + buffer.appendInt32(-1760785394) + } + serializeBytes(thumb, buffer: buffer, boxed: false) + serializeInt32(thumbW, buffer: buffer, boxed: false) + serializeInt32(thumbH, buffer: buffer, boxed: false) + serializeInt32(duration, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + serializeBytes(key, buffer: buffer, boxed: false) + serializeBytes(iv, buffer: buffer, boxed: false) + serializeString(caption, buffer: buffer, boxed: false) + break + case .decryptedMessageMediaVenue(let lat, let long, let title, let address, let provider, let venueId): + if boxed { + buffer.appendInt32(-1978796689) + } + serializeDouble(lat, buffer: buffer, boxed: false) + serializeDouble(long, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(address, buffer: buffer, boxed: false) + serializeString(provider, buffer: buffer, boxed: false) + serializeString(venueId, buffer: buffer, boxed: false) + break + case .decryptedMessageMediaWebPage(let url): + if boxed { + buffer.appendInt32(-452652584) + } + serializeString(url, buffer: buffer, boxed: false) + break + } + } + fileprivate static func parse_decryptedMessageMediaEmpty(_ reader: BufferReader) -> DecryptedMessageMedia? { + return SecretApi73.DecryptedMessageMedia.decryptedMessageMediaEmpty + } + fileprivate static func parse_decryptedMessageMediaGeoPoint(_ reader: BufferReader) -> DecryptedMessageMedia? { + var _1: Double? + _1 = reader.readDouble() + var _2: Double? + _2 = reader.readDouble() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return SecretApi73.DecryptedMessageMedia.decryptedMessageMediaGeoPoint(lat: _1!, long: _2!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageMediaContact(_ reader: BufferReader) -> DecryptedMessageMedia? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return SecretApi73.DecryptedMessageMedia.decryptedMessageMediaContact(phoneNumber: _1!, firstName: _2!, lastName: _3!, userId: _4!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageMediaAudio(_ reader: BufferReader) -> DecryptedMessageMedia? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Int32? + _3 = reader.readInt32() + var _4: Buffer? + _4 = parseBytes(reader) + var _5: Buffer? + _5 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return SecretApi73.DecryptedMessageMedia.decryptedMessageMediaAudio(duration: _1!, mimeType: _2!, size: _3!, key: _4!, iv: _5!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageMediaExternalDocument(_ reader: BufferReader) -> DecryptedMessageMedia? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + var _4: String? + _4 = parseString(reader) + var _5: Int32? + _5 = reader.readInt32() + var _6: SecretApi73.PhotoSize? + if let signature = reader.readInt32() { + _6 = SecretApi73.parse(reader, signature: signature) as? SecretApi73.PhotoSize + } + var _7: Int32? + _7 = reader.readInt32() + var _8: [SecretApi73.DocumentAttribute]? + if let _ = reader.readInt32() { + _8 = SecretApi73.parseVector(reader, elementSignature: 0, elementType: SecretApi73.DocumentAttribute.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return SecretApi73.DecryptedMessageMedia.decryptedMessageMediaExternalDocument(id: _1!, accessHash: _2!, date: _3!, mimeType: _4!, size: _5!, thumb: _6!, dcId: _7!, attributes: _8!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageMediaPhoto(_ reader: BufferReader) -> DecryptedMessageMedia? { + var _1: Buffer? + _1 = parseBytes(reader) + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() + var _7: Buffer? + _7 = parseBytes(reader) + var _8: Buffer? + _8 = parseBytes(reader) + var _9: String? + _9 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return SecretApi73.DecryptedMessageMedia.decryptedMessageMediaPhoto(thumb: _1!, thumbW: _2!, thumbH: _3!, w: _4!, h: _5!, size: _6!, key: _7!, iv: _8!, caption: _9!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageMediaDocument(_ reader: BufferReader) -> DecryptedMessageMedia? { + var _1: Buffer? + _1 = parseBytes(reader) + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: String? + _4 = parseString(reader) + var _5: Int32? + _5 = reader.readInt32() + var _6: Buffer? + _6 = parseBytes(reader) + var _7: Buffer? + _7 = parseBytes(reader) + var _8: [SecretApi73.DocumentAttribute]? + if let _ = reader.readInt32() { + _8 = SecretApi73.parseVector(reader, elementSignature: 0, elementType: SecretApi73.DocumentAttribute.self) + } + var _9: String? + _9 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return SecretApi73.DecryptedMessageMedia.decryptedMessageMediaDocument(thumb: _1!, thumbW: _2!, thumbH: _3!, mimeType: _4!, size: _5!, key: _6!, iv: _7!, attributes: _8!, caption: _9!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageMediaVideo(_ reader: BufferReader) -> DecryptedMessageMedia? { + var _1: Buffer? + _1 = parseBytes(reader) + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: String? + _5 = parseString(reader) + var _6: Int32? + _6 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() + var _8: Int32? + _8 = reader.readInt32() + var _9: Buffer? + _9 = parseBytes(reader) + var _10: Buffer? + _10 = parseBytes(reader) + var _11: String? + _11 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + let _c10 = _10 != nil + let _c11 = _11 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { + return SecretApi73.DecryptedMessageMedia.decryptedMessageMediaVideo(thumb: _1!, thumbW: _2!, thumbH: _3!, duration: _4!, mimeType: _5!, w: _6!, h: _7!, size: _8!, key: _9!, iv: _10!, caption: _11!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageMediaVenue(_ reader: BufferReader) -> DecryptedMessageMedia? { + var _1: Double? + _1 = reader.readDouble() + var _2: Double? + _2 = reader.readDouble() + var _3: String? + _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) + var _5: String? + _5 = parseString(reader) + var _6: String? + _6 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return SecretApi73.DecryptedMessageMedia.decryptedMessageMediaVenue(lat: _1!, long: _2!, title: _3!, address: _4!, provider: _5!, venueId: _6!) + } + else { + return nil + } + } + fileprivate static func parse_decryptedMessageMediaWebPage(_ reader: BufferReader) -> DecryptedMessageMedia? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return SecretApi73.DecryptedMessageMedia.decryptedMessageMediaWebPage(url: _1!) + } + else { + return nil + } + } + + + } + + public struct functions { + + } + +} diff --git a/TelegramCore/SecretChatEncryption.swift b/TelegramCore/SecretChatEncryption.swift index dd73321799..75773a8350 100644 --- a/TelegramCore/SecretChatEncryption.swift +++ b/TelegramCore/SecretChatEncryption.swift @@ -7,121 +7,224 @@ import Foundation import MtProtoKitDynamic #endif -private func messageKey(key: SecretChatKey, msgKey: UnsafeRawPointer) -> (aesKey: Data, aesIv: Data) { - let x: Int = 0 - - var sha1AData = Data() - sha1AData.count = 16 + 32 - sha1AData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - memcpy(bytes, msgKey, 16) - memcpy(bytes.advanced(by: 16), key.key.memory.advanced(by: x), 32) +private func messageKey(key: SecretChatKey, msgKey: UnsafeRawPointer, mode: SecretChatEncryptionMode) -> (aesKey: Data, aesIv: Data) { + switch mode { + case .v1: + let x: Int = 0 + + var sha1AData = Data() + sha1AData.count = 16 + 32 + sha1AData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + memcpy(bytes, msgKey, 16) + memcpy(bytes.advanced(by: 16), key.key.memory.advanced(by: x), 32) + } + let sha1A = MTSha1(sha1AData)! + + var sha1BData = Data() + sha1BData.count = 16 + 16 + 16 + sha1BData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + memcpy(bytes, key.key.memory.advanced(by: 32 + x), 16) + memcpy(bytes.advanced(by: 16), msgKey, 16) + memcpy(bytes.advanced(by: 16 + 16), key.key.memory.advanced(by: 48 + x), 16) + } + let sha1B = MTSha1(sha1BData)! + + var sha1CData = Data() + sha1CData.count = 32 + 16 + sha1CData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + memcpy(bytes, key.key.memory.advanced(by: 64 + x), 32) + memcpy(bytes.advanced(by: 32), msgKey, 16) + } + let sha1C = MTSha1(sha1CData)! + + var sha1DData = Data() + sha1DData.count = 16 + 32 + sha1DData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + memcpy(bytes, msgKey, 16) + memcpy(bytes.advanced(by: 16), key.key.memory.advanced(by: 96 + x), 32) + } + let sha1D = MTSha1(sha1DData)! + + var aesKey = Data() + aesKey.count = 8 + 12 + 12 + aesKey.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + sha1A.withUnsafeBytes { (sha1A: UnsafePointer) -> Void in + memcpy(bytes, sha1A, 8) + } + sha1B.withUnsafeBytes { (sha1B: UnsafePointer) -> Void in + memcpy(bytes.advanced(by: 8), sha1B.advanced(by: 8), 12) + } + sha1C.withUnsafeBytes { (sha1C: UnsafePointer) -> Void in + memcpy(bytes.advanced(by: 8 + 12), sha1C.advanced(by: 4), 12) + } + } + + var aesIv = Data() + aesIv.count = 12 + 8 + 4 + 8 + aesIv.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + sha1A.withUnsafeBytes { (sha1A: UnsafePointer) -> Void in + memcpy(bytes, sha1A.advanced(by: 8), 12) + } + sha1B.withUnsafeBytes { (sha1B: UnsafePointer) -> Void in + memcpy(bytes.advanced(by: 12), sha1B, 8) + } + sha1C.withUnsafeBytes { (sha1C: UnsafePointer) -> Void in + memcpy(bytes.advanced(by: 12 + 8), sha1C.advanced(by: 16), 4) + } + sha1D.withUnsafeBytes { (sha1D: UnsafePointer) -> Void in + memcpy(bytes.advanced(by: 12 + 8 + 4), sha1D, 8) + } + } + return (aesKey, aesIv) + case let .v2(role): + var xValue: Int + switch role { + case .creator: + xValue = 0 + case .participant: + xValue = 8 + } + + var sha256_a_data = Data() + sha256_a_data.append(msgKey.assumingMemoryBound(to: UInt8.self), count: 16) + sha256_a_data.append(key.key.memory.assumingMemoryBound(to: UInt8.self).advanced(by: xValue), count: 36) + + let sha256_a = MTSha256(sha256_a_data)! + + var sha256_b_data = Data() + sha256_b_data.append(key.key.memory.assumingMemoryBound(to: UInt8.self).advanced(by: 40 + xValue), count: 36) + sha256_b_data.append(msgKey.assumingMemoryBound(to: UInt8.self), count: 16) + + let sha256_b = MTSha256(sha256_b_data)! + + var aesKey = Data() + aesKey.append(sha256_a.subdata(in: 0 ..< (0 + 8))) + aesKey.append(sha256_b.subdata(in: 8 ..< (8 + 16))) + aesKey.append(sha256_a.subdata(in: 24 ..< (24 + 8))) + + var aesIv = Data() + aesIv.append(sha256_b.subdata(in: 0 ..< (0 + 8))) + aesIv.append(sha256_a.subdata(in: 8 ..< (8 + 16))) + aesIv.append(sha256_b.subdata(in: 24 ..< (24 + 8))) + + return (aesKey, aesIv) } - let sha1A = MTSha1(sha1AData)! - - var sha1BData = Data() - sha1BData.count = 16 + 16 + 16 - sha1BData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - memcpy(bytes, key.key.memory.advanced(by: 32 + x), 16) - memcpy(bytes.advanced(by: 16), msgKey, 16) - memcpy(bytes.advanced(by: 16 + 16), key.key.memory.advanced(by: 48 + x), 16) - } - let sha1B = MTSha1(sha1BData)! - - var sha1CData = Data() - sha1CData.count = 32 + 16 - sha1CData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - memcpy(bytes, key.key.memory.advanced(by: 64 + x), 32) - memcpy(bytes.advanced(by: 32), msgKey, 16) - } - let sha1C = MTSha1(sha1CData)! - - var sha1DData = Data() - sha1DData.count = 16 + 32 - sha1DData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - memcpy(bytes, msgKey, 16) - memcpy(bytes.advanced(by: 16), key.key.memory.advanced(by: 96 + x), 32) - } - let sha1D = MTSha1(sha1DData)! - - var aesKey = Data() - aesKey.count = 8 + 12 + 12 - aesKey.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - sha1A.withUnsafeBytes { (sha1A: UnsafePointer) -> Void in - memcpy(bytes, sha1A, 8) - } - sha1B.withUnsafeBytes { (sha1B: UnsafePointer) -> Void in - memcpy(bytes.advanced(by: 8), sha1B.advanced(by: 8), 12) - } - sha1C.withUnsafeBytes { (sha1C: UnsafePointer) -> Void in - memcpy(bytes.advanced(by: 8 + 12), sha1C.advanced(by: 4), 12) - } - } - - var aesIv = Data() - aesIv.count = 12 + 8 + 4 + 8 - aesIv.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - sha1A.withUnsafeBytes { (sha1A: UnsafePointer) -> Void in - memcpy(bytes, sha1A.advanced(by: 8), 12) - } - sha1B.withUnsafeBytes { (sha1B: UnsafePointer) -> Void in - memcpy(bytes.advanced(by: 12), sha1B, 8) - } - sha1C.withUnsafeBytes { (sha1C: UnsafePointer) -> Void in - memcpy(bytes.advanced(by: 12 + 8), sha1C.advanced(by: 16), 4) - } - sha1D.withUnsafeBytes { (sha1D: UnsafePointer) -> Void in - memcpy(bytes.advanced(by: 12 + 8 + 4), sha1D, 8) - } - } - return (aesKey, aesIv) } -func withDecryptedMessageContents(key: SecretChatKey, data: MemoryBuffer, _ f: (MemoryBuffer?) -> Void) { - assert(key.key.length == 256) +func withDecryptedMessageContents(parameters: SecretChatEncryptionParameters, data: MemoryBuffer) -> MemoryBuffer? { + assert(parameters.key.key.length == 256) if data.length < 4 + 16 + 16 { - f(nil) - return + return nil } let msgKey = data.memory.advanced(by: 8) - let (aesKey, aesIv) = messageKey(key: key, msgKey: msgKey) - - let decryptedData = MTAesDecrypt(Data(bytes: data.memory.advanced(by: 8 + 16), count: data.length - (8 + 16)), aesKey, aesIv)! - - if decryptedData.count < 4 * 3 { - f(nil) - return - } - - var payloadLength: Int32 = 0 - decryptedData.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - memcpy(&payloadLength, bytes, 4) - } - - let paddingLength = decryptedData.count - (Int(payloadLength) + 4) - if Int(payloadLength) > decryptedData.count - 4 || paddingLength > 16 { - f(nil) - return - } - - let calculatedMsgKeyData = MTSubdataSha1(decryptedData, 0, UInt(payloadLength) + 4)! - let msgKeyMatches = calculatedMsgKeyData.withUnsafeBytes { (bytes: UnsafePointer) -> Bool in - return memcmp(bytes.advanced(by: calculatedMsgKeyData.count - 16), msgKey, 16) == 0 - } - - if !msgKeyMatches { - f(nil) - return - } - - decryptedData.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - f(MemoryBuffer(memory: UnsafeMutablePointer(mutating: bytes.advanced(by: 4)), capacity: Int(payloadLength), length: Int(payloadLength), freeWhenDone: false)) + switch parameters.mode { + case .v1: + let (aesKey, aesIv) = messageKey(key: parameters.key, msgKey: msgKey, mode: parameters.mode) + + let decryptedData = MTAesDecrypt(Data(bytes: data.memory.advanced(by: 8 + 16), count: data.length - (8 + 16)), aesKey, aesIv)! + + if decryptedData.count < 4 * 3 { + return nil + } + + var payloadLength: Int32 = 0 + decryptedData.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + memcpy(&payloadLength, bytes, 4) + } + + let paddingLength = decryptedData.count - (Int(payloadLength) + 4) + if Int(payloadLength) > decryptedData.count - 4 || paddingLength > 16 { + return nil + } + + let calculatedMsgKeyData = MTSubdataSha1(decryptedData, 0, UInt(payloadLength) + 4)! + let msgKeyMatches = calculatedMsgKeyData.withUnsafeBytes { (bytes: UnsafePointer) -> Bool in + return memcmp(bytes.advanced(by: calculatedMsgKeyData.count - 16), msgKey, 16) == 0 + } + + if !msgKeyMatches { + return nil + } + + let result = decryptedData.withUnsafeBytes { (bytes: UnsafePointer) -> Data in + return Data(bytes: bytes.advanced(by: 4), count: Int(payloadLength)) + } + return MemoryBuffer(data: result) + case let .v2(role): + let senderRole: SecretChatRole + switch role { + case .creator: + senderRole = .participant + case .participant: + senderRole = .creator + } + let (aesKey, aesIv) = messageKey(key: parameters.key, msgKey: msgKey, mode: .v2(role: senderRole)) + + let decryptedData = MTAesDecrypt(Data(bytes: data.memory.advanced(by: 8 + 16), count: data.length - (8 + 16)), aesKey, aesIv)! + + if decryptedData.count < 4 * 3 { + return nil + } + + var payloadLength: Int32 = 0 + decryptedData.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + memcpy(&payloadLength, bytes, 4) + } + + let paddingLength = decryptedData.count - (Int(payloadLength) + 4) + + let xValue: Int + switch role { + case .creator: + xValue = 8 + case .participant: + xValue = 0 + } + + var keyLargeData = Data() + keyLargeData.append(parameters.key.key.memory.assumingMemoryBound(to: UInt8.self).advanced(by: 88 + xValue), count: 32) + keyLargeData.append(decryptedData) + + let keyLarge = MTSha256(keyLargeData)! + let localMessageKey = keyLarge.subdata(in: 8 ..< (8 + 16)) + + let msgKeyData = Data(bytes: msgKey.assumingMemoryBound(to: UInt8.self), count: 16) + + if Int(payloadLength) <= 0 || Int(payloadLength) > decryptedData.count - 4 || paddingLength < 12 || paddingLength > 1024 { + + if localMessageKey != msgKeyData { + Logger.shared.log("SecretChatEncryption", "message key doesn't match (length check)") + } + + return nil + } + + if localMessageKey != msgKeyData { + Logger.shared.log("SecretChatEncryption", "message key doesn't match") + return nil + } + + let result = decryptedData.withUnsafeBytes { (bytes: UnsafePointer) -> Data in + return Data(bytes: bytes.advanced(by: 4), count: Int(payloadLength)) + } + return MemoryBuffer(data: result) } } -func encryptedMessageContents(key: SecretChatKey, data: MemoryBuffer) -> Data { +enum SecretChatEncryptionMode { + case v1 + case v2(role: SecretChatRole) +} + +struct SecretChatEncryptionParameters { + let key: SecretChatKey + let mode: SecretChatEncryptionMode +} + +func encryptedMessageContents(parameters: SecretChatEncryptionParameters, data: MemoryBuffer) -> Data { var payloadLength: Int32 = Int32(data.length) var payloadData = Data() withUnsafeBytes(of: &payloadLength, { bytes -> Void in @@ -129,33 +232,94 @@ func encryptedMessageContents(key: SecretChatKey, data: MemoryBuffer) -> Data { }) payloadData.append(data.memory.assumingMemoryBound(to: UInt8.self), count: data.length) - var msgKey = MTSha1(payloadData)! - msgKey.replaceSubrange(0 ..< (msgKey.count - 16), with: Data()) - - var randomBuf = malloc(16)! - defer { - free(randomBuf) + switch parameters.mode { + case .v1: + var msgKey = MTSha1(payloadData)! + msgKey.replaceSubrange(0 ..< (msgKey.count - 16), with: Data()) + + var randomBuf = malloc(16)! + defer { + free(randomBuf) + } + let randomBytes = randomBuf.assumingMemoryBound(to: UInt8.self) + arc4random_buf(randomBuf, 16) + + var randomIndex = 0 + while payloadData.count % 16 != 0 { + payloadData.append(randomBytes.advanced(by: randomIndex), count: 1) + randomIndex += 1 + } + + let (aesKey, aesIv) = msgKey.withUnsafeBytes { (bytes: UnsafePointer) -> (Data, Data) in + return messageKey(key: parameters.key, msgKey: bytes, mode: parameters.mode) + } + + let encryptedData = MTAesEncrypt(payloadData, aesKey, aesIv)! + var encryptedPayload = Data() + var keyFingerprint: Int64 = parameters.key.fingerprint + withUnsafeBytes(of: &keyFingerprint, { bytes -> Void in + encryptedPayload.append(bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), count: 8) + }) + encryptedPayload.append(msgKey) + encryptedPayload.append(encryptedData) + return encryptedPayload + case let .v2(role): + var randomBytes = Data(count: 128) + randomBytes.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + arc4random_buf(bytes, randomBytes.count) + } + + var decryptedData = payloadData + var take = 0 + while take < 12 { + decryptedData.append(randomBytes.subdata(in: take ..< (take + 1))) + take += 1 + } + + while decryptedData.count % 16 != 0 { + decryptedData.append(randomBytes.subdata(in: take ..< (take + 1))) + take += 1 + } + + var remainingCount = Int(arc4random_uniform(UInt32(72 + 1 - take))) + while remainingCount % 16 != 0 { + remainingCount -= 1 + } + + for _ in 0 ..< remainingCount { + decryptedData.append(randomBytes.subdata(in: take ..< (take + 1))) + take += 1 + } + + var xValue: Int + switch role { + case .creator: + xValue = 0 + case .participant: + xValue = 8 + } + + var keyData = Data() + keyData.append(parameters.key.key.memory.assumingMemoryBound(to: UInt8.self).advanced(by: 88 + xValue), count: 32) + + keyData.append(decryptedData) + + let keyLarge = MTSha256(keyData)! + + let msgKey = keyLarge.subdata(in: 8 ..< (8 + 16)) + + let (aesKey, aesIv) = msgKey.withUnsafeBytes { (bytes: UnsafePointer) -> (Data, Data) in + return messageKey(key: parameters.key, msgKey: bytes, mode: parameters.mode) + } + + let encryptedData = MTAesEncrypt(decryptedData, aesKey, aesIv)! + var encryptedPayload = Data() + var keyFingerprint: Int64 = parameters.key.fingerprint + withUnsafeBytes(of: &keyFingerprint, { bytes -> Void in + encryptedPayload.append(bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), count: 8) + }) + encryptedPayload.append(msgKey) + encryptedPayload.append(encryptedData) + return encryptedPayload } - let randomBytes = randomBuf.assumingMemoryBound(to: UInt8.self) - arc4random_buf(randomBuf, 16) - - var randomIndex = 0 - while payloadData.count % 16 != 0 { - payloadData.append(randomBytes.advanced(by: randomIndex), count: 1) - randomIndex += 1 - } - - let (aesKey, aesIv) = msgKey.withUnsafeBytes { (bytes: UnsafePointer) -> (Data, Data) in - return messageKey(key: key, msgKey: bytes) - } - - let encryptedData = MTAesEncrypt(payloadData, aesKey, aesIv)! - var encryptedPayload = Data() - var keyFingerprint: Int64 = key.fingerprint - withUnsafeBytes(of: &keyFingerprint, { bytes -> Void in - encryptedPayload.append(bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), count: 8) - }) - encryptedPayload.append(msgKey) - encryptedPayload.append(encryptedData) - return encryptedPayload } diff --git a/TelegramCore/SecretChatLayerNegotiation.swift b/TelegramCore/SecretChatLayerNegotiation.swift new file mode 100644 index 0000000000..9b693514a3 --- /dev/null +++ b/TelegramCore/SecretChatLayerNegotiation.swift @@ -0,0 +1,63 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +private let topSupportedLayer: SecretChatSequenceBasedLayer = .layer73 + +func secretChatCommonSupportedLayer(remoteLayer: Int32) -> SecretChatSequenceBasedLayer { + switch remoteLayer { + case 46: + return .layer46 + case 73: + return .layer73 + default: + return topSupportedLayer + } +} + +func secretChatAddReportCurrentLayerSupportOperationAndUpdateRequestedLayer(modifier: Modifier, peerId: PeerId, state: SecretChatState) -> SecretChatState { + switch state.embeddedState { + case .basicLayer: + var updatedState = state + updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: .reportLayerSupport(layer: topSupportedLayer.secretChatLayer, actionGloballyUniqueId: arc4random64(), layerSupport: topSupportedLayer.rawValue), state: updatedState) + return updatedState + case let .sequenceBasedLayer(sequenceState): + var updatedState = state + updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: .reportLayerSupport(layer: sequenceState.layerNegotiationState.activeLayer.secretChatLayer, actionGloballyUniqueId: arc4random64(), layerSupport: topSupportedLayer.rawValue), state: updatedState) + updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedLayerNegotiationState(sequenceState.layerNegotiationState.withUpdatedLocallyRequestedLayer(topSupportedLayer.rawValue)))) + return updatedState + default: + return state + } +} + +func secretChatCheckLayerNegotiationIfNeeded(modifier: Modifier, peerId: PeerId, state: SecretChatState) -> SecretChatState { + switch state.embeddedState { + case let .sequenceBasedLayer(sequenceState): + if sequenceState.layerNegotiationState.activeLayer != topSupportedLayer { + var updatedState = state + + if let remotelyRequestedLayer = sequenceState.layerNegotiationState.remotelyRequestedLayer { + let updatedSequenceState = sequenceState.withUpdatedLayerNegotiationState(sequenceState.layerNegotiationState.withUpdatedActiveLayer(secretChatCommonSupportedLayer(remoteLayer: remotelyRequestedLayer))) + updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(updatedSequenceState)) + } + + if (sequenceState.layerNegotiationState.locallyRequestedLayer ?? 0) < topSupportedLayer.rawValue { + updatedState = secretChatAddReportCurrentLayerSupportOperationAndUpdateRequestedLayer(modifier: modifier, peerId: peerId, state: updatedState) + } + + return updatedState + } else { + return state + } + case .basicLayer: + return state + default: + return state + } +} diff --git a/TelegramCore/SecretChatOutgoingOperation.swift b/TelegramCore/SecretChatOutgoingOperation.swift index be306cd25d..efd3038ef5 100644 --- a/TelegramCore/SecretChatOutgoingOperation.swift +++ b/TelegramCore/SecretChatOutgoingOperation.swift @@ -78,6 +78,16 @@ struct SecretChatOutgoingFile: PostboxCoding { enum SecretChatSequenceBasedLayer: Int32 { case layer46 = 46 + case layer73 = 73 + + var secretChatLayer: SecretChatLayer { + switch self { + case .layer46: + return .layer46 + case .layer73: + return .layer73 + } + } } private enum SecretChatOutgoingOperationValue: Int32 { diff --git a/TelegramCore/SecretChatRekeySession.swift b/TelegramCore/SecretChatRekeySession.swift index 4556f5bd78..0f7416c7c9 100644 --- a/TelegramCore/SecretChatRekeySession.swift +++ b/TelegramCore/SecretChatRekeySession.swift @@ -23,7 +23,7 @@ func secretChatInitiateRekeySessionIfNeeded(modifier: Modifier, peerId: PeerId, let _ = SecRandomCopyBytes(nil, 256, aBytes.assumingMemoryBound(to: UInt8.self)) let a = MemoryBuffer(memory: aBytes, capacity: 256, length: 256, freeWhenDone: true) - modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsRequestKey(layer: .layer46, actionGloballyUniqueId: arc4random64(), rekeySessionId: sessionId, a: a), mutable: true, delivered: false)) + modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsRequestKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: sessionId, a: a), mutable: true, delivered: false)) return state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(SecretChatRekeySessionState(id: sessionId, data: .requesting)))) } default: @@ -69,7 +69,7 @@ func secretChatAdvanceRekeySessionIfNeeded(modifier: Modifier, peerId: PeerId, s assert(remoteKeyFingerprint == keyFingerprint) - modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsCommitKey(layer: .layer46, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id, keyFingerprint: keyFingerprint), mutable: true, delivered: false)) + modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsCommitKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id, keyFingerprint: keyFingerprint), mutable: true, delivered: false)) let keyValidityOperationIndex = modifier.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing) let keyValidityOperationCanonicalIndex = sequenceState.canonicalOutgoingOperationIndex(keyValidityOperationIndex) @@ -92,7 +92,7 @@ func secretChatAdvanceRekeySessionIfNeeded(modifier: Modifier, peerId: PeerId, s return SecretChatKey(fingerprint: keyFingerprint, key: key, validity: .sequenceBasedIndexRange(fromCanonicalIndex: keyValidityOperationCanonicalIndex), useCount: 0) })) - modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .noop(layer: .layer46, actionGloballyUniqueId: arc4random64()), mutable: true, delivered: false)) + modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .noop(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64()), mutable: true, delivered: false)) return updatedState } else { @@ -107,7 +107,7 @@ func secretChatAdvanceRekeySessionIfNeeded(modifier: Modifier, peerId: PeerId, s switch rekeySession.data { case .requesting, .requested: if rekeySessionId < rekeySession.id { - modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAbortSession(layer: .layer46, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id), mutable: true, delivered: false)) + modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAbortSession(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id), mutable: true, delivered: false)) } else { acceptSession = false } @@ -117,14 +117,13 @@ func secretChatAdvanceRekeySessionIfNeeded(modifier: Modifier, peerId: PeerId, s } if acceptSession { - let sessionId = arc4random64() let bBytes = malloc(256)! let _ = SecRandomCopyBytes(nil, 256, bBytes.assumingMemoryBound(to: UInt8.self)) let b = MemoryBuffer(memory: bBytes, capacity: 256, length: 256, freeWhenDone: true) let rekeySession = SecretChatRekeySessionState(id: rekeySessionId, data: .accepting) - modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAcceptKey(layer: .layer46, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id, gA: gA, b: b), mutable: true, delivered: false)) + modifier.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAcceptKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id, gA: gA, b: b), mutable: true, delivered: false)) return state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(rekeySession))) } } diff --git a/TelegramCore/SecretChatState.swift b/TelegramCore/SecretChatState.swift index 4f765e70d6..695ea0d74c 100644 --- a/TelegramCore/SecretChatState.swift +++ b/TelegramCore/SecretChatState.swift @@ -13,6 +13,7 @@ public enum SecretChatRole: Int32 { enum SecretChatLayer: Int32 { case layer8 = 8 case layer46 = 46 + case layer73 = 73 } public struct SecretChatKeySha1Fingerprint: PostboxCoding, Equatable { @@ -240,24 +241,24 @@ enum SecretChatHandshakeState: PostboxCoding, Equatable { } struct SecretChatLayerNegotiationState: PostboxCoding, Equatable { - let activeLayer: Int32 + let activeLayer: SecretChatSequenceBasedLayer let locallyRequestedLayer: Int32? let remotelyRequestedLayer: Int32? - init(activeLayer: Int32, locallyRequestedLayer: Int32?, remotelyRequestedLayer: Int32?) { + init(activeLayer: SecretChatSequenceBasedLayer, locallyRequestedLayer: Int32?, remotelyRequestedLayer: Int32?) { self.activeLayer = activeLayer self.locallyRequestedLayer = locallyRequestedLayer self.remotelyRequestedLayer = remotelyRequestedLayer } init(decoder: PostboxDecoder) { - self.activeLayer = decoder.decodeInt32ForKey("a", orElse: 0) + self.activeLayer = SecretChatSequenceBasedLayer(rawValue: decoder.decodeInt32ForKey("a", orElse: 0)) ?? .layer46 self.locallyRequestedLayer = decoder.decodeOptionalInt32ForKey("lr") self.remotelyRequestedLayer = decoder.decodeOptionalInt32ForKey("rr") } func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt32(self.activeLayer, forKey: "a") + encoder.encodeInt32(self.activeLayer.rawValue, forKey: "a") if let locallyRequestedLayer = self.locallyRequestedLayer { encoder.encodeInt32(locallyRequestedLayer, forKey: "lr") } else { @@ -283,6 +284,14 @@ struct SecretChatLayerNegotiationState: PostboxCoding, Equatable { return true } + func withUpdatedActiveLayer(_ activeLayer: SecretChatSequenceBasedLayer) -> SecretChatLayerNegotiationState { + return SecretChatLayerNegotiationState(activeLayer: activeLayer, locallyRequestedLayer: self.locallyRequestedLayer, remotelyRequestedLayer: self.remotelyRequestedLayer) + } + + func withUpdatedLocallyRequestedLayer(_ locallyRequestedLayer: Int32?) -> SecretChatLayerNegotiationState { + return SecretChatLayerNegotiationState(activeLayer: self.activeLayer, locallyRequestedLayer: locallyRequestedLayer, remotelyRequestedLayer: self.remotelyRequestedLayer) + } + func withUpdatedRemotelyRequestedLayer(_ remotelyRequestedLayer: Int32?) -> SecretChatLayerNegotiationState { return SecretChatLayerNegotiationState(activeLayer: self.activeLayer, locallyRequestedLayer: self.locallyRequestedLayer, remotelyRequestedLayer: remotelyRequestedLayer) } diff --git a/TelegramCore/SecretChatStateBridge.swift b/TelegramCore/SecretChatStateBridge.swift deleted file mode 100644 index d94c2a14a2..0000000000 --- a/TelegramCore/SecretChatStateBridge.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation -#if os(macOS) - import PostboxMac -#else - import Postbox -#endif - -public enum SecretChatBridgeRole: Int32 { - case creator - case participant -} - -public struct SecretChatStateBridge { - public let role:SecretChatBridgeRole - public init(role: SecretChatBridgeRole) { - self.role = role - } - - public var state: PeerChatState { - return SecretChatState(role: SecretChatRole(rawValue: role.rawValue)!, embeddedState: .terminated, keychain: SecretChatKeychain(keys: []), keyFingerprint: nil, messageAutoremoveTimeout: nil) - } - - -} - - -public func terminateLegacySecretChat(modifier: Modifier, peerId: PeerId, state: SecretChatStateBridge) -> PeerChatState { - return addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: SecretChatOutgoingOperationContents.terminate(reportSpam: false), state: state.state as! SecretChatState).withUpdatedEmbeddedState(.terminated) -} diff --git a/TelegramCore/Serialization.swift b/TelegramCore/Serialization.swift index 9d0f228226..2317811900 100644 --- a/TelegramCore/Serialization.swift +++ b/TelegramCore/Serialization.swift @@ -90,16 +90,11 @@ public class BoxedMessage: NSObject { #else redact = true #endif - return recursiveDescription(redact: redact, of: self.body) - /*if let body = self.body as? RedactingCustomStringConvertible { - - return body.redactingDescription(false) - #else - return body.redactingDescription(true) - #endif + if let body = self.body as? RedactingCustomStringConvertible { + return body.redactingDescription(redact) } else { - return "\(self.body)" - }*/ + return recursiveDescription(redact: redact, of: self.body) + } } } } diff --git a/TelegramCore/UpdateSecretChat.swift b/TelegramCore/UpdateSecretChat.swift index fe3cbe0b53..05b52fb56a 100644 --- a/TelegramCore/UpdateSecretChat.swift +++ b/TelegramCore/UpdateSecretChat.swift @@ -52,7 +52,7 @@ func updateSecretChat(accountPeerId: PeerId, modifier: Modifier, chat: Api.Encry case .basicLayer: layer = .layer8 case let .sequenceBasedLayer(sequenceState): - layer = SecretChatLayer(rawValue: sequenceState.layerNegotiationState.activeLayer) + layer = sequenceState.layerNegotiationState.activeLayer.secretChatLayer } if let layer = layer { updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: currentPeer.id, operation: .reportLayerSupport(layer: layer, actionGloballyUniqueId: arc4random64(), layerSupport: 46), state: updatedState) From 3306b36ddb76eb2d35269bd2f60b2410539ee6bc Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Thu, 1 Mar 2018 03:54:11 +0400 Subject: [PATCH 2/2] no message --- TelegramCore.xcodeproj/project.pbxproj | 6 + TelegramCore/Account.swift | 1 + .../AccountStateManagementUtils.swift | 8 +- .../ApplyMaxReadIndexInteractively.swift | 74 ++-- TelegramCore/ApplyUpdateMessage.swift | 15 +- TelegramCore/CallSessionManager.swift | 10 +- TelegramCore/ContentPrivacySettings.swift | 66 ++++ TelegramCore/InlineBotMessageAttribute.swift | 30 +- .../ManagedSecretChatOutgoingOperations.swift | 324 +++++++++++++++--- TelegramCore/Namespaces.swift | 8 + ...OutgoingMessageWithChatContextResult.swift | 106 +----- .../PendingMessageUploadedContent.swift | 50 ++- ...ecretChatIncomingDecryptedOperations.swift | 160 ++++++++- TelegramCore/SecretChatLayerNegotiation.swift | 2 +- TelegramCore/StoreMessage_Telegram.swift | 4 +- TelegramCore/TelegramMediaWebpage.swift | 25 +- TelegramCore/UpdateSecretChat.swift | 14 +- TelegramCore/WebpagePreview.swift | 6 +- 18 files changed, 657 insertions(+), 252 deletions(-) create mode 100644 TelegramCore/ContentPrivacySettings.swift diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index ace902b5e3..ded77b6296 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -593,6 +593,8 @@ D0F8C39E20178B9B00236FC5 /* GroupFeedPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F8C39C20178B9B00236FC5 /* GroupFeedPeers.swift */; }; D0F8C3A02017AF2700236FC5 /* GlobalTelegramCoreConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F8C39F2017AF2700236FC5 /* GlobalTelegramCoreConfiguration.swift */; }; D0F8C3A12017AF2700236FC5 /* GlobalTelegramCoreConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F8C39F2017AF2700236FC5 /* GlobalTelegramCoreConfiguration.swift */; }; + D0FA08BB2046B37900DD23FC /* ContentPrivacySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA08BA2046B37900DD23FC /* ContentPrivacySettings.swift */; }; + D0FA08BC2046B37900DD23FC /* ContentPrivacySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA08BA2046B37900DD23FC /* ContentPrivacySettings.swift */; }; D0FA0ABD1E76C908005BB9B7 /* TwoStepVerification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA0ABC1E76C908005BB9B7 /* TwoStepVerification.swift */; }; D0FA35051EA6135D00E56FFA /* CacheStorageSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA35041EA6135D00E56FFA /* CacheStorageSettings.swift */; }; D0FA35061EA6135D00E56FFA /* CacheStorageSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA35041EA6135D00E56FFA /* CacheStorageSettings.swift */; }; @@ -957,6 +959,7 @@ D0F7AB2E1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplyMarkupMessageAttribute.swift; sourceTree = ""; }; D0F8C39C20178B9B00236FC5 /* GroupFeedPeers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupFeedPeers.swift; sourceTree = ""; }; D0F8C39F2017AF2700236FC5 /* GlobalTelegramCoreConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalTelegramCoreConfiguration.swift; sourceTree = ""; }; + D0FA08BA2046B37900DD23FC /* ContentPrivacySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPrivacySettings.swift; sourceTree = ""; }; D0FA0ABC1E76C908005BB9B7 /* TwoStepVerification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TwoStepVerification.swift; sourceTree = ""; }; D0FA35041EA6135D00E56FFA /* CacheStorageSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheStorageSettings.swift; sourceTree = ""; }; D0FA35071EA632E400E56FFA /* CollectCacheUsageStats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectCacheUsageStats.swift; sourceTree = ""; }; @@ -1028,6 +1031,7 @@ D01C7ED51EF5E468008305F1 /* ProxySettings.swift */, D0B167221F9F972E00976B40 /* LoggingSettings.swift */, D0AF32301FACEDEC0097362B /* CoreSettings.swift */, + D0FA08BA2046B37900DD23FC /* ContentPrivacySettings.swift */, ); name = Settings; sourceTree = ""; @@ -2079,6 +2083,7 @@ D0B85AC51F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift in Sources */, D0B843C31DA7FF30005F29E1 /* NBPhoneMetaDataGenerator.m in Sources */, C2366C861E4F403C0097CCFF /* AddressNames.swift in Sources */, + D0FA08BB2046B37900DD23FC /* ContentPrivacySettings.swift in Sources */, D0F8C39D20178B9B00236FC5 /* GroupFeedPeers.swift in Sources */, D0B843C11DA7FF30005F29E1 /* NBPhoneMetaData.m in Sources */, D0528E601E65B94E00E2FEF5 /* SingleMessageView.swift in Sources */, @@ -2298,6 +2303,7 @@ C210DD631FBDB90800F673D8 /* SourceReferenceMessageAttribute.swift in Sources */, D0B8440F1DAB91CD005F29E1 /* Either.swift in Sources */, D0DC35511DE36908000195EB /* RequestChatContextResults.swift in Sources */, + D0FA08BC2046B37900DD23FC /* ContentPrivacySettings.swift in Sources */, D08CAA8D1ED81EDF0000FDA8 /* Localizations.swift in Sources */, D0F7B1EC1E045C87007EB8A5 /* SearchPeers.swift in Sources */, D001F3EF1E128A1C007A8C60 /* AccountIntermediateState.swift in Sources */, diff --git a/TelegramCore/Account.swift b/TelegramCore/Account.swift index 741c094565..4c9807e79c 100644 --- a/TelegramCore/Account.swift +++ b/TelegramCore/Account.swift @@ -253,6 +253,7 @@ private var declaredEncodables: Void = { declareEncodable(LoggingSettings.self, f: { LoggingSettings(decoder: $0) }) declareEncodable(CachedLocalizationInfos.self, f: { CachedLocalizationInfos(decoder: $0) }) declareEncodable(SynchronizeGroupedPeersOperation.self, f: { SynchronizeGroupedPeersOperation(decoder: $0) }) + declareEncodable(ContentPrivacySettings.self, f: { ContentPrivacySettings(decoder: $0) }) return }() diff --git a/TelegramCore/AccountStateManagementUtils.swift b/TelegramCore/AccountStateManagementUtils.swift index 984f52d4d6..98c850214d 100644 --- a/TelegramCore/AccountStateManagementUtils.swift +++ b/TelegramCore/AccountStateManagementUtils.swift @@ -701,7 +701,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState, case let .webPageEmpty(id): updatedState.updateMedia(MediaId(namespace: Namespaces.Media.CloudWebpage, id: id), media: nil) default: - if let webpage = telegramMediaWebpageFromApiWebpage(apiWebpage) { + if let webpage = telegramMediaWebpageFromApiWebpage(apiWebpage, url: nil) { updatedState.updateMedia(webpage.webpageId, media: webpage) } } @@ -838,7 +838,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState, case let .webPageEmpty(id): updatedState.updateMedia(MediaId(namespace: Namespaces.Media.CloudWebpage, id: id), media: nil) default: - if let webpage = telegramMediaWebpageFromApiWebpage(apiWebpage) { + if let webpage = telegramMediaWebpageFromApiWebpage(apiWebpage, url: nil) { updatedState.updateMedia(webpage.webpageId, media: webpage) } } @@ -1539,7 +1539,7 @@ private func pollChannel(_ account: Account, peer: Peer, state: AccountMutableSt case let .webPageEmpty(id): updatedState.updateMedia(MediaId(namespace: Namespaces.Media.CloudWebpage, id: id), media: nil) default: - if let webpage = telegramMediaWebpageFromApiWebpage(apiWebpage) { + if let webpage = telegramMediaWebpageFromApiWebpage(apiWebpage, url: nil) { updatedState.updateMedia(webpage.webpageId, media: webpage) } } @@ -1975,7 +1975,7 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, modifier: Modif peerIdsWithAddedSecretMessages.insert(peerId) } case let .ReadSecretOutbox(peerId, maxTimestamp, actionTimestamp): - applyOutgoingReadMaxIndex(modifier: modifier, index: MessageIndex.upperBound(peerId: peerId, timestamp: maxTimestamp, namespace: Namespaces.Message.Local), beginAt: actionTimestamp) + applyOutgoingReadMaxIndex(modifier: modifier, index: MessageIndex.upperBound(peerId: peerId, timestamp: maxTimestamp, namespace: Namespaces.Message.Local), beginCountdownAt: actionTimestamp) case let .AddPeerInputActivity(chatPeerId, peerId, activity): if let peerId = peerId { if updatedTypingActivities[chatPeerId] == nil { diff --git a/TelegramCore/ApplyMaxReadIndexInteractively.swift b/TelegramCore/ApplyMaxReadIndexInteractively.swift index f59b647ba1..6cd904a238 100644 --- a/TelegramCore/ApplyMaxReadIndexInteractively.swift +++ b/TelegramCore/ApplyMaxReadIndexInteractively.swift @@ -44,32 +44,66 @@ public func applyMaxReadIndexInteractively(postbox: Postbox, network: Network, s } } -func applyOutgoingReadMaxIndex(modifier: Modifier, index: MessageIndex, beginAt timestamp: Int32) { +func applyOutgoingReadMaxIndex(modifier: Modifier, index: MessageIndex, beginCountdownAt timestamp: Int32) { let messageIds = modifier.applyOutgoingReadMaxIndex(index) if index.id.peerId.namespace == Namespaces.Peer.SecretChat { for id in messageIds { - if let message = modifier.getMessage(id), !message.flags.contains(.Incoming) { - for attribute in message.attributes { - if let attribute = attribute as? AutoremoveTimeoutMessageAttribute { - if (attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0) && !message.containsSecretMedia { - modifier.updateMessage(message.id, update: { currentMessage in - var storeForwardInfo: StoreMessageForwardInfo? - if let forwardInfo = currentMessage.forwardInfo { - storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) + applySecretOutgoingMessageReadActions(modifier: modifier, id: id, beginCountdownAt: timestamp) + } + } +} + +func maybeReadSecretOutgoingMessage(modifier: Modifier, index: MessageIndex) { + guard index.id.peerId.namespace == Namespaces.Peer.SecretChat else { + assertionFailure() + return + } + guard index.id.namespace == Namespaces.Message.Local else { + assertionFailure() + return + } + + guard let combinedState = modifier.getCombinedPeerReadState(index.id.peerId) else { + return + } + + if combinedState.isOutgoingMessageIndexRead(index) { + applySecretOutgoingMessageReadActions(modifier: modifier, id: index.id, beginCountdownAt: index.timestamp) + } +} + +func applySecretOutgoingMessageReadActions(modifier: Modifier, id: MessageId, beginCountdownAt timestamp: Int32) { + guard id.peerId.namespace == Namespaces.Peer.SecretChat else { + assertionFailure() + return + } + guard id.namespace == Namespaces.Message.Local else { + assertionFailure() + return + } + + if let message = modifier.getMessage(id), !message.flags.contains(.Incoming) { + if message.flags.intersection([.Unsent, .Sending, .Failed]).isEmpty { + for attribute in message.attributes { + if let attribute = attribute as? AutoremoveTimeoutMessageAttribute { + if (attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0) && !message.containsSecretMedia { + modifier.updateMessage(message.id, update: { currentMessage in + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) + } + let updatedAttributes = currentMessage.attributes.map({ currentAttribute -> MessageAttribute in + if let currentAttribute = currentAttribute as? AutoremoveTimeoutMessageAttribute { + return AutoremoveTimeoutMessageAttribute(timeout: currentAttribute.timeout, countdownBeginTime: timestamp) + } else { + return currentAttribute } - let updatedAttributes = currentMessage.attributes.map({ currentAttribute -> MessageAttribute in - if let currentAttribute = currentAttribute as? AutoremoveTimeoutMessageAttribute { - return AutoremoveTimeoutMessageAttribute(timeout: currentAttribute.timeout, countdownBeginTime: timestamp) - } else { - return currentAttribute - } - }) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) }) - modifier.addTimestampBasedMessageAttribute(tag: 0, timestamp: timestamp + attribute.timeout, messageId: id) - } - break + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) + }) + modifier.addTimestampBasedMessageAttribute(tag: 0, timestamp: timestamp + attribute.timeout, messageId: id) } + break } } } diff --git a/TelegramCore/ApplyUpdateMessage.swift b/TelegramCore/ApplyUpdateMessage.swift index caae48fcc4..7afcac2920 100644 --- a/TelegramCore/ApplyUpdateMessage.swift +++ b/TelegramCore/ApplyUpdateMessage.swift @@ -55,6 +55,10 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes var sentStickers: [TelegramMediaFile] = [] var sentGifs: [TelegramMediaFile] = [] + if let updatedTimestamp = updatedTimestamp { + modifier.offsetPendingMessagesTimestamps(lowerBound: message.id, excludeIds: Set([message.id]), timestamp: updatedTimestamp) + } + modifier.updateMessage(message.id, update: { currentMessage in let updatedId: MessageId if let messageId = messageId { @@ -140,9 +144,6 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes return .update(StoreMessage(id: updatedId, globallyUniqueId: nil, groupingKey: currentMessage.groupingKey, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media)) }) - if let updatedTimestamp = updatedTimestamp { - modifier.offsetPendingMessagesTimestamps(lowerBound: message.id, timestamp: updatedTimestamp) - } for file in sentStickers { modifier.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 20) } @@ -198,6 +199,10 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage var updatedGroupingKey: Int64? + if let latestIndex = mapping.last?.1 { + modifier.offsetPendingMessagesTimestamps(lowerBound: latestIndex.id, excludeIds: Set(mapping.map { $0.0.id }), timestamp: latestIndex.timestamp) + } + for (message, _, updatedMessage) in mapping { modifier.updateMessage(message.id, update: { currentMessage in let updatedId: MessageId @@ -259,10 +264,6 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage modifier.updateMessageGroupingKeysAtomically(mapping.map { $0.1.id }, groupingKey: updatedGroupingKey) } - if let latestIndex = mapping.last?.1 { - modifier.offsetPendingMessagesTimestamps(lowerBound: latestIndex.id, timestamp: latestIndex.timestamp) - } - for file in sentStickers { modifier.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 20) } diff --git a/TelegramCore/CallSessionManager.swift b/TelegramCore/CallSessionManager.swift index 475c75845e..fd79f45b28 100644 --- a/TelegramCore/CallSessionManager.swift +++ b/TelegramCore/CallSessionManager.swift @@ -179,6 +179,8 @@ private final class CallSessionContext { var state: CallSessionInternalState let subscribers = Bag<(CallSession) -> Void>() + let acknowledgeIncomingCallDisposable = MetaDisposable() + var isEmpty: Bool { if case .terminated = self.state { return self.subscribers.isEmpty @@ -192,6 +194,10 @@ private final class CallSessionContext { self.isOutgoing = isOutgoing self.state = state } + + deinit { + self.acknowledgeIncomingCallDisposable.dispose() + } } private final class CallSessionManagerContext { @@ -304,7 +310,9 @@ private final class CallSessionManagerContext { if randomStatus == 0 { let internalId = CallSessionInternalId() - self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: false, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b)) + let context = CallSessionContext(peerId: peerId, isOutgoing: false, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b)) + self.contexts[internalId] = context + context.acknowledgeIncomingCallDisposable.set(self.network.request(Api.functions.phone.receivedCall(peer: .inputPhoneCall(id: stableId, accessHash: accessHash))).start()) self.contextIdByStableId[stableId] = internalId self.contextUpdated(internalId: internalId) self.ringingStatesUpdated() diff --git a/TelegramCore/ContentPrivacySettings.swift b/TelegramCore/ContentPrivacySettings.swift new file mode 100644 index 0000000000..84a3985ad9 --- /dev/null +++ b/TelegramCore/ContentPrivacySettings.swift @@ -0,0 +1,66 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac + import MtProtoKitMac +#else + import Postbox + import SwiftSignalKit + import MtProtoKitDynamic +#endif + +public final class ContentPrivacySettings: PreferencesEntry, Equatable { + public let enableSecretChatWebpagePreviews: Bool? + + public static var defaultSettings = ContentPrivacySettings(enableSecretChatWebpagePreviews: nil) + + public init(enableSecretChatWebpagePreviews: Bool?) { + self.enableSecretChatWebpagePreviews = enableSecretChatWebpagePreviews + } + + public init(decoder: PostboxDecoder) { + self.enableSecretChatWebpagePreviews = decoder.decodeOptionalInt32ForKey("enableSecretChatWebpagePreviews").flatMap { $0 != 0 } + } + + public func encode(_ encoder: PostboxEncoder) { + if let enableSecretChatWebpagePreviews = self.enableSecretChatWebpagePreviews { + encoder.encodeInt32(enableSecretChatWebpagePreviews ? 1 : 0, forKey: "enableSecretChatWebpagePreviews") + } else { + encoder.encodeNil(forKey: "enableSecretChatWebpagePreviews") + } + } + + public func withUpdatedEnableSecretChatWebpagePreviews(_ enableSecretChatWebpagePreviews: Bool) -> ContentPrivacySettings { + return ContentPrivacySettings(enableSecretChatWebpagePreviews: enableSecretChatWebpagePreviews) + } + + public func isEqual(to: PreferencesEntry) -> Bool { + guard let to = to as? ContentPrivacySettings else { + return false + } + + return self == to + } + + public static func ==(lhs: ContentPrivacySettings, rhs: ContentPrivacySettings) -> Bool { + if lhs.enableSecretChatWebpagePreviews != rhs.enableSecretChatWebpagePreviews { + return false + } + return true + } +} + +public func updateContentPrivacySettings(postbox: Postbox, _ f: @escaping (ContentPrivacySettings) -> ContentPrivacySettings) -> Signal { + return postbox.modify { modifier -> Void in + var updated: ContentPrivacySettings? + modifier.updatePreferencesEntry(key: PreferencesKeys.contentPrivacySettings, { current in + if let current = current as? ContentPrivacySettings { + updated = f(current) + return updated + } else { + updated = f(ContentPrivacySettings.defaultSettings) + return updated + } + }) + } +} diff --git a/TelegramCore/InlineBotMessageAttribute.swift b/TelegramCore/InlineBotMessageAttribute.swift index a334ab64c7..c49f186678 100644 --- a/TelegramCore/InlineBotMessageAttribute.swift +++ b/TelegramCore/InlineBotMessageAttribute.swift @@ -6,21 +6,41 @@ import Foundation #endif public class InlineBotMessageAttribute: MessageAttribute { - public let peerId: PeerId + public let peerId: PeerId? + public let title: String? public var associatedPeerIds: [PeerId] { - return [self.peerId] + if let peerId = self.peerId { + return [peerId] + } else { + return [] + } } - init(peerId: PeerId) { + init(peerId: PeerId?, title: String?) { self.peerId = peerId + self.title = title } required public init(decoder: PostboxDecoder) { - self.peerId = PeerId(decoder.decodeInt64ForKey("i", orElse: 0)) + if let peerId = decoder.decodeOptionalInt64ForKey("i") { + self.peerId = PeerId(peerId) + } else { + self.peerId = nil + } + self.title = decoder.decodeOptionalStringForKey("t") } public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt64(self.peerId.toInt64(), forKey: "i") + if let peerId = self.peerId { + encoder.encodeInt64(peerId.toInt64(), forKey: "i") + } else { + encoder.encodeNil(forKey: "i") + } + if let title = self.title { + encoder.encodeString(title, forKey: "t") + } else { + encoder.encodeNil(forKey: "t") + } } } diff --git a/TelegramCore/ManagedSecretChatOutgoingOperations.swift b/TelegramCore/ManagedSecretChatOutgoingOperations.swift index 8618b805ec..e3b4f46951 100644 --- a/TelegramCore/ManagedSecretChatOutgoingOperations.swift +++ b/TelegramCore/ManagedSecretChatOutgoingOperations.swift @@ -517,7 +517,46 @@ private func decryptedAttributes73(_ attributes: [TelegramMediaFileAttribute]) - return result } -private func boxedDecryptedMessage(message: Message, globallyUniqueId: Int64, uploadedFile: SecretChatOutgoingFile?, layer: SecretChatLayer) -> BoxedDecryptedMessage { +private func decryptedEntities73(_ entities: [MessageTextEntity]?) -> [SecretApi73.MessageEntity]? { + guard let entities = entities else { + return nil + } + + var result: [SecretApi73.MessageEntity] = [] + for entity in entities { + switch entity.type { + case .Unknown: + break + case .Mention: + result.append(.messageEntityMention(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count))) + case .Hashtag: + result.append(.messageEntityHashtag(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count))) + case .BotCommand: + result.append(.messageEntityBotCommand(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count))) + case .Url: + result.append(.messageEntityUrl(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count))) + case .Email: + result.append(.messageEntityEmail(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count))) + case .Bold: + result.append(.messageEntityBold(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count))) + case .Italic: + result.append(.messageEntityItalic(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count))) + case .Code: + result.append(.messageEntityCode(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count))) + case .Pre: + result.append(.messageEntityPre(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count), language: "")) + case let .TextUrl(url): + result.append(.messageEntityTextUrl(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count), url: url)) + case .TextMention: + break + case .PhoneNumber: + break + } + } + return result +} + +private func boxedDecryptedMessage(modifier: Modifier, message: Message, globallyUniqueId: Int64, uploadedFile: SecretChatOutgoingFile?, thumbnailData: [MediaId: Data], layer: SecretChatLayer) -> BoxedDecryptedMessage { let media: Media? = message.media.first var messageAutoremoveTimeout: Int32 = 0 var replyGlobalId: Int64? = nil @@ -532,37 +571,83 @@ private func boxedDecryptedMessage(message: Message, globallyUniqueId: Int64, up } } + var viaBotName: String? + var entities: [MessageTextEntity]? + for attribute in message.attributes { if let attribute = attribute as? AutoremoveTimeoutMessageAttribute { messageAutoremoveTimeout = attribute.timeout - break + } else if let attribute = attribute as? InlineBotMessageAttribute { + if let title = attribute.title { + viaBotName = title + } else if let peerId = attribute.peerId, let peer = modifier.getPeer(peerId), let addressName = peer.addressName { + viaBotName = addressName + } + } else if let attribute = attribute as? TextEntitiesMessageAttribute { + entities = attribute.entities } } if let media = media { if let image = media as? TelegramMediaImage, let uploadedFile = uploadedFile, let largestRepresentation = largestImageRepresentation(image.representations) { + let thumbW: Int32 + let thumbH: Int32 + let thumb: Buffer + if let smallestRepresentation = smallestImageRepresentation(image.representations), let data = thumbnailData[image.imageId] { + thumbW = Int32(smallestRepresentation.dimensions.width) + thumbH = Int32(smallestRepresentation.dimensions.height) + thumb = Buffer(data: data) + } else { + thumbW = 90 + thumbH = 90 + thumb = Buffer() + } + switch layer { case .layer8: let randomBytesData = malloc(15)! arc4random_buf(randomBytesData, 15) let randomBytes = Buffer(memory: randomBytesData, size: 15, capacity: 15, freeWhenDone: true) - let decryptedMedia = SecretApi8.DecryptedMessageMedia.decryptedMessageMediaPhoto(thumb: Buffer(), thumbW: 90, thumbH: 90, w: Int32(largestRepresentation.dimensions.width), h: Int32(largestRepresentation.dimensions.height), size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv)) + let decryptedMedia = SecretApi8.DecryptedMessageMedia.decryptedMessageMediaPhoto(thumb: thumb, thumbW: thumbW, thumbH: thumbH, w: Int32(largestRepresentation.dimensions.width), h: Int32(largestRepresentation.dimensions.height), size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv)) return .layer8(.decryptedMessage(randomId: globallyUniqueId, randomBytes: randomBytes, message: message.text, media: decryptedMedia)) case .layer46: - let decryptedMedia = SecretApi46.DecryptedMessageMedia.decryptedMessageMediaPhoto(thumb: Buffer(), thumbW: 90, thumbH: 90, w: Int32(largestRepresentation.dimensions.width), h: Int32(largestRepresentation.dimensions.height), size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), caption: "") + if let _ = viaBotName { + flags |= (1 << 11) + } + let decryptedMedia = SecretApi46.DecryptedMessageMedia.decryptedMessageMediaPhoto(thumb: thumb, thumbW: thumbW, thumbH: thumbH, w: Int32(largestRepresentation.dimensions.width), h: Int32(largestRepresentation.dimensions.height), size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), caption: "") flags |= (1 << 9) - return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId)) + return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId)) case .layer73: - let decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaPhoto(thumb: Buffer(), thumbW: 90, thumbH: 90, w: Int32(largestRepresentation.dimensions.width), h: Int32(largestRepresentation.dimensions.height), size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), caption: "") + if let _ = viaBotName { + flags |= (1 << 11) + } + let decryptedEntites = entities.flatMap(decryptedEntities73) + if let _ = decryptedEntites { + flags |= (1 << 7) + } + let decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaPhoto(thumb: thumb, thumbW: thumbW, thumbH: thumbH, w: Int32(largestRepresentation.dimensions.width), h: Int32(largestRepresentation.dimensions.height), size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), caption: "") flags |= (1 << 9) if message.groupingKey != nil { flags |= (1 << 17) } - return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId, groupedId: message.groupingKey)) + return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey)) } } else if let file = media as? TelegramMediaFile { + let thumbW: Int32 + let thumbH: Int32 + let thumb: Buffer + if let smallestRepresentation = smallestImageRepresentation(file.previewRepresentations), let data = thumbnailData[file.fileId] { + thumbW = Int32(smallestRepresentation.dimensions.width) + thumbH = Int32(smallestRepresentation.dimensions.height) + thumb = Buffer(data: data) + } else { + thumbW = 0 + thumbH = 0 + thumb = Buffer() + } + switch layer { case .layer8: if let uploadedFile = uploadedFile { @@ -570,7 +655,7 @@ private func boxedDecryptedMessage(message: Message, globallyUniqueId: Int64, up arc4random_buf(randomBytesData, 15) let randomBytes = Buffer(memory: randomBytesData, size: 15, capacity: 15, freeWhenDone: true) - let decryptedMedia = SecretApi8.DecryptedMessageMedia.decryptedMessageMediaDocument(thumb: Buffer(), thumbW: 0, thumbH: 0, fileName: file.fileName ?? "file", mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv)) + let decryptedMedia = SecretApi8.DecryptedMessageMedia.decryptedMessageMediaDocument(thumb: thumb, thumbW: thumbW, thumbH: thumbH, fileName: file.fileName ?? "file", mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv)) return .layer8(.decryptedMessage(randomId: globallyUniqueId, randomBytes: randomBytes, message: message.text, media: decryptedMedia)) } @@ -591,17 +676,26 @@ private func boxedDecryptedMessage(message: Message, globallyUniqueId: Int64, up if let voiceDuration = voiceDuration { decryptedMedia = SecretApi46.DecryptedMessageMedia.decryptedMessageMediaAudio(duration: voiceDuration, mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv)) } else { - decryptedMedia = SecretApi46.DecryptedMessageMedia.decryptedMessageMediaDocument(thumb: Buffer(), thumbW: 0, thumbH: 0, mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), attributes: decryptedAttributes46(file.attributes), caption: "") + decryptedMedia = SecretApi46.DecryptedMessageMedia.decryptedMessageMediaDocument(thumb: thumb, thumbW: thumbW, thumbH: thumbH, mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), attributes: decryptedAttributes46(file.attributes), caption: "") } } else { if let resource = file.resource as? CloudDocumentMediaResource, let size = file.size { - decryptedMedia = SecretApi46.DecryptedMessageMedia.decryptedMessageMediaExternalDocument(id: resource.fileId, accessHash: resource.accessHash, date: 0, mimeType: file.mimeType, size: Int32(size), thumb: SecretApi46.PhotoSize.photoSizeEmpty(type: "s"), dcId: Int32(resource.datacenterId), attributes: decryptedAttributes46(file.attributes)) + let thumb: SecretApi46.PhotoSize + if let smallestRepresentation = smallestImageRepresentation(file.previewRepresentations), let thumbResource = smallestRepresentation.resource as? CloudFileMediaResource { + thumb = .photoSize(type: "s", location: .fileLocation(dcId: Int32(thumbResource.datacenterId), volumeId: thumbResource.volumeId, localId: thumbResource.localId, secret: thumbResource.secret), w: Int32(smallestRepresentation.dimensions.width), h: Int32(smallestRepresentation.dimensions.height), size: thumbResource.size.flatMap(Int32.init) ?? 0) + } else { + thumb = SecretApi46.PhotoSize.photoSizeEmpty(type: "s") + } + decryptedMedia = SecretApi46.DecryptedMessageMedia.decryptedMessageMediaExternalDocument(id: resource.fileId, accessHash: resource.accessHash, date: 0, mimeType: file.mimeType, size: Int32(size), thumb: thumb, dcId: Int32(resource.datacenterId), attributes: decryptedAttributes46(file.attributes)) } } if let decryptedMedia = decryptedMedia { + if let _ = viaBotName { + flags |= (1 << 11) + } flags |= (1 << 9) - return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId)) + return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId)) } case .layer73: var decryptedMedia: SecretApi73.DecryptedMessageMedia? @@ -620,19 +714,62 @@ private func boxedDecryptedMessage(message: Message, globallyUniqueId: Int64, up if let voiceDuration = voiceDuration { decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaAudio(duration: voiceDuration, mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv)) } else { - decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaDocument(thumb: Buffer(), thumbW: 0, thumbH: 0, mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), attributes: decryptedAttributes73(file.attributes), caption: "") + decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaDocument(thumb: thumb, thumbW: thumbW, thumbH: thumbH, mimeType: file.mimeType, size: uploadedFile.size, key: Buffer(data: uploadedFile.key.aesKey), iv: Buffer(data: uploadedFile.key.aesIv), attributes: decryptedAttributes73(file.attributes), caption: "") } } else { if let resource = file.resource as? CloudDocumentMediaResource, let size = file.size { - decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaExternalDocument(id: resource.fileId, accessHash: resource.accessHash, date: 0, mimeType: file.mimeType, size: Int32(size), thumb: SecretApi73.PhotoSize.photoSizeEmpty(type: "s"), dcId: Int32(resource.datacenterId), attributes: decryptedAttributes73(file.attributes)) + let thumb: SecretApi73.PhotoSize + if let smallestRepresentation = smallestImageRepresentation(file.previewRepresentations), let thumbResource = smallestRepresentation.resource as? CloudFileMediaResource { + thumb = .photoSize(type: "s", location: .fileLocation(dcId: Int32(thumbResource.datacenterId), volumeId: thumbResource.volumeId, localId: thumbResource.localId, secret: thumbResource.secret), w: Int32(smallestRepresentation.dimensions.width), h: Int32(smallestRepresentation.dimensions.height), size: thumbResource.size.flatMap(Int32.init) ?? 0) + } else { + thumb = SecretApi73.PhotoSize.photoSizeEmpty(type: "s") + } + decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaExternalDocument(id: resource.fileId, accessHash: resource.accessHash, date: 0, mimeType: file.mimeType, size: Int32(size), thumb: thumb, dcId: Int32(resource.datacenterId), attributes: decryptedAttributes73(file.attributes)) } } if let decryptedMedia = decryptedMedia { + if let _ = viaBotName { + flags |= (1 << 11) + } + let decryptedEntites = entities.flatMap(decryptedEntities73) + if let _ = decryptedEntites { + flags |= (1 << 7) + } flags |= (1 << 9) - return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId, groupedId: message.groupingKey)) + return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey)) } } + } else if let webpage = media as? TelegramMediaWebpage { + var url: String? + if case let .Loaded(content) = webpage.content { + url = content.url + } + + if let url = url, !url.isEmpty { + switch layer { + case .layer8: + break + case .layer46: + if let _ = viaBotName { + flags |= (1 << 11) + } + let decryptedMedia = SecretApi46.DecryptedMessageMedia.decryptedMessageMediaWebPage(url: url) + flags |= (1 << 9) + return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId)) + case .layer73: + if let _ = viaBotName { + flags |= (1 << 11) + } + let decryptedEntites = entities.flatMap(decryptedEntities73) + if let _ = decryptedEntites { + flags |= (1 << 7) + } + let decryptedMedia = SecretApi73.DecryptedMessageMedia.decryptedMessageMediaWebPage(url: url) + flags |= (1 << 9) + return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey)) + } + } } } @@ -644,9 +781,19 @@ private func boxedDecryptedMessage(message: Message, globallyUniqueId: Int64, up return .layer8(.decryptedMessage(randomId: globallyUniqueId, randomBytes: randomBytes, message: message.text, media: .decryptedMessageMediaEmpty)) case .layer46: - return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId)) + if let _ = viaBotName { + flags |= (1 << 11) + } + return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId)) case .layer73: - return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: nil, viaBotName: nil, replyToRandomId: replyGlobalId, groupedId: message.groupingKey)) + if let _ = viaBotName { + flags |= (1 << 11) + } + let decryptedEntites = entities.flatMap(decryptedEntities73) + if let _ = decryptedEntites { + flags |= (1 << 7) + } + return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey)) } } @@ -828,21 +975,66 @@ private func replaceOutgoingOperationWithEmptyMessage(modifier: Modifier, peerId } } +private func resourceThumbnailData(mediaBox: MediaBox, resource: MediaResource, mediaId: MediaId) -> Signal<(MediaId, Data)?, NoError> { + return mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) + |> take(1) + |> map { data -> (MediaId, Data)? in + if data.complete, data.size < 1024 * 16, let content = try? Data(contentsOf: URL(fileURLWithPath: data.path)) { + return (mediaId, content) + } else { + return nil + } + } +} + +private func messageWithThumbnailData(mediaBox: MediaBox, message: Message) -> Signal<[MediaId: Data], NoError> { + var signals: [Signal<(MediaId, Data)?, NoError>] = [] + for media in message.media { + if let image = media as? TelegramMediaImage { + if let smallestRepresentation = smallestImageRepresentation(image.representations) { + signals.append(resourceThumbnailData(mediaBox: mediaBox, resource: smallestRepresentation.resource, mediaId: image.imageId)) + } + } else if let file = media as? TelegramMediaFile { + if let smallestRepresentation = smallestImageRepresentation(file.previewRepresentations) { + signals.append(resourceThumbnailData(mediaBox: mediaBox, resource: smallestRepresentation.resource, mediaId: file.fileId)) + } + } + } + return combineLatest(signals) + |> map { values in + var result: [MediaId: Data] = [:] + for value in values { + if let value = value { + result[value.0] = value.1 + } + } + return result + } +} + private func sendMessage(postbox: Postbox, network: Network, messageId: MessageId, file: SecretChatOutgoingFile?, tagLocalIndex: Int32, wasDelivered: Bool, layer: SecretChatLayer) -> Signal { - return postbox.modify { modifier -> Signal in - if let state = modifier.getPeerChatState(messageId.peerId) as? SecretChatState, let peer = modifier.getPeer(messageId.peerId) as? TelegramSecretChat { - if let message = modifier.getMessage(messageId), let globallyUniqueId = message.globallyUniqueId { - let decryptedMessage = boxedDecryptedMessage(message: message, globallyUniqueId: globallyUniqueId, uploadedFile: file, layer: layer) - return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: globallyUniqueId, file: file, asService: wasDelivered, wasDelivered: wasDelivered) - |> mapToSignal { result in - return postbox.modify { modifier -> Void in - if result == nil { - replaceOutgoingOperationWithEmptyMessage(modifier: modifier, peerId: messageId.peerId, tagLocalIndex: tagLocalIndex, globallyUniqueId: globallyUniqueId) - } else { - markOutgoingOperationAsCompleted(modifier: modifier, peerId: messageId.peerId, tagLocalIndex: tagLocalIndex, forceRemove: result == nil) - } - modifier.updateMessage(message.id, update: { currentMessage in - var flags = StoreMessageFlags(currentMessage.flags) + return postbox.modify { modifier -> Signal<[MediaId: Data], NoError> in + if let message = modifier.getMessage(messageId) { + return messageWithThumbnailData(mediaBox: postbox.mediaBox, message: message) + } else { + return .single([:]) + } + } + |> switchToLatest + |> mapToSignal { thumbnailData -> Signal in + return postbox.modify { modifier -> Signal in + if let state = modifier.getPeerChatState(messageId.peerId) as? SecretChatState, let peer = modifier.getPeer(messageId.peerId) as? TelegramSecretChat { + if let message = modifier.getMessage(messageId), let globallyUniqueId = message.globallyUniqueId { + let decryptedMessage = boxedDecryptedMessage(modifier: modifier, message: message, globallyUniqueId: globallyUniqueId, uploadedFile: file, thumbnailData: thumbnailData, layer: layer) + return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: globallyUniqueId, file: file, asService: wasDelivered, wasDelivered: wasDelivered) + |> mapToSignal { result in + return postbox.modify { modifier -> Void in + if result == nil { + replaceOutgoingOperationWithEmptyMessage(modifier: modifier, peerId: messageId.peerId, tagLocalIndex: tagLocalIndex, globallyUniqueId: globallyUniqueId) + } else { + markOutgoingOperationAsCompleted(modifier: modifier, peerId: messageId.peerId, tagLocalIndex: tagLocalIndex, forceRemove: result == nil) + } + var timestamp = message.timestamp if let result = result { switch result { @@ -851,30 +1043,40 @@ private func sendMessage(postbox: Postbox, network: Network, messageId: MessageI case let .sentEncryptedFile(date, _): timestamp = date } - flags.remove(.Unsent) - flags.remove(.Sending) - } else { - flags = [.Failed] - } - var storeForwardInfo: StoreMessageForwardInfo? - if let forwardInfo = currentMessage.forwardInfo { - storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) } - modifier.offsetPendingMessagesTimestamps(lowerBound: currentMessage.id, timestamp: timestamp) + modifier.offsetPendingMessagesTimestamps(lowerBound: message.id, excludeIds: Set([messageId]), timestamp: timestamp) - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) - }) - } + modifier.updateMessage(message.id, update: { currentMessage in + var flags = StoreMessageFlags(currentMessage.flags) + if let _ = result { + flags.remove(.Unsent) + flags.remove(.Sending) + } else { + flags = [.Failed] + } + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) + } + + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + }) + + maybeReadSecretOutgoingMessage(modifier: modifier, index: MessageIndex(id: message.id, timestamp: timestamp)) + } + } + } else { + replaceOutgoingOperationWithEmptyMessage(modifier: modifier, peerId: messageId.peerId, tagLocalIndex: tagLocalIndex, globallyUniqueId: arc4random64()) + modifier.deleteMessages([messageId]) + //assertionFailure() + return .complete() } } else { - assertionFailure() - return .never() + return .complete() } - } else { - return .complete() - } - } |> switchToLatest + } |> switchToLatest + } } private func sendServiceActionMessage(postbox: Postbox, network: Network, peerId: PeerId, action: SecretMessageAction, tagLocalIndex: Int32, wasDelivered: Bool) -> Signal { @@ -890,6 +1092,7 @@ private func sendServiceActionMessage(postbox: Postbox, network: Network, peerId markOutgoingOperationAsCompleted(modifier: modifier, peerId: peerId, tagLocalIndex: tagLocalIndex, forceRemove: result == nil) } if let messageId = action.messageId { + var resultTimestamp: Int32? modifier.updateMessage(messageId, update: { currentMessage in var flags = StoreMessageFlags(currentMessage.flags) var timestamp = currentMessage.timestamp @@ -905,12 +1108,17 @@ private func sendServiceActionMessage(postbox: Postbox, network: Network, peerId } else { flags = [.Failed] } + resultTimestamp = timestamp var storeForwardInfo: StoreMessageForwardInfo? if let forwardInfo = currentMessage.forwardInfo { storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) } return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) }) + + if let resultTimestamp = resultTimestamp { + maybeReadSecretOutgoingMessage(modifier: modifier, index: MessageIndex(id: messageId, timestamp: resultTimestamp)) + } } } } @@ -924,12 +1132,21 @@ private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer: let payload = Buffer() var sequenceInfo: SecretChatOperationSequenceInfo? var maybeParameters: SecretChatEncryptionParameters? + + let mode: SecretChatEncryptionMode + switch decryptedMessage { + case .layer8, .layer46: + mode = .v1 + default: + mode = .v2(role: state.role) + } + switch state.embeddedState { case .terminated, .handshake: break case .basicLayer: if let key = state.keychain.indefinitelyValidKey() { - maybeParameters = SecretChatEncryptionParameters(key: key, mode: .v1) + maybeParameters = SecretChatEncryptionParameters(key: key, mode: mode) } case let .sequenceBasedLayer(sequenceState): let topReceivedOperationIndex: Int32 @@ -940,14 +1157,7 @@ private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer: } let canonicalOperationIndex = sequenceState.canonicalOutgoingOperationIndex(operationIndex) if let key = state.keychain.latestKey(validForSequenceBasedCanonicalIndex: canonicalOperationIndex) { - let mode: SecretChatEncryptionMode - switch sequenceState.layerNegotiationState.activeLayer { - case .layer73: - mode = .v2(role: state.role) - default: - mode = .v1 - } - maybeParameters = SecretChatEncryptionParameters(key: key, mode: .v1) + maybeParameters = SecretChatEncryptionParameters(key: key, mode: mode) } Logger.shared.log("SecretChat", "sending message with index \(canonicalOperationIndex) key \(String(describing: maybeParameters?.key.fingerprint))") sequenceInfo = SecretChatOperationSequenceInfo(topReceivedOperationIndex: topReceivedOperationIndex, operationIndex: canonicalOperationIndex) diff --git a/TelegramCore/Namespaces.swift b/TelegramCore/Namespaces.swift index a955f6c1a6..0b84b4352e 100644 --- a/TelegramCore/Namespaces.swift +++ b/TelegramCore/Namespaces.swift @@ -25,6 +25,7 @@ public struct Namespaces { public static let CloudSecretFile: Int32 = 10 public static let CloudGame: Int32 = 11 public static let CloudInvoice: Int32 = 12 + public static let LocalWebpage: Int32 = 13 } public struct Peer { @@ -132,6 +133,7 @@ private enum PreferencesKeyValues: Int32 { case proxySettings = 5 case loggingSettings = 6 case coreSettings = 7 + case contentPrivacySettings = 8 } public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { @@ -188,4 +190,10 @@ public struct PreferencesKeys { key.setInt32(0, value: PreferencesKeyValues.coreSettings.rawValue) return key }() + + public static let contentPrivacySettings: ValueBoxKey = { + let key = ValueBoxKey(length: 4) + key.setInt32(0, value: PreferencesKeyValues.contentPrivacySettings.rawValue) + return key + }() } diff --git a/TelegramCore/OutgoingMessageWithChatContextResult.swift b/TelegramCore/OutgoingMessageWithChatContextResult.swift index c8444ef20c..f861f6281a 100644 --- a/TelegramCore/OutgoingMessageWithChatContextResult.swift +++ b/TelegramCore/OutgoingMessageWithChatContextResult.swift @@ -12,114 +12,10 @@ private func aspectFitSize(_ size: CGSize, to: CGSize) -> CGSize { return CGSize(width: floor(size.width * scale), height: floor(size.height * scale)) } -/* -if ([result isKindOfClass:[TGBotContextMediaResult class]]) { - TGBotContextMediaResult *concreteResult = (TGBotContextMediaResult *)result; - if ([concreteResult.type isEqualToString:@"game"]) { - TGGameMediaAttachment *gameMedia = [[TGGameMediaAttachment alloc] initWithGameId:0 accessHash:0 shortName:nil title:concreteResult.title gameDescription:concreteResult.resultDescription photo:concreteResult.photo document:concreteResult.document]; - [strongSelf->_companion controllerWantsToSendGame:gameMedia asReplyToMessageId:[strongSelf currentReplyMessageId] botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup]; - [strongSelf->_inputTextPanel.inputField setText:@"" animated:true]; - } else if (concreteResult.document != nil) { - TGDocumentAttributeVideo *video = nil; - bool isAnimated = false; - for (id attribute in concreteResult.document.attributes) { - if ([attribute isKindOfClass:[TGDocumentAttributeVideo class]]) { - video = attribute; - } else if ([attribute isKindOfClass:[TGDocumentAttributeAnimated class]]) { - isAnimated = true; - } - } - - if (video != nil && !isAnimated) { - TGVideoMediaAttachment *videoMedia = [[TGVideoMediaAttachment alloc] init]; - videoMedia = [[TGVideoMediaAttachment alloc] init]; - videoMedia.videoId = concreteResult.document.documentId; - videoMedia.accessHash = concreteResult.document.accessHash; - videoMedia.duration = video.duration; - videoMedia.dimensions = video.size; - videoMedia.thumbnailInfo = concreteResult.document.thumbnailInfo; - TGVideoInfo *videoInfo = [[TGVideoInfo alloc] init]; - [videoInfo addVideoWithQuality:1 url:[[NSString alloc] initWithFormat:@"video:%lld:%lld:%d:%d", videoMedia.videoId, videoMedia.accessHash, concreteResult.document.datacenterId, concreteResult.document.size] size:concreteResult.document.size]; - videoMedia.videoInfo = videoInfo; - [strongSelf->_companion controllerWantsToSendRemoteVideoWithMedia:videoMedia asReplyToMessageId:[strongSelf currentReplyMessageId] text:concreteMessage.caption botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup]; - } else { - [strongSelf->_companion controllerWantsToSendRemoteDocument:concreteResult.document asReplyToMessageId:[strongSelf currentReplyMessageId] text:concreteMessage.caption botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup]; - } - [strongSelf->_inputTextPanel.inputField setText:@"" animated:true]; - } else if (concreteResult.photo != nil) { - [strongSelf->_companion controllerWantsToSendRemoteImage:concreteResult.photo text:concreteMessage.caption asReplyToMessageId:[strongSelf currentReplyMessageId] botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup]; - [strongSelf->_inputTextPanel.inputField setText:@"" animated:true]; - } -} else if ([result isKindOfClass:[TGBotContextExternalResult class]]) { - TGBotContextExternalResult *concreteResult = (TGBotContextExternalResult *)result; - if ([concreteResult.type isEqualToString:@"gif"]) { - TGExternalGifSearchResult *externalGifSearchResult = [[TGExternalGifSearchResult alloc] initWithUrl:concreteResult.url originalUrl:concreteResult.originalUrl thumbnailUrl:concreteResult.thumbUrl size:concreteResult.size]; - id description = [strongSelf->_companion documentDescriptionFromExternalGifSearchResult:externalGifSearchResult text:concreteMessage.caption botContextResult:botContextResult]; - if (description != nil) { - [strongSelf->_companion controllerWantsToSendImagesWithDescriptions:@[description] asReplyToMessageId:[strongSelf currentReplyMessageId] botReplyMarkup:concreteMessage.replyMarkup]; - [strongSelf->_inputTextPanel.inputField setText:@"" animated:true]; - [TGRecentContextBotsSignal addRecentBot:results.userId]; - } - } else if ([concreteResult.type isEqualToString:@"photo"]) { - TGExternalImageSearchResult *externalImageSearchResult = [[TGExternalImageSearchResult alloc] initWithUrl:concreteResult.url originalUrl:concreteResult.originalUrl thumbnailUrl:concreteResult.thumbUrl title:concreteResult.title size:concreteResult.size]; - id description = [strongSelf->_companion imageDescriptionFromExternalImageSearchResult:externalImageSearchResult text:concreteMessage.caption botContextResult:botContextResult]; - if (description != nil) { - [strongSelf->_companion controllerWantsToSendImagesWithDescriptions:@[description] asReplyToMessageId:[strongSelf currentReplyMessageId] botReplyMarkup:concreteMessage.replyMarkup]; - [strongSelf->_inputTextPanel.inputField setText:@"" animated:true]; - [TGRecentContextBotsSignal addRecentBot:results.userId]; - } - } else if ([concreteResult.type isEqualToString:@"audio"] || [concreteResult.type isEqualToString:@"voice"] || [concreteResult.type isEqualToString:@"file"]) { - id description = [strongSelf->_companion documentDescriptionFromBotContextResult:concreteResult text:concreteMessage.caption botContextResult:botContextResult]; - if (description != nil) { - [strongSelf->_companion controllerWantsToSendImagesWithDescriptions:@[description] asReplyToMessageId:[strongSelf currentReplyMessageId] botReplyMarkup:concreteMessage.replyMarkup]; - [strongSelf->_inputTextPanel.inputField setText:@"" animated:true]; - [TGRecentContextBotsSignal addRecentBot:results.userId]; - } - } else { - if (![_companion allowMessageForwarding] && !TGAppDelegateInstance.allowSecretWebpages) { - for (id result in [TGMessage textCheckingResultsForText:concreteMessage.caption highlightMentionsAndTags:false highlightCommands:false entities:nil]) { - if ([result isKindOfClass:[NSTextCheckingResult class]] && ((NSTextCheckingResult *)result).resultType == NSTextCheckingTypeLink) { - [_companion maybeAskForSecretWebpages]; - return; - } - } - } - - [strongSelf->_companion controllerWantsToSendTextMessage:concreteMessage.caption entities:@[] asReplyToMessageId:[strongSelf currentReplyMessageId] withAttachedMessages:[strongSelf currentForwardMessages] disableLinkPreviews:false botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup]; - } -} -} else if ([result.sendMessage isKindOfClass:[TGBotContextResultSendMessageText class]]) { - TGBotContextResultSendMessageText *concreteMessage = (TGBotContextResultSendMessageText *)result.sendMessage; - - if (![_companion allowMessageForwarding] && !TGAppDelegateInstance.allowSecretWebpages) { - for (id result in [TGMessage textCheckingResultsForText:concreteMessage.message highlightMentionsAndTags:false highlightCommands:false entities:nil]) { - if ([result isKindOfClass:[NSTextCheckingResult class]] && ((NSTextCheckingResult *)result).resultType == NSTextCheckingTypeLink) { - [_companion maybeAskForSecretWebpages]; - return; - } - } - } - - [strongSelf->_companion controllerWantsToSendTextMessage:concreteMessage.message entities:concreteMessage.entities asReplyToMessageId:[strongSelf currentReplyMessageId] withAttachedMessages:[strongSelf currentForwardMessages] disableLinkPreviews:false botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup]; -} else if ([result.sendMessage isKindOfClass:[TGBotContextResultSendMessageGeo class]]) { - TGBotContextResultSendMessageGeo *concreteMessage = (TGBotContextResultSendMessageGeo *)result.sendMessage; - [strongSelf->_companion controllerWantsToSendMapWithLatitude:concreteMessage.location.latitude longitude:concreteMessage.location.longitude venue:concreteMessage.location.venue asReplyToMessageId:[strongSelf currentReplyMessageId] botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup]; - [strongSelf->_inputTextPanel.inputField setText:@"" animated:true]; -} else if ([result.sendMessage isKindOfClass:[TGBotContextResultSendMessageContact class]]) { - TGBotContextResultSendMessageContact *concreteMessage = (TGBotContextResultSendMessageContact *)result.sendMessage; - TGUser *contactUser = [[TGUser alloc] init]; - contactUser.firstName = concreteMessage.contact.firstName; - contactUser.lastName = concreteMessage.contact.lastName; - contactUser.phoneNumber = concreteMessage.contact.phoneNumber; - [strongSelf->_companion controllerWantsToSendContact:contactUser asReplyToMessageId:[strongSelf currentReplyMessageId] botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup]; - [strongSelf->_inputTextPanel.inputField setText:@"" animated:true]; -} -}*/ - public func outgoingMessageWithChatContextResult(_ results: ChatContextResultCollection, _ result: ChatContextResult) -> EnqueueMessage? { var attributes: [MessageAttribute] = [] attributes.append(OutgoingChatContextResultMessageAttribute(queryId: results.queryId, id: result.id)) - attributes.append(InlineBotMessageAttribute(peerId: results.botId)) + attributes.append(InlineBotMessageAttribute(peerId: results.botId, title: nil)) switch result.message { case let .auto(caption, entities, replyMarkup): diff --git a/TelegramCore/PendingMessageUploadedContent.swift b/TelegramCore/PendingMessageUploadedContent.swift index 5fe66e32d2..3171528216 100644 --- a/TelegramCore/PendingMessageUploadedContent.swift +++ b/TelegramCore/PendingMessageUploadedContent.swift @@ -40,7 +40,9 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods var autoremoveAttribute: AutoremoveTimeoutMessageAttribute? for attribute in attributes { if let attribute = attribute as? OutgoingChatContextResultMessageAttribute { - contextResult = attribute + if peerId.namespace != Namespaces.Peer.SecretChat { + contextResult = attribute + } } else if let attribute = attribute as? AutoremoveTimeoutMessageAttribute { autoremoveAttribute = attribute } @@ -332,9 +334,14 @@ private enum UploadedMediaTransform { case done(Media?) } +private enum UploadedMediaThumbnailResult { + case file(Api.InputFile) + case none +} + private enum UploadedMediaThumbnail { case pending - case done(Api.InputFile?) + case done(UploadedMediaThumbnailResult) } private func uploadedThumbnail(network: Network, postbox: Postbox, image: TelegramMediaImageRepresentation) -> Signal { @@ -441,27 +448,40 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili return .single(.pending) case let .done(media): if let media = media as? TelegramMediaFile, let smallestThumbnail = smallestImageRepresentation(media.previewRepresentations) { - return uploadedThumbnail(network: network, postbox: postbox, image: smallestThumbnail) - |> mapError { _ -> PendingMessageUploadError in return .generic } - |> map { result in - return .done(result) - } + if peerId.namespace == Namespaces.Peer.SecretChat { + return .single(.done(.none)) + } else { + return uploadedThumbnail(network: network, postbox: postbox, image: smallestThumbnail) + |> mapError { _ -> PendingMessageUploadError in return .generic } + |> map { result in + if let result = result { + return .done(.file(result)) + } else { + return .done(.none) + } + } + } } else { - return .single(.done(nil)) + return .single(.done(.none)) } } }) return combineLatest(upload, thumbnail) - |> mapToSignal { content, media -> Signal in + |> mapToSignal { content, thumbnailResult -> Signal in switch content { case let .progress(progress): return .single(.progress(progress)) case let .inputFile(inputFile): - if case let .done(thumbnail) = media { + if case let .done(thumbnail) = thumbnailResult { var flags: Int32 = 0 - if let _ = thumbnail { + var thumbnailFile: Api.InputFile? + if case let .file(file) = thumbnail { + thumbnailFile = file + } + + if let thumbnailFile = thumbnailFile { flags |= 1 << 2 } @@ -473,10 +493,8 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili } } - //flags |= 1 << 3 - if ttlSeconds != nil { - return .single(.content(.media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds), text))) + return .single(.content(.media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds), text))) } return postbox.modify { modifier -> Api.InputPeer? in @@ -485,7 +503,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili |> mapError { _ -> PendingMessageUploadError in return .generic } |> mapToSignal { inputPeer -> Signal in if let inputPeer = inputPeer { - return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds))) + return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds))) |> mapError { _ -> PendingMessageUploadError in return .generic } |> mapToSignal { result -> Signal in switch result { @@ -506,7 +524,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili return .complete() } case let .inputSecretFile(file, size, key): - if case .done = media { + if case .done = thumbnailResult { return .single(.content(.secretMedia(file, size, key))) } else { return .complete() diff --git a/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift b/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift index f758131336..3bf1c8b1f0 100644 --- a/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift @@ -488,8 +488,6 @@ extension TelegramMediaFileAttribute { self = .Sticker(displayText: alt, packReference: packReference, maskData: nil) case let .documentAttributeVideo(duration, w, h): self = .Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)), flags: []) - default: - return nil } } } @@ -531,9 +529,42 @@ extension TelegramMediaFileAttribute { } } +private func parseEntities(_ entities: [SecretApi46.MessageEntity]?) -> TextEntitiesMessageAttribute { + var result: [MessageTextEntity] = [] + if let entities = entities { + for entity in entities { + switch entity { + case let .messageEntityMention(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Mention)) + case let .messageEntityHashtag(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Hashtag)) + case let .messageEntityBotCommand(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BotCommand)) + case let .messageEntityUrl(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Url)) + case let .messageEntityEmail(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Email)) + case let .messageEntityBold(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Bold)) + case let .messageEntityItalic(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Italic)) + case let .messageEntityCode(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Code)) + case let .messageEntityPre(offset, length, _): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre)) + case let .messageEntityTextUrl(offset, length, url): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextUrl(url: url))) + case .messageEntityUnknown: + break + } + } + } + return TextEntitiesMessageAttribute(entities: result) +} + private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi46.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? { switch apiMessage { - case let .decryptedMessage(flags, randomId, ttl, message, media, entities, viaBotName, replyToRandomId): + case let .decryptedMessage(_, randomId, ttl, message, media, entities, viaBotName, replyToRandomId): var text = message var parsedMedia: [Media] = [] var attributes: [MessageAttribute] = [] @@ -543,6 +574,12 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 attributes.append(AutoremoveTimeoutMessageAttribute(timeout: ttl, countdownBeginTime: nil)) } + attributes.append(parseEntities(entities)) + + if let viaBotName = viaBotName, !viaBotName.isEmpty { + attributes.append(InlineBotMessageAttribute(peerId: nil, title: viaBotName)) + } + if let media = media { switch media { case let .decryptedMessageMediaPhoto(thumb, thumbW, thumbH, w, h, size, key, iv, caption): @@ -590,7 +627,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 text = caption } if let file = file { - var parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)), flags: []), .FileName(fileName: "video.mov")] + let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)), flags: []), .FileName(fileName: "video.mov")] var previewRepresentations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { let resource = LocalFileMediaResource(fileId: arc4random64()) @@ -600,16 +637,47 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes) parsedMedia.append(fileMedia) } - case let .decryptedMessageMediaExternalDocument(id, accessHash, date, mimeType, size, thumb, dcId, attributes): + case let .decryptedMessageMediaExternalDocument(id, accessHash, _, mimeType, size, thumb, dcId, attributes): var parsedAttributes: [TelegramMediaFileAttribute] = [] for attribute in attributes { if let parsedAttribute = TelegramMediaFileAttribute(attribute) { parsedAttributes.append(parsedAttribute) } } - let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size)), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: parsedAttributes) + var previewRepresentations: [TelegramMediaImageRepresentation] = [] + switch thumb { + case let .photoSize(_, location, w, h, size): + switch location { + case let .fileLocation(dcId, volumeId, localId, secret): + previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size)))) + case .fileLocationUnavailable: + break + } + case let .photoCachedSize(_, location, w, h, bytes): + if bytes.size > 0 { + switch location { + case let .fileLocation(dcId, volumeId, localId, secret): + let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: bytes.size) + resources.append((resource, bytes.makeData())) + previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource)) + case .fileLocationUnavailable: + break + } + } + default: + break + } + let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size)), previewRepresentations: previewRepresentations, mimeType: mimeType, size: Int(size), attributes: parsedAttributes) parsedMedia.append(fileMedia) - default: + case let .decryptedMessageMediaWebPage(url): + parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url))) + case let .decryptedMessageMediaGeoPoint(lat, long): + parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil)) + case let .decryptedMessageMediaContact(phoneNumber, firstName, lastName, userId): + parsedMedia.append(TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId))) + case let .decryptedMessageMediaVenue(lat, long, title, address, provider, venueId): + parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, geoPlace: nil, venue: MapVenue(title: title, address: address, provider: provider, id: venueId, type: nil), liveBroadcastingTimeout: nil)) + case .decryptedMessageMediaEmpty: break } } @@ -658,9 +726,42 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } } +private func parseEntities(_ entities: [SecretApi73.MessageEntity]?) -> TextEntitiesMessageAttribute { + var result: [MessageTextEntity] = [] + if let entities = entities { + for entity in entities { + switch entity { + case let .messageEntityMention(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Mention)) + case let .messageEntityHashtag(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Hashtag)) + case let .messageEntityBotCommand(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BotCommand)) + case let .messageEntityUrl(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Url)) + case let .messageEntityEmail(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Email)) + case let .messageEntityBold(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Bold)) + case let .messageEntityItalic(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Italic)) + case let .messageEntityCode(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Code)) + case let .messageEntityPre(offset, length, _): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre)) + case let .messageEntityTextUrl(offset, length, url): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextUrl(url: url))) + case .messageEntityUnknown: + break + } + } + } + return TextEntitiesMessageAttribute(entities: result) +} + private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi73.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? { switch apiMessage { - case let .decryptedMessage(flags, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId): + case let .decryptedMessage(_, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId): var text = message var parsedMedia: [Media] = [] var attributes: [MessageAttribute] = [] @@ -670,6 +771,12 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 attributes.append(AutoremoveTimeoutMessageAttribute(timeout: ttl, countdownBeginTime: nil)) } + attributes.append(parseEntities(entities)) + + if let viaBotName = viaBotName, !viaBotName.isEmpty { + attributes.append(InlineBotMessageAttribute(peerId: nil, title: viaBotName)) + } + if let media = media { switch media { case let .decryptedMessageMediaPhoto(thumb, thumbW, thumbH, w, h, size, key, iv, caption): @@ -717,7 +824,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 text = caption } if let file = file { - var parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)), flags: []), .FileName(fileName: "video.mov")] + let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)), flags: []), .FileName(fileName: "video.mov")] var previewRepresentations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { let resource = LocalFileMediaResource(fileId: arc4random64()) @@ -734,9 +841,40 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 parsedAttributes.append(parsedAttribute) } } + var previewRepresentations: [TelegramMediaImageRepresentation] = [] + switch thumb { + case let .photoSize(_, location, w, h, size): + switch location { + case let .fileLocation(dcId, volumeId, localId, secret): + previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size)))) + case .fileLocationUnavailable: + break + } + case let .photoCachedSize(_, location, w, h, bytes): + if bytes.size > 0 { + switch location { + case let .fileLocation(dcId, volumeId, localId, secret): + let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: bytes.size) + resources.append((resource, bytes.makeData())) + previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource)) + case .fileLocationUnavailable: + break + } + } + default: + break + } let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size)), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: parsedAttributes) parsedMedia.append(fileMedia) - default: + case let .decryptedMessageMediaWebPage(url): + parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url))) + case let .decryptedMessageMediaGeoPoint(lat, long): + parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil)) + case let .decryptedMessageMediaContact(phoneNumber, firstName, lastName, userId): + parsedMedia.append(TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId))) + case let .decryptedMessageMediaVenue(lat, long, title, address, provider, venueId): + parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, geoPlace: nil, venue: MapVenue(title: title, address: address, provider: provider, id: venueId, type: nil), liveBroadcastingTimeout: nil)) + case .decryptedMessageMediaEmpty: break } } @@ -771,7 +909,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) case let .decryptedMessageService(randomId, action): switch action { - case let .decryptedMessageActionDeleteMessages(randomIds): + case .decryptedMessageActionDeleteMessages: return nil case .decryptedMessageActionFlushHistory: return nil diff --git a/TelegramCore/SecretChatLayerNegotiation.swift b/TelegramCore/SecretChatLayerNegotiation.swift index 9b693514a3..f27add1412 100644 --- a/TelegramCore/SecretChatLayerNegotiation.swift +++ b/TelegramCore/SecretChatLayerNegotiation.swift @@ -24,7 +24,7 @@ func secretChatAddReportCurrentLayerSupportOperationAndUpdateRequestedLayer(modi switch state.embeddedState { case .basicLayer: var updatedState = state - updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: .reportLayerSupport(layer: topSupportedLayer.secretChatLayer, actionGloballyUniqueId: arc4random64(), layerSupport: topSupportedLayer.rawValue), state: updatedState) + updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: peerId, operation: .reportLayerSupport(layer: .layer8, actionGloballyUniqueId: arc4random64(), layerSupport: topSupportedLayer.rawValue), state: updatedState) return updatedState case let .sequenceBasedLayer(sequenceState): var updatedState = state diff --git a/TelegramCore/StoreMessage_Telegram.swift b/TelegramCore/StoreMessage_Telegram.swift index 78d4c51261..0207573d9a 100644 --- a/TelegramCore/StoreMessage_Telegram.swift +++ b/TelegramCore/StoreMessage_Telegram.swift @@ -299,7 +299,7 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI return (TelegramMediaExpiredContent(data: .file), nil) } case let .messageMediaWebPage(webpage): - if let mediaWebpage = telegramMediaWebpageFromApiWebpage(webpage) { + if let mediaWebpage = telegramMediaWebpageFromApiWebpage(webpage, url: nil) { return (mediaWebpage, nil) } case .messageMediaUnsupported: @@ -475,7 +475,7 @@ extension StoreMessage { } if let viaBotId = viaBotId { - attributes.append(InlineBotMessageAttribute(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: viaBotId))) + attributes.append(InlineBotMessageAttribute(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: viaBotId), title: nil)) } if let replyToMsgId = replyToMsgId { diff --git a/TelegramCore/TelegramMediaWebpage.swift b/TelegramCore/TelegramMediaWebpage.swift index ff67ea5ea4..b1f4b51cae 100644 --- a/TelegramCore/TelegramMediaWebpage.swift +++ b/TelegramCore/TelegramMediaWebpage.swift @@ -191,7 +191,7 @@ public func ==(lhs: TelegramMediaWebpageLoadedContent, rhs: TelegramMediaWebpage } public enum TelegramMediaWebpageContent { - case Pending(Int32) + case Pending(Int32, String?) case Loaded(TelegramMediaWebpageLoadedContent) } @@ -213,7 +213,7 @@ public final class TelegramMediaWebpage: Media, Equatable { self.webpageId = MediaId(decoder.decodeBytesForKeyNoCopy("i")!) if decoder.decodeInt32ForKey("ct", orElse: 0) == 0 { - self.content = .Pending(decoder.decodeInt32ForKey("pendingDate", orElse: 0)) + self.content = .Pending(decoder.decodeInt32ForKey("pendingDate", orElse: 0), decoder.decodeOptionalStringForKey("pendingUrl")) } else { self.content = .Loaded(TelegramMediaWebpageLoadedContent(decoder: decoder)) } @@ -225,9 +225,14 @@ public final class TelegramMediaWebpage: Media, Equatable { encoder.encodeBytes(buffer, forKey: "i") switch self.content { - case let .Pending(date): + case let .Pending(date, url): encoder.encodeInt32(0, forKey: "ct") encoder.encodeInt32(date, forKey: "pendingDate") + if let url = url { + encoder.encodeString(url, forKey: "pendingUrl") + } else { + encoder.encodeNil(forKey: "pendingUrl") + } case let .Loaded(loadedContent): encoder.encodeInt32(1, forKey: "ct") loadedContent.encode(encoder) @@ -251,10 +256,14 @@ public final class TelegramMediaWebpage: Media, Equatable { } switch lhs.content { - case let .Pending(lhsDate): + case let .Pending(lhsDate, lhsUrl): switch rhs.content { - case let .Pending(rhsDate) where lhsDate == rhsDate: - return true + case let .Pending(rhsDate, rhsUrl): + if lhsDate == rhsDate, lhsUrl == rhsUrl { + return true + } else { + return false + } default: return false } @@ -269,12 +278,12 @@ public final class TelegramMediaWebpage: Media, Equatable { } } -func telegramMediaWebpageFromApiWebpage(_ webpage: Api.WebPage) -> TelegramMediaWebpage? { +func telegramMediaWebpageFromApiWebpage(_ webpage: Api.WebPage, url: String?) -> TelegramMediaWebpage? { switch webpage { case .webPageNotModified: return nil case let .webPagePending(id, date): - return TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.CloudWebpage, id: id), content: .Pending(date)) + return TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.CloudWebpage, id: id), content: .Pending(date, url)) case let .webPage(_, id, url, displayUrl, hash, type, siteName, title, description, photo, embedUrl, embedType, embedWidth, embedHeight, duration, author, document, cachedPage): var embedSize: CGSize? if let embedWidth = embedWidth, let embedHeight = embedHeight { diff --git a/TelegramCore/UpdateSecretChat.swift b/TelegramCore/UpdateSecretChat.swift index 05b52fb56a..7b221c5e07 100644 --- a/TelegramCore/UpdateSecretChat.swift +++ b/TelegramCore/UpdateSecretChat.swift @@ -44,19 +44,9 @@ func updateSecretChat(accountPeerId: PeerId, modifier: Modifier, chat: Api.Encry } var updatedState = currentState.withUpdatedKeychain(SecretChatKeychain(keys: [SecretChatKey(fingerprint: keyFingerprint, key: MemoryBuffer(data: key), validity: .indefinite, useCount: 0)])).withUpdatedEmbeddedState(.basicLayer).withUpdatedKeyFingerprint(SecretChatKeyFingerprint(sha1: SecretChatKeySha1Fingerprint(digest: sha1Digest(key)), sha256: SecretChatKeySha256Fingerprint(digest: sha256Digest(key)))) + + updatedState = secretChatAddReportCurrentLayerSupportOperationAndUpdateRequestedLayer(modifier: modifier, peerId: currentPeer.id, state: updatedState) - var layer: SecretChatLayer? - switch updatedState.embeddedState { - case .terminated, .handshake: - break - case .basicLayer: - layer = .layer8 - case let .sequenceBasedLayer(sequenceState): - layer = sequenceState.layerNegotiationState.activeLayer.secretChatLayer - } - if let layer = layer { - updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: currentPeer.id, operation: .reportLayerSupport(layer: layer, actionGloballyUniqueId: arc4random64(), layerSupport: 46), state: updatedState) - } modifier.setPeerChatState(currentPeer.id, state: updatedState) updatePeers(modifier: modifier, peers: [currentPeer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in return updated diff --git a/TelegramCore/WebpagePreview.swift b/TelegramCore/WebpagePreview.swift index c4cd5e5353..ae5d306e6e 100644 --- a/TelegramCore/WebpagePreview.swift +++ b/TelegramCore/WebpagePreview.swift @@ -21,7 +21,7 @@ public func webpagePreview(account: Account, url: String, webpageId: MediaId? = |> mapToSignal { result -> Signal in switch result { case let .messageMediaWebPage(webpage): - if let media = telegramMediaWebpageFromApiWebpage(webpage) { + if let media = telegramMediaWebpageFromApiWebpage(webpage, url: url) { if case .Loaded = media.content { return .single(media) } else { @@ -45,9 +45,9 @@ public func actualizedWebpage(postbox: Postbox, network: Network, webpage: Teleg return .single(.webPageNotModified) } |> mapToSignal { result -> Signal in - if let updatedWebpage = telegramMediaWebpageFromApiWebpage(result), case .Loaded = updatedWebpage.content, updatedWebpage.webpageId == webpage.webpageId { + if let updatedWebpage = telegramMediaWebpageFromApiWebpage(result, url: nil), case .Loaded = updatedWebpage.content, updatedWebpage.webpageId == webpage.webpageId { return postbox.modify { modifier -> TelegramMediaWebpage in - modifier.updateMedia(updatedWebpage.webpageId, update: updatedWebpage) + modifier.updateMedia(webpage.webpageId, update: updatedWebpage) return updatedWebpage } } else {