mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-17 09:10:30 +00:00
no message
This commit is contained in:
parent
3dc3a8d655
commit
0f42fde3f5
@ -149,7 +149,7 @@ private func generateSecureSecret(network: Network, password: String) -> Signal<
|
||||
}
|
||||
}
|
||||
|
||||
public struct SecureIdAccessContext {
|
||||
public struct SecureIdAccessContext: Equatable {
|
||||
let secret: Data
|
||||
let id: Int64
|
||||
}
|
||||
|
@ -614,10 +614,18 @@ func passwordKDF(password: String, derivation: TwoStepPasswordDerivation, srpSes
|
||||
})
|
||||
}
|
||||
|
||||
if !MTCheckIsSafeB(srpSessionData.B, p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
let B = paddedToLength(what: srpSessionData.B, to: p)
|
||||
let A = paddedToLength(what: MTExp(g, a, p)!, to: p)
|
||||
let u = sha256Digest(A + B)
|
||||
|
||||
if MTIsZero(u) {
|
||||
return nil
|
||||
}
|
||||
|
||||
let pbkdfInnerData = sha256Digest(salt2 + sha256Digest(salt1 + passwordData + salt1) + salt2)
|
||||
|
||||
guard let pbkdfResult = MTPBKDF2(pbkdfInnerData, salt1, iterations) else {
|
||||
@ -631,6 +639,11 @@ func passwordKDF(password: String, derivation: TwoStepPasswordDerivation, srpSes
|
||||
let k = sha256Digest(p + paddedToLength(what: g, to: p))
|
||||
|
||||
let s1 = MTModSub(B, MTModMul(k, gx, p)!, p)!
|
||||
|
||||
if !MTCheckIsSafeGAOrB(s1, p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
let s2 = MTAdd(a, MTMul(u, x)!)!
|
||||
let S = MTExp(s1, s2, p)!
|
||||
let K = sha256Digest(paddedToLength(what: S, to: p))
|
||||
|
@ -12,7 +12,6 @@ import Foundation
|
||||
final class AccountInitialState {
|
||||
let state: AuthorizedAccountState.State
|
||||
let peerIds: Set<PeerId>
|
||||
let messageIds: Set<MessageId>
|
||||
let chatStates: [PeerId: PeerChatState]
|
||||
let peerNotificationSettings: [PeerId: PeerNotificationSettings]
|
||||
let peerIdsWithNewMessages: Set<PeerId>
|
||||
@ -20,10 +19,9 @@ final class AccountInitialState {
|
||||
let cloudReadStates: [PeerId: PeerReadState]
|
||||
let channelsToPollExplicitely: Set<PeerId>
|
||||
|
||||
init(state: AuthorizedAccountState.State, peerIds: Set<PeerId>, messageIds: Set<MessageId>, peerIdsWithNewMessages: Set<PeerId>, chatStates: [PeerId: PeerChatState], peerNotificationSettings: [PeerId: PeerNotificationSettings], locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]], cloudReadStates: [PeerId: PeerReadState], channelsToPollExplicitely: Set<PeerId>) {
|
||||
init(state: AuthorizedAccountState.State, peerIds: Set<PeerId>, peerIdsWithNewMessages: Set<PeerId>, chatStates: [PeerId: PeerChatState], peerNotificationSettings: [PeerId: PeerNotificationSettings], locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]], cloudReadStates: [PeerId: PeerReadState], channelsToPollExplicitely: Set<PeerId>) {
|
||||
self.state = state
|
||||
self.peerIds = peerIds
|
||||
self.messageIds = messageIds
|
||||
self.chatStates = chatStates
|
||||
self.peerIdsWithNewMessages = peerIdsWithNewMessages
|
||||
self.peerNotificationSettings = peerNotificationSettings
|
||||
@ -102,6 +100,7 @@ struct AccountMutableState {
|
||||
var peers: [PeerId: Peer]
|
||||
var chatStates: [PeerId: PeerChatState]
|
||||
var peerNotificationSettings: [PeerId: PeerNotificationSettings]
|
||||
var referencedMessageIds: Set<MessageId>
|
||||
var storedMessages: Set<MessageId>
|
||||
var readInboxMaxIds: [PeerId: MessageId]
|
||||
var namespacesWithHolesFromPreviousState: [PeerId: Set<MessageId.Namespace>]
|
||||
@ -113,10 +112,11 @@ struct AccountMutableState {
|
||||
|
||||
var preCachedResources: [(MediaResource, Data)] = []
|
||||
|
||||
init(initialState: AccountInitialState, initialPeers: [PeerId: Peer], initialStoredMessages: Set<MessageId>, initialReadInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set<MessageIndex>]) {
|
||||
init(initialState: AccountInitialState, initialPeers: [PeerId: Peer], initialReferencedMessageIds: Set<MessageId>, initialStoredMessages: Set<MessageId>, initialReadInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set<MessageIndex>]) {
|
||||
self.initialState = initialState
|
||||
self.state = initialState.state
|
||||
self.peers = initialPeers
|
||||
self.referencedMessageIds = initialReferencedMessageIds
|
||||
self.storedMessages = initialStoredMessages
|
||||
self.readInboxMaxIds = initialReadInboxMaxIds
|
||||
self.chatStates = initialState.chatStates
|
||||
@ -126,12 +126,13 @@ struct AccountMutableState {
|
||||
self.namespacesWithHolesFromPreviousState = [:]
|
||||
}
|
||||
|
||||
init(initialState: AccountInitialState, operations: [AccountStateMutationOperation], state: AuthorizedAccountState.State, peers: [PeerId: Peer], chatStates: [PeerId: PeerChatState], peerNotificationSettings: [PeerId: PeerNotificationSettings], storedMessages: Set<MessageId>, readInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set<MessageIndex>], namespacesWithHolesFromPreviousState: [PeerId: Set<MessageId.Namespace>], displayAlerts: [String], branchOperationIndex: Int) {
|
||||
init(initialState: AccountInitialState, operations: [AccountStateMutationOperation], state: AuthorizedAccountState.State, peers: [PeerId: Peer], chatStates: [PeerId: PeerChatState], peerNotificationSettings: [PeerId: PeerNotificationSettings], referencedMessageIds: Set<MessageId>, storedMessages: Set<MessageId>, readInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set<MessageIndex>], namespacesWithHolesFromPreviousState: [PeerId: Set<MessageId.Namespace>], displayAlerts: [String], branchOperationIndex: Int) {
|
||||
self.initialState = initialState
|
||||
self.operations = operations
|
||||
self.state = state
|
||||
self.peers = peers
|
||||
self.chatStates = chatStates
|
||||
self.referencedMessageIds = referencedMessageIds
|
||||
self.storedMessages = storedMessages
|
||||
self.peerNotificationSettings = peerNotificationSettings
|
||||
self.readInboxMaxIds = readInboxMaxIds
|
||||
@ -142,10 +143,11 @@ struct AccountMutableState {
|
||||
}
|
||||
|
||||
func branch() -> AccountMutableState {
|
||||
return AccountMutableState(initialState: self.initialState, operations: self.operations, state: self.state, peers: self.peers, chatStates: self.chatStates, peerNotificationSettings: self.peerNotificationSettings, storedMessages: self.storedMessages, readInboxMaxIds: self.readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: self.storedMessagesByPeerIdAndTimestamp, namespacesWithHolesFromPreviousState: self.namespacesWithHolesFromPreviousState, displayAlerts: self.displayAlerts, branchOperationIndex: self.operations.count)
|
||||
return AccountMutableState(initialState: self.initialState, operations: self.operations, state: self.state, peers: self.peers, chatStates: self.chatStates, peerNotificationSettings: self.peerNotificationSettings, referencedMessageIds: self.referencedMessageIds, storedMessages: self.storedMessages, readInboxMaxIds: self.readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: self.storedMessagesByPeerIdAndTimestamp, namespacesWithHolesFromPreviousState: self.namespacesWithHolesFromPreviousState, displayAlerts: self.displayAlerts, branchOperationIndex: self.operations.count)
|
||||
}
|
||||
|
||||
mutating func merge(_ other: AccountMutableState) {
|
||||
self.referencedMessageIds.formUnion(other.referencedMessageIds)
|
||||
for i in other.branchOperationIndex ..< other.operations.count {
|
||||
self.addOperation(other.operations[i])
|
||||
}
|
||||
@ -340,6 +342,12 @@ struct AccountMutableState {
|
||||
if case let .Id(id) = message.id {
|
||||
self.storedMessages.insert(id)
|
||||
}
|
||||
inner: for attribute in message.attributes {
|
||||
if let attribute = attribute as? ReplyMessageAttribute {
|
||||
self.referencedMessageIds.insert(attribute.messageId)
|
||||
break inner
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .UpdateState(state):
|
||||
self.state = state
|
||||
|
@ -378,7 +378,7 @@ private func initialStateWithPeerIds(_ transaction: Transaction, peerIds: Set<Pe
|
||||
}
|
||||
}
|
||||
|
||||
return AccountMutableState(initialState: AccountInitialState(state: (transaction.getState() as? AuthorizedAccountState)!.state!, peerIds: peerIds, messageIds: associatedMessageIds, peerIdsWithNewMessages: peerIdsWithNewMessages, chatStates: chatStates, peerNotificationSettings: peerNotificationSettings, locallyGeneratedMessageTimestamps: locallyGeneratedMessageTimestamps, cloudReadStates: cloudReadStates, channelsToPollExplicitely: channelsToPollExplicitely), initialPeers: peers, initialStoredMessages: storedMessages, initialReadInboxMaxIds: readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: storedMessagesByPeerIdAndTimestamp)
|
||||
return AccountMutableState(initialState: AccountInitialState(state: (transaction.getState() as? AuthorizedAccountState)!.state!, peerIds: peerIds, peerIdsWithNewMessages: peerIdsWithNewMessages, chatStates: chatStates, peerNotificationSettings: peerNotificationSettings, locallyGeneratedMessageTimestamps: locallyGeneratedMessageTimestamps, cloudReadStates: cloudReadStates, channelsToPollExplicitely: channelsToPollExplicitely), initialPeers: peers, initialReferencedMessageIds: associatedMessageIds, initialStoredMessages: storedMessages, initialReadInboxMaxIds: readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: storedMessagesByPeerIdAndTimestamp)
|
||||
}
|
||||
|
||||
func initialStateWithUpdateGroups(_ account: Account, groups: [UpdateGroup]) -> Signal<AccountMutableState, NoError> {
|
||||
@ -1230,21 +1230,21 @@ private func finalStateWithUpdatesAndServerTime(account: Account, state: Account
|
||||
}
|
||||
}
|
||||
return resolveAssociatedMessages(account: account, state: finalState)
|
||||
|> mapToSignal { resultingState -> Signal<AccountFinalState, NoError> in
|
||||
return resolveMissingPeerNotificationSettings(account: account, state: resultingState)
|
||||
|> mapToSignal { resultingState -> Signal<AccountFinalState, NoError> in
|
||||
return resolveMissingPeerNotificationSettings(account: account, state: resultingState)
|
||||
|> mapToSignal { resultingState -> Signal<AccountFinalState, NoError> in
|
||||
return resolveMissingPeerCloudReadStates(account: account, state: resultingState)
|
||||
|> map { resultingState -> AccountFinalState in
|
||||
return AccountFinalState(state: resultingState, shouldPoll: shouldPoll || hadError, incomplete: missingUpdates)
|
||||
}
|
||||
}
|
||||
return resolveMissingPeerCloudReadStates(account: account, state: resultingState)
|
||||
|> map { resultingState -> AccountFinalState in
|
||||
return AccountFinalState(state: resultingState, shouldPoll: shouldPoll || hadError, incomplete: missingUpdates)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func resolveAssociatedMessages(account: Account, state: AccountMutableState) -> Signal<AccountMutableState, NoError> {
|
||||
let missingMessageIds = state.initialState.messageIds.subtracting(state.storedMessages)
|
||||
let missingMessageIds = state.referencedMessageIds.subtracting(state.storedMessages)
|
||||
if missingMessageIds.isEmpty {
|
||||
return .single(state)
|
||||
} else {
|
||||
@ -1402,7 +1402,7 @@ func keepPollingChannel(account: Account, peerId: PeerId, stateManager: AccountS
|
||||
if let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings {
|
||||
peerNotificationSettings[peerId] = notificationSettings
|
||||
}
|
||||
let initialState = AccountMutableState(initialState: AccountInitialState(state: accountState, peerIds: Set(), messageIds: Set(), peerIdsWithNewMessages: Set(), chatStates: chatStates, peerNotificationSettings: peerNotificationSettings, locallyGeneratedMessageTimestamps: [:], cloudReadStates: [:], channelsToPollExplicitely: Set()), initialPeers: initialPeers, initialStoredMessages: Set(), initialReadInboxMaxIds: [:], storedMessagesByPeerIdAndTimestamp: [:])
|
||||
let initialState = AccountMutableState(initialState: AccountInitialState(state: accountState, peerIds: Set(), peerIdsWithNewMessages: Set(), chatStates: chatStates, peerNotificationSettings: peerNotificationSettings, locallyGeneratedMessageTimestamps: [:], cloudReadStates: [:], channelsToPollExplicitely: Set()), initialPeers: initialPeers, initialReferencedMessageIds: Set(), initialStoredMessages: Set(), initialReadInboxMaxIds: [:], storedMessagesByPeerIdAndTimestamp: [:])
|
||||
return pollChannel(account, peer: peer, state: initialState)
|
||||
|> mapToSignal { (finalState, _, timeout) -> Signal<Void, NoError> in
|
||||
return resolveAssociatedMessages(account: account, state: finalState)
|
||||
@ -1441,7 +1441,7 @@ private func resetChannels(_ account: Account, peers: [Peer], state: AccountMuta
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|> map { result -> AccountMutableState in
|
||||
|> mapToSignal { result -> Signal<AccountMutableState, NoError> in
|
||||
var updatedState = state
|
||||
|
||||
var dialogsChats: [Api.Chat] = []
|
||||
@ -1565,7 +1565,10 @@ private func resetChannels(_ account: Account, peers: [Peer], state: AccountMuta
|
||||
|
||||
// TODO: delete messages later than top
|
||||
|
||||
return updatedState
|
||||
return resolveAssociatedMessages(account: account, state: updatedState)
|
||||
|> mapToSignal { resultingState -> Signal<AccountMutableState, NoError> in
|
||||
return .single(resultingState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,8 +89,8 @@ public final class AccountStateManager {
|
||||
return self.isUpdatingValue.get()
|
||||
}
|
||||
|
||||
private let notificationMessagesPipe = ValuePipe<[(Message, PeerGroupId?)]>()
|
||||
public var notificationMessages: Signal<[(Message, PeerGroupId?)], NoError> {
|
||||
private let notificationMessagesPipe = ValuePipe<[([Message], PeerGroupId?)]>()
|
||||
public var notificationMessages: Signal<[([Message], PeerGroupId?)], NoError> {
|
||||
return self.notificationMessagesPipe.signal()
|
||||
}
|
||||
|
||||
@ -532,15 +532,15 @@ public final class AccountStateManager {
|
||||
let _ = self.delayNotificatonsUntil.swap(events.delayNotificatonsUntil)
|
||||
}
|
||||
|
||||
let signal = self.account.postbox.transaction { transaction -> [(Message, PeerGroupId?)] in
|
||||
var messages: [(Message, PeerGroupId?)] = []
|
||||
let signal = self.account.postbox.transaction { transaction -> [([Message], PeerGroupId?)] in
|
||||
var messageList: [([Message], PeerGroupId?)] = []
|
||||
for id in events.addedIncomingMessageIds {
|
||||
let (message, notify, _, _) = messageForNotification(transaction: transaction, id: id, alwaysReturnMessage: false)
|
||||
if let message = message, notify {
|
||||
messages.append((message, transaction.getPeerGroupId(message.id.peerId)))
|
||||
let (messages, notify, _, _) = messagesForNotification(transaction: transaction, id: id, alwaysReturnMessage: false)
|
||||
if !messages.isEmpty && notify {
|
||||
messageList.append((messages, transaction.getPeerGroupId(messages[0].id.peerId)))
|
||||
}
|
||||
}
|
||||
return messages
|
||||
return messageList
|
||||
}
|
||||
|
||||
let _ = (signal
|
||||
@ -789,10 +789,10 @@ public final class AccountStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public func messageForNotification(transaction: Transaction, id: MessageId, alwaysReturnMessage: Bool) -> (message: Message?, notify: Bool, sound: PeerMessageSound, displayContents: Bool) {
|
||||
public func messagesForNotification(transaction: Transaction, id: MessageId, alwaysReturnMessage: Bool) -> (messages: [Message], notify: Bool, sound: PeerMessageSound, displayContents: Bool) {
|
||||
guard let message = transaction.getMessage(id) else {
|
||||
Logger.shared.log("AccountStateManager", "notification message doesn't exist")
|
||||
return (nil, false, .bundledModern(id: 0), false)
|
||||
return ([], false, .bundledModern(id: 0), false)
|
||||
}
|
||||
|
||||
var notify = true
|
||||
@ -864,7 +864,7 @@ public func messageForNotification(transaction: Transaction, id: MessageId, alwa
|
||||
if let channel = message.peers[message.id.peerId] as? TelegramChannel {
|
||||
switch channel.participationStatus {
|
||||
case .kicked, .left:
|
||||
return (nil, false, sound, false)
|
||||
return ([], false, sound, false)
|
||||
case .member:
|
||||
break
|
||||
}
|
||||
@ -883,9 +883,21 @@ public func messageForNotification(transaction: Transaction, id: MessageId, alwa
|
||||
Logger.shared.log("AccountStateManager", "read state for \(id.peerId) is undefined")
|
||||
}
|
||||
|
||||
var resultMessages: [Message] = [message]
|
||||
|
||||
var messageGroup: [Message]?
|
||||
if message.forwardInfo != nil {
|
||||
messageGroup = transaction.getMessageForwardedGroup(message.id)
|
||||
} else if message.groupingKey != nil {
|
||||
messageGroup = transaction.getMessageGroup(message.id)
|
||||
}
|
||||
if let messageGroup = messageGroup {
|
||||
resultMessages.append(contentsOf: messageGroup.filter({ $0.id != message.id }))
|
||||
}
|
||||
|
||||
if notify {
|
||||
return (message, isUnread, sound, displayContents)
|
||||
return (resultMessages, isUnread, sound, displayContents)
|
||||
} else {
|
||||
return (alwaysReturnMessage ? message : nil, false, sound, displayContents)
|
||||
return (alwaysReturnMessage ? resultMessages : [], false, sound, displayContents)
|
||||
}
|
||||
}
|
||||
|
@ -17,246 +17,246 @@ func accountStateReset(postbox: Postbox, network: Network) -> Signal<Void, NoErr
|
||||
|> retryRequest
|
||||
|
||||
return combineLatest(network.request(Api.functions.messages.getDialogs(flags: 0, /*feed*//*feedId: nil,*/ offsetDate: 0, offsetId: 0, offsetPeer: .inputPeerEmpty, limit: 100, hash: 0))
|
||||
|> retryRequest, pinnedChats, state)
|
||||
|> mapToSignal { result, pinnedChats, state -> Signal<Void, NoError> in
|
||||
var dialogsDialogs: [Api.Dialog] = []
|
||||
var dialogsMessages: [Api.Message] = []
|
||||
var dialogsChats: [Api.Chat] = []
|
||||
var dialogsUsers: [Api.User] = []
|
||||
|
||||
var holeExists = false
|
||||
|
||||
switch result {
|
||||
case let .dialogs(dialogs, messages, chats, users):
|
||||
dialogsDialogs = dialogs
|
||||
dialogsMessages = messages
|
||||
dialogsChats = chats
|
||||
dialogsUsers = users
|
||||
case let .dialogsSlice(_, dialogs, messages, chats, users):
|
||||
dialogsDialogs = dialogs
|
||||
dialogsMessages = messages
|
||||
dialogsChats = chats
|
||||
dialogsUsers = users
|
||||
holeExists = true
|
||||
case .dialogsNotModified:
|
||||
dialogsDialogs = []
|
||||
dialogsMessages = []
|
||||
dialogsChats = []
|
||||
dialogsUsers = []
|
||||
}
|
||||
|
||||
let replacePinnedItemIds: [PinnedItemId]
|
||||
switch pinnedChats {
|
||||
case let .peerDialogs(apiDialogs, apiMessages, apiChats, apiUsers, _):
|
||||
dialogsDialogs.append(contentsOf: apiDialogs)
|
||||
dialogsMessages.append(contentsOf: apiMessages)
|
||||
dialogsChats.append(contentsOf: apiChats)
|
||||
dialogsUsers.append(contentsOf: apiUsers)
|
||||
|
||||
var itemIds: [PinnedItemId] = []
|
||||
|
||||
loop: for dialog in apiDialogs {
|
||||
switch dialog {
|
||||
case let .dialog(_, peer, _, _, _, _, _, _, _, _):
|
||||
itemIds.append(.peer(peer.peerId))
|
||||
/*feed*/
|
||||
/*case let .dialogFeed(_, _, _, feedId, _, _, _, _):
|
||||
itemIds.append(.group(PeerGroupId(rawValue: feedId)))
|
||||
continue loop*/
|
||||
}
|
||||
}
|
||||
|> retryRequest, pinnedChats, state)
|
||||
|> mapToSignal { result, pinnedChats, state -> Signal<Void, NoError> in
|
||||
var dialogsDialogs: [Api.Dialog] = []
|
||||
var dialogsMessages: [Api.Message] = []
|
||||
var dialogsChats: [Api.Chat] = []
|
||||
var dialogsUsers: [Api.User] = []
|
||||
|
||||
var holeExists = false
|
||||
|
||||
switch result {
|
||||
case let .dialogs(dialogs, messages, chats, users):
|
||||
dialogsDialogs = dialogs
|
||||
dialogsMessages = messages
|
||||
dialogsChats = chats
|
||||
dialogsUsers = users
|
||||
case let .dialogsSlice(_, dialogs, messages, chats, users):
|
||||
dialogsDialogs = dialogs
|
||||
dialogsMessages = messages
|
||||
dialogsChats = chats
|
||||
dialogsUsers = users
|
||||
holeExists = true
|
||||
case .dialogsNotModified:
|
||||
dialogsDialogs = []
|
||||
dialogsMessages = []
|
||||
dialogsChats = []
|
||||
dialogsUsers = []
|
||||
}
|
||||
|
||||
let replacePinnedItemIds: [PinnedItemId]
|
||||
switch pinnedChats {
|
||||
case let .peerDialogs(apiDialogs, apiMessages, apiChats, apiUsers, _):
|
||||
dialogsDialogs.append(contentsOf: apiDialogs)
|
||||
dialogsMessages.append(contentsOf: apiMessages)
|
||||
dialogsChats.append(contentsOf: apiChats)
|
||||
dialogsUsers.append(contentsOf: apiUsers)
|
||||
|
||||
replacePinnedItemIds = itemIds
|
||||
var itemIds: [PinnedItemId] = []
|
||||
|
||||
loop: for dialog in apiDialogs {
|
||||
switch dialog {
|
||||
case let .dialog(_, peer, _, _, _, _, _, _, _, _):
|
||||
itemIds.append(.peer(peer.peerId))
|
||||
/*feed*/
|
||||
/*case let .dialogFeed(_, _, _, feedId, _, _, _, _):
|
||||
itemIds.append(.group(PeerGroupId(rawValue: feedId)))
|
||||
continue loop*/
|
||||
}
|
||||
}
|
||||
|
||||
var replacementHole: ChatListHole?
|
||||
var storeMessages: [StoreMessage] = []
|
||||
var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]
|
||||
var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:]
|
||||
var chatStates: [PeerId: PeerChatState] = [:]
|
||||
var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
|
||||
replacePinnedItemIds = itemIds
|
||||
}
|
||||
|
||||
var replacementHole: ChatListHole?
|
||||
var storeMessages: [StoreMessage] = []
|
||||
var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]
|
||||
var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:]
|
||||
var chatStates: [PeerId: PeerChatState] = [:]
|
||||
var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
|
||||
|
||||
var topMesageIds: [PeerId: MessageId] = [:]
|
||||
|
||||
loop: for dialog in dialogsDialogs {
|
||||
let apiPeer: Api.Peer
|
||||
let apiReadInboxMaxId: Int32
|
||||
let apiReadOutboxMaxId: Int32
|
||||
let apiTopMessage: Int32
|
||||
let apiUnreadCount: Int32
|
||||
let apiMarkedUnread: Bool
|
||||
let apiUnreadMentionsCount: Int32
|
||||
var apiChannelPts: Int32?
|
||||
let apiNotificationSettings: Api.PeerNotifySettings
|
||||
switch dialog {
|
||||
case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _):
|
||||
apiPeer = peer
|
||||
apiTopMessage = topMessage
|
||||
apiReadInboxMaxId = readInboxMaxId
|
||||
apiReadOutboxMaxId = readOutboxMaxId
|
||||
apiUnreadCount = unreadCount
|
||||
apiMarkedUnread = (flags & (1 << 3)) != 0
|
||||
apiUnreadMentionsCount = unreadMentionsCount
|
||||
apiNotificationSettings = peerNotificationSettings
|
||||
apiChannelPts = pts
|
||||
/*feed*/
|
||||
/*case .dialogFeed:
|
||||
//assertionFailure()
|
||||
continue loop*/
|
||||
}
|
||||
|
||||
var topMesageIds: [PeerId: MessageId] = [:]
|
||||
let peerId: PeerId
|
||||
switch apiPeer {
|
||||
case let .peerUser(userId):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
case let .peerChat(chatId):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)
|
||||
case let .peerChannel(channelId):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||
}
|
||||
|
||||
loop: for dialog in dialogsDialogs {
|
||||
let apiPeer: Api.Peer
|
||||
let apiReadInboxMaxId: Int32
|
||||
let apiReadOutboxMaxId: Int32
|
||||
let apiTopMessage: Int32
|
||||
let apiUnreadCount: Int32
|
||||
let apiMarkedUnread: Bool
|
||||
let apiUnreadMentionsCount: Int32
|
||||
var apiChannelPts: Int32?
|
||||
let apiNotificationSettings: Api.PeerNotifySettings
|
||||
if readStates[peerId] == nil {
|
||||
readStates[peerId] = [:]
|
||||
}
|
||||
readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount, markedUnread: apiMarkedUnread)
|
||||
|
||||
if apiTopMessage != 0 {
|
||||
mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage))
|
||||
topMesageIds[peerId] = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: apiTopMessage)
|
||||
}
|
||||
|
||||
if let apiChannelPts = apiChannelPts {
|
||||
chatStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: apiChannelPts)
|
||||
} else if peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudUser {
|
||||
switch state {
|
||||
case let .state(pts, _, _, _, _):
|
||||
chatStates[peerId] = RegularChatState(invalidatedPts: pts)
|
||||
}
|
||||
}
|
||||
|
||||
notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings)
|
||||
}
|
||||
|
||||
for message in dialogsMessages {
|
||||
if let storeMessage = StoreMessage(apiMessage: message) {
|
||||
storeMessages.append(storeMessage)
|
||||
}
|
||||
}
|
||||
|
||||
if holeExists {
|
||||
for dialog in dialogsDialogs {
|
||||
switch dialog {
|
||||
case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _):
|
||||
apiPeer = peer
|
||||
apiTopMessage = topMessage
|
||||
apiReadInboxMaxId = readInboxMaxId
|
||||
apiReadOutboxMaxId = readOutboxMaxId
|
||||
apiUnreadCount = unreadCount
|
||||
apiMarkedUnread = (flags & (1 << 3)) != 0
|
||||
apiUnreadMentionsCount = unreadMentionsCount
|
||||
apiNotificationSettings = peerNotificationSettings
|
||||
apiChannelPts = pts
|
||||
case let .dialog(flags, peer, topMessage, _, _, _, _, _, _, _):
|
||||
let isPinned = (flags & (1 << 2)) != 0
|
||||
|
||||
if !isPinned {
|
||||
var timestamp: Int32?
|
||||
for message in storeMessages {
|
||||
if case let .Id(id) = message.id, id.id == topMessage {
|
||||
timestamp = message.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
if let timestamp = timestamp {
|
||||
let index = MessageIndex(id: MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: topMessage - 1), timestamp: timestamp)
|
||||
if (replacementHole == nil || replacementHole!.index > index) {
|
||||
replacementHole = ChatListHole(index: index)
|
||||
}
|
||||
}
|
||||
}
|
||||
/*feed*/
|
||||
/*case .dialogFeed:
|
||||
//assertionFailure()
|
||||
continue loop*/
|
||||
}
|
||||
|
||||
let peerId: PeerId
|
||||
switch apiPeer {
|
||||
case let .peerUser(userId):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
case let .peerChat(chatId):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)
|
||||
case let .peerChannel(channelId):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||
}
|
||||
|
||||
if readStates[peerId] == nil {
|
||||
readStates[peerId] = [:]
|
||||
}
|
||||
readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount, markedUnread: apiMarkedUnread)
|
||||
|
||||
if apiTopMessage != 0 {
|
||||
mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage))
|
||||
topMesageIds[peerId] = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: apiTopMessage)
|
||||
}
|
||||
|
||||
if let apiChannelPts = apiChannelPts {
|
||||
chatStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: apiChannelPts)
|
||||
} else if peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudUser {
|
||||
switch state {
|
||||
case let .state(pts, _, _, _, _):
|
||||
chatStates[peerId] = RegularChatState(invalidatedPts: pts)
|
||||
}
|
||||
}
|
||||
|
||||
notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings)
|
||||
}
|
||||
|
||||
for message in dialogsMessages {
|
||||
if let storeMessage = StoreMessage(apiMessage: message) {
|
||||
storeMessages.append(storeMessage)
|
||||
}
|
||||
}
|
||||
|
||||
if holeExists {
|
||||
for dialog in dialogsDialogs {
|
||||
switch dialog {
|
||||
case let .dialog(flags, peer, topMessage, _, _, _, _, _, _, _):
|
||||
let isPinned = (flags & (1 << 2)) != 0
|
||||
|
||||
if !isPinned {
|
||||
var timestamp: Int32?
|
||||
for message in storeMessages {
|
||||
if case let .Id(id) = message.id, id.id == topMessage {
|
||||
timestamp = message.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
if let timestamp = timestamp {
|
||||
let index = MessageIndex(id: MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: topMessage - 1), timestamp: timestamp)
|
||||
if (replacementHole == nil || replacementHole!.index > index) {
|
||||
replacementHole = ChatListHole(index: index)
|
||||
}
|
||||
}
|
||||
}
|
||||
/*feed*/
|
||||
/*case .dialogFeed:
|
||||
//assertionFailure()
|
||||
break*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
for chat in dialogsChats {
|
||||
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(groupOrChannel)
|
||||
}
|
||||
}
|
||||
for user in dialogsUsers {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
|
||||
return postbox.transaction { transaction -> Void in
|
||||
let previousPeerIds = transaction.resetChatList(keepPeerNamespaces: Set([Namespaces.Peer.SecretChat]), replacementHole: replacementHole)
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
transaction.updatePeerPresences(peerPresences)
|
||||
|
||||
transaction.updateCurrentPeerNotificationSettings(notificationSettings)
|
||||
|
||||
var allPeersWithMessages = Set<PeerId>()
|
||||
for message in storeMessages {
|
||||
allPeersWithMessages.insert(message.id.peerId)
|
||||
}
|
||||
|
||||
for (_, messageId) in topMesageIds {
|
||||
if messageId.id > 1 {
|
||||
var skipHole = false
|
||||
if let localTopId = transaction.getTopPeerMessageIndex(peerId: messageId.peerId, namespace: messageId.namespace)?.id {
|
||||
if localTopId >= messageId {
|
||||
skipHole = true
|
||||
}
|
||||
}
|
||||
if !skipHole {
|
||||
transaction.addHole(MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: messageId.id - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = transaction.addMessages(storeMessages, location: .UpperHistoryBlock)
|
||||
|
||||
transaction.resetIncomingReadStates(readStates)
|
||||
|
||||
for (peerId, chatState) in chatStates {
|
||||
if let chatState = chatState as? ChannelState {
|
||||
if let current = transaction.getPeerChatState(peerId) as? ChannelState {
|
||||
transaction.setPeerChatState(peerId, state: current.withUpdatedPts(chatState.pts))
|
||||
} else {
|
||||
transaction.setPeerChatState(peerId, state: chatState)
|
||||
}
|
||||
} else {
|
||||
transaction.setPeerChatState(peerId, state: chatState)
|
||||
}
|
||||
}
|
||||
|
||||
transaction.setPinnedItemIds(replacePinnedItemIds)
|
||||
|
||||
for (peerId, summary) in mentionTagSummaries {
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId)
|
||||
}
|
||||
|
||||
let namespacesWithHoles: [PeerId.Namespace: [MessageId.Namespace]] = [
|
||||
Namespaces.Peer.CloudUser: [Namespaces.Message.Cloud],
|
||||
Namespaces.Peer.CloudGroup: [Namespaces.Message.Cloud],
|
||||
Namespaces.Peer.CloudChannel: [Namespaces.Message.Cloud]
|
||||
]
|
||||
for peerId in previousPeerIds {
|
||||
if !allPeersWithMessages.contains(peerId), let namespaces = namespacesWithHoles[peerId.namespace] {
|
||||
for namespace in namespaces {
|
||||
transaction.addHole(MessageId(peerId: peerId, namespace: namespace, id: Int32.max - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let currentState = transaction.getState() as? AuthorizedAccountState, let embeddedState = currentState.state {
|
||||
switch state {
|
||||
case let .state(pts, _, _, seq, _):
|
||||
transaction.setState(currentState.changedState(AuthorizedAccountState.State(pts: pts, qts: embeddedState.qts, date: embeddedState.date, seq: seq)))
|
||||
}
|
||||
break*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
for chat in dialogsChats {
|
||||
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(groupOrChannel)
|
||||
}
|
||||
}
|
||||
for user in dialogsUsers {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
|
||||
return withResolvedAssociatedMessages(postbox: postbox, source: .network(network), storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages in
|
||||
let previousPeerIds = transaction.resetChatList(keepPeerNamespaces: Set([Namespaces.Peer.SecretChat]), replacementHole: replacementHole)
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
transaction.updatePeerPresences(peerPresences)
|
||||
|
||||
transaction.updateCurrentPeerNotificationSettings(notificationSettings)
|
||||
|
||||
var allPeersWithMessages = Set<PeerId>()
|
||||
for message in storeMessages {
|
||||
allPeersWithMessages.insert(message.id.peerId)
|
||||
}
|
||||
|
||||
for (_, messageId) in topMesageIds {
|
||||
if messageId.id > 1 {
|
||||
var skipHole = false
|
||||
if let localTopId = transaction.getTopPeerMessageIndex(peerId: messageId.peerId, namespace: messageId.namespace)?.id {
|
||||
if localTopId >= messageId {
|
||||
skipHole = true
|
||||
}
|
||||
}
|
||||
if !skipHole {
|
||||
transaction.addHole(MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: messageId.id - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = transaction.addMessages(storeMessages, location: .UpperHistoryBlock)
|
||||
|
||||
transaction.resetIncomingReadStates(readStates)
|
||||
|
||||
for (peerId, chatState) in chatStates {
|
||||
if let chatState = chatState as? ChannelState {
|
||||
if let current = transaction.getPeerChatState(peerId) as? ChannelState {
|
||||
transaction.setPeerChatState(peerId, state: current.withUpdatedPts(chatState.pts))
|
||||
} else {
|
||||
transaction.setPeerChatState(peerId, state: chatState)
|
||||
}
|
||||
} else {
|
||||
transaction.setPeerChatState(peerId, state: chatState)
|
||||
}
|
||||
}
|
||||
|
||||
transaction.setPinnedItemIds(replacePinnedItemIds)
|
||||
|
||||
for (peerId, summary) in mentionTagSummaries {
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId)
|
||||
}
|
||||
|
||||
let namespacesWithHoles: [PeerId.Namespace: [MessageId.Namespace]] = [
|
||||
Namespaces.Peer.CloudUser: [Namespaces.Message.Cloud],
|
||||
Namespaces.Peer.CloudGroup: [Namespaces.Message.Cloud],
|
||||
Namespaces.Peer.CloudChannel: [Namespaces.Message.Cloud]
|
||||
]
|
||||
for peerId in previousPeerIds {
|
||||
if !allPeersWithMessages.contains(peerId), let namespaces = namespacesWithHoles[peerId.namespace] {
|
||||
for namespace in namespaces {
|
||||
transaction.addHole(MessageId(peerId: peerId, namespace: namespace, id: Int32.max - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let currentState = transaction.getState() as? AuthorizedAccountState, let embeddedState = currentState.state {
|
||||
switch state {
|
||||
case let .state(pts, _, _, seq, _):
|
||||
transaction.setState(currentState.changedState(AuthorizedAccountState.State(pts: pts, qts: embeddedState.qts, date: embeddedState.date, seq: seq)))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -455,38 +455,42 @@ private final class CallSessionManagerContext {
|
||||
if let internalId = self.contextIdByStableId[id] {
|
||||
if let context = self.contexts[internalId] {
|
||||
switch context.state {
|
||||
case let .requested(_, accessHash, a, gA, config, _):
|
||||
var key = MTExp(gB.makeData(), a, config.p.makeData())!
|
||||
|
||||
if key.count > 256 {
|
||||
key.count = 256
|
||||
} else {
|
||||
while key.count < 256 {
|
||||
key.insert(0, at: 0)
|
||||
case let .requested(_, accessHash, a, gA, config, _):
|
||||
let p = config.p.makeData()
|
||||
if !MTCheckIsSafeGAOrB(gA, p) {
|
||||
self.drop(internalId: internalId, reason: .disconnect)
|
||||
}
|
||||
}
|
||||
|
||||
let keyHash = MTSha1(key)!
|
||||
|
||||
var keyId: Int64 = 0
|
||||
keyHash.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||
memcpy(&keyId, bytes.advanced(by: keyHash.count - 8), 8)
|
||||
}
|
||||
|
||||
let keyVisualHash = MTSha256(key + gA)!
|
||||
|
||||
context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state {
|
||||
if let updatedCall = updatedCall {
|
||||
strongSelf.updateSession(updatedCall)
|
||||
} else {
|
||||
strongSelf.drop(internalId: internalId, reason: .disconnect)
|
||||
var key = MTExp(gB.makeData(), a, p)!
|
||||
|
||||
if key.count > 256 {
|
||||
key.count = 256
|
||||
} else {
|
||||
while key.count < 256 {
|
||||
key.insert(0, at: 0)
|
||||
}
|
||||
}
|
||||
}))
|
||||
self.contextUpdated(internalId: internalId)
|
||||
default:
|
||||
self.drop(internalId: internalId, reason: .disconnect)
|
||||
|
||||
let keyHash = MTSha1(key)!
|
||||
|
||||
var keyId: Int64 = 0
|
||||
keyHash.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||
memcpy(&keyId, bytes.advanced(by: keyHash.count - 8), 8)
|
||||
}
|
||||
|
||||
let keyVisualHash = MTSha256(key + gA)!
|
||||
|
||||
context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state {
|
||||
if let updatedCall = updatedCall {
|
||||
strongSelf.updateSession(updatedCall)
|
||||
} else {
|
||||
strongSelf.drop(internalId: internalId, reason: .disconnect)
|
||||
}
|
||||
}
|
||||
}))
|
||||
self.contextUpdated(internalId: internalId)
|
||||
default:
|
||||
self.drop(internalId: internalId, reason: .disconnect)
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
@ -773,54 +777,58 @@ private enum AcceptCallResult {
|
||||
|
||||
private func acceptCallSession(postbox: Postbox, network: Network, stableId: CallSessionStableId, accessHash: Int64, b: Data) -> Signal<AcceptCallResult, NoError> {
|
||||
return validatedEncryptionConfig(postbox: postbox, network: network)
|
||||
|> mapToSignal { config in
|
||||
var gValue: Int32 = config.g.byteSwapped
|
||||
let g = Data(bytes: &gValue, count: 4)
|
||||
let p = config.p.makeData()
|
||||
|
||||
let bData = b
|
||||
|
||||
let gb = MTExp(g, bData, p)!
|
||||
|
||||
return network.request(Api.functions.phone.acceptCall(peer: .inputPhoneCall(id: stableId, accessHash: accessHash), gB: Buffer(data: gb), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: kCallMinLayer, maxLayer: kCallMaxLayer)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { call -> Signal<AcceptCallResult, NoError> in
|
||||
if let call = call {
|
||||
return postbox.transaction { transaction -> AcceptCallResult in
|
||||
switch call {
|
||||
case let .phoneCall(phoneCall, users):
|
||||
var parsedUsers: [Peer] = []
|
||||
for user in users {
|
||||
parsedUsers.append(TelegramUser(user: user))
|
||||
}
|
||||
updatePeers(transaction: transaction, peers: parsedUsers, update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
|
||||
switch phoneCall {
|
||||
case .phoneCallEmpty, .phoneCallRequested, .phoneCallAccepted, .phoneCallDiscarded:
|
||||
return .failed
|
||||
case .phoneCallWaiting:
|
||||
return .success(.waiting(config: config))
|
||||
case let .phoneCall(id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connection, alternativeConnections, startDate):
|
||||
if id == stableId {
|
||||
switch callProtocol{
|
||||
case let .phoneCallProtocol(_, _, maxLayer):
|
||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connection, alternative: alternativeConnections), maxLayer: maxLayer))
|
||||
}
|
||||
} else {
|
||||
return .failed
|
||||
}
|
||||
|> mapToSignal { config -> Signal<AcceptCallResult, NoError> in
|
||||
var gValue: Int32 = config.g.byteSwapped
|
||||
let g = Data(bytes: &gValue, count: 4)
|
||||
let p = config.p.makeData()
|
||||
|
||||
let bData = b
|
||||
|
||||
let gb = MTExp(g, bData, p)!
|
||||
|
||||
if !MTCheckIsSafeGAOrB(gb, p) {
|
||||
return .single(.failed)
|
||||
}
|
||||
|
||||
return network.request(Api.functions.phone.acceptCall(peer: .inputPhoneCall(id: stableId, accessHash: accessHash), gB: Buffer(data: gb), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: kCallMinLayer, maxLayer: kCallMaxLayer)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { call -> Signal<AcceptCallResult, NoError> in
|
||||
if let call = call {
|
||||
return postbox.transaction { transaction -> AcceptCallResult in
|
||||
switch call {
|
||||
case let .phoneCall(phoneCall, users):
|
||||
var parsedUsers: [Peer] = []
|
||||
for user in users {
|
||||
parsedUsers.append(TelegramUser(user: user))
|
||||
}
|
||||
updatePeers(transaction: transaction, peers: parsedUsers, update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
|
||||
switch phoneCall {
|
||||
case .phoneCallEmpty, .phoneCallRequested, .phoneCallAccepted, .phoneCallDiscarded:
|
||||
return .failed
|
||||
case .phoneCallWaiting:
|
||||
return .success(.waiting(config: config))
|
||||
case let .phoneCall(id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connection, alternativeConnections, startDate):
|
||||
if id == stableId {
|
||||
switch callProtocol{
|
||||
case let .phoneCallProtocol(_, _, maxLayer):
|
||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connection, alternative: alternativeConnections), maxLayer: maxLayer))
|
||||
}
|
||||
} else {
|
||||
return .failed
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.failed)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.failed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -831,49 +839,53 @@ private enum RequestCallSessionResult {
|
||||
|
||||
private func requestCallSession(postbox: Postbox, network: Network, peerId: PeerId, a: Data) -> Signal<RequestCallSessionResult, NoError> {
|
||||
return validatedEncryptionConfig(postbox: postbox, network: network)
|
||||
|> mapToSignal { config -> Signal<RequestCallSessionResult, NoError> in
|
||||
return postbox.transaction { transaction -> Signal<RequestCallSessionResult, NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputUser = apiInputUser(peer) {
|
||||
var gValue: Int32 = config.g.byteSwapped
|
||||
let g = Data(bytes: &gValue, count: 4)
|
||||
let p = config.p.makeData()
|
||||
|
||||
let ga = MTExp(g, a, p)!
|
||||
|
||||
let gAHash = MTSha256(ga)!
|
||||
|
||||
return network.request(Api.functions.phone.requestCall(userId: inputUser, randomId: Int32(bitPattern: arc4random()), gAHash: Buffer(data: gAHash), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: kCallMinLayer, maxLayer: kCallMaxLayer)))
|
||||
|> map { result -> RequestCallSessionResult in
|
||||
switch result {
|
||||
case let .phoneCall(phoneCall, _):
|
||||
switch phoneCall {
|
||||
case let .phoneCallRequested(id, accessHash, _, _, _, _, _):
|
||||
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: nil)
|
||||
case let .phoneCallWaiting(_, id, accessHash, _, _, _, _, receiveDate):
|
||||
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: receiveDate)
|
||||
default:
|
||||
return .failed(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> `catch` { error -> Signal<RequestCallSessionResult, NoError> in
|
||||
switch error.errorDescription {
|
||||
case "PARTICIPANT_VERSION_OUTDATED":
|
||||
return .single(.failed(.notSupportedByPeer))
|
||||
case "USER_PRIVACY_RESTRICTED":
|
||||
return .single(.failed(.privacyRestricted))
|
||||
default:
|
||||
if error.errorCode == 406 {
|
||||
return .single(.failed(.serverProvided(error.errorDescription)))
|
||||
} else {
|
||||
return .single(.failed(.generic))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|> mapToSignal { config -> Signal<RequestCallSessionResult, NoError> in
|
||||
return postbox.transaction { transaction -> Signal<RequestCallSessionResult, NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputUser = apiInputUser(peer) {
|
||||
var gValue: Int32 = config.g.byteSwapped
|
||||
let g = Data(bytes: &gValue, count: 4)
|
||||
let p = config.p.makeData()
|
||||
|
||||
let ga = MTExp(g, a, p)!
|
||||
if !MTCheckIsSafeGAOrB(ga, p) {
|
||||
return .single(.failed(.generic))
|
||||
}
|
||||
} |> switchToLatest
|
||||
|
||||
let gAHash = MTSha256(ga)!
|
||||
|
||||
return network.request(Api.functions.phone.requestCall(userId: inputUser, randomId: Int32(bitPattern: arc4random()), gAHash: Buffer(data: gAHash), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: kCallMinLayer, maxLayer: kCallMaxLayer)))
|
||||
|> map { result -> RequestCallSessionResult in
|
||||
switch result {
|
||||
case let .phoneCall(phoneCall, _):
|
||||
switch phoneCall {
|
||||
case let .phoneCallRequested(id, accessHash, _, _, _, _, _):
|
||||
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: nil)
|
||||
case let .phoneCallWaiting(_, id, accessHash, _, _, _, _, receiveDate):
|
||||
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: receiveDate)
|
||||
default:
|
||||
return .failed(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> `catch` { error -> Signal<RequestCallSessionResult, NoError> in
|
||||
switch error.errorDescription {
|
||||
case "PARTICIPANT_VERSION_OUTDATED":
|
||||
return .single(.failed(.notSupportedByPeer))
|
||||
case "USER_PRIVACY_RESTRICTED":
|
||||
return .single(.failed(.privacyRestricted))
|
||||
default:
|
||||
if error.errorCode == 406 {
|
||||
return .single(.failed(.serverProvided(error.errorDescription)))
|
||||
} else {
|
||||
return .single(.failed(.generic))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.failed(.generic))
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,126 +134,129 @@ public func channelBlacklistParticipants(account: Account, peerId: PeerId) -> Si
|
||||
|
||||
public func updateChannelMemberBannedRights(account: Account, peerId: PeerId, memberId: PeerId, rights: TelegramChannelBannedRights?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> {
|
||||
return fetchChannelParticipant(account: account, peerId: peerId, participantId: memberId)
|
||||
|> mapToSignal { currentParticipant -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> in
|
||||
return account.postbox.transaction { transaction -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputChannel = apiInputChannel(peer), let _ = transaction.getPeer(account.peerId), let memberPeer = transaction.getPeer(memberId), let inputUser = apiInputUser(memberPeer) {
|
||||
let updatedParticipant: ChannelParticipant
|
||||
if let currentParticipant = currentParticipant, case let .member(_, invitedAt, _, currentBanInfo) = currentParticipant {
|
||||
let banInfo: ChannelParticipantBannedInfo?
|
||||
if let rights = rights, !rights.flags.isEmpty {
|
||||
banInfo = ChannelParticipantBannedInfo(rights: rights, restrictedBy: currentBanInfo?.restrictedBy ?? account.peerId, isMember: currentBanInfo?.isMember ?? true)
|
||||
} else {
|
||||
banInfo = nil
|
||||
}
|
||||
updatedParticipant = ChannelParticipant.member(id: memberId, invitedAt: invitedAt, adminInfo: nil, banInfo: banInfo)
|
||||
|> mapToSignal { currentParticipant -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> in
|
||||
return account.postbox.transaction { transaction -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputChannel = apiInputChannel(peer), let _ = transaction.getPeer(account.peerId), let memberPeer = transaction.getPeer(memberId), let inputUser = apiInputUser(memberPeer) {
|
||||
let updatedParticipant: ChannelParticipant
|
||||
if let currentParticipant = currentParticipant, case let .member(_, invitedAt, _, currentBanInfo) = currentParticipant {
|
||||
let banInfo: ChannelParticipantBannedInfo?
|
||||
if let rights = rights, !rights.flags.isEmpty {
|
||||
banInfo = ChannelParticipantBannedInfo(rights: rights, restrictedBy: currentBanInfo?.restrictedBy ?? account.peerId, isMember: currentBanInfo?.isMember ?? true)
|
||||
} else {
|
||||
let banInfo: ChannelParticipantBannedInfo?
|
||||
if let rights = rights, !rights.flags.isEmpty {
|
||||
banInfo = ChannelParticipantBannedInfo(rights: rights, restrictedBy: account.peerId, isMember: false)
|
||||
} else {
|
||||
banInfo = nil
|
||||
}
|
||||
updatedParticipant = ChannelParticipant.member(id: memberId, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: nil, banInfo: banInfo)
|
||||
banInfo = nil
|
||||
}
|
||||
|
||||
let effectiveRights: TelegramChannelBannedRights = rights ?? TelegramChannelBannedRights(flags: [], untilDate: 0)
|
||||
|
||||
return account.network.request(Api.functions.channels.editBanned(channel: inputChannel, userId: inputUser, bannedRights: effectiveRights.apiBannedRights))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> in
|
||||
return account.postbox.transaction { transaction -> (ChannelParticipant?, RenderedChannelParticipant) in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
var updatedData = cachedData
|
||||
var wasKicked = false
|
||||
var wasBanned = false
|
||||
var wasMember = false
|
||||
var wasAdmin = false
|
||||
if let currentParticipant = currentParticipant {
|
||||
switch currentParticipant {
|
||||
case .creator:
|
||||
break
|
||||
case let .member(_, _, adminInfo, banInfo):
|
||||
if let adminInfo = adminInfo {
|
||||
wasAdmin = true
|
||||
}
|
||||
if let banInfo = banInfo {
|
||||
if banInfo.rights.flags.contains(.banReadMessages) {
|
||||
wasKicked = true
|
||||
} else if !banInfo.rights.flags.isEmpty {
|
||||
wasBanned = true
|
||||
}
|
||||
}
|
||||
wasMember = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var isKicked = false
|
||||
var isBanned = false
|
||||
if effectiveRights.flags.contains(.banReadMessages) {
|
||||
isKicked = true
|
||||
} else if !effectiveRights.flags.isEmpty {
|
||||
isBanned = true
|
||||
}
|
||||
|
||||
let isMember = !wasKicked && !effectiveRights.flags.contains(.banReadMessages)
|
||||
|
||||
if isKicked != wasKicked {
|
||||
if let kickedCount = updatedData.participantsSummary.kickedCount {
|
||||
updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedKickedCount(max(0, kickedCount + (isKicked ? 1 : -1))))
|
||||
}
|
||||
}
|
||||
|
||||
if isBanned != wasBanned {
|
||||
if let bannedCount = updatedData.participantsSummary.bannedCount {
|
||||
updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedBannedCount(max(0, bannedCount + (isBanned ? 1 : -1))))
|
||||
}
|
||||
}
|
||||
|
||||
if wasAdmin {
|
||||
if let adminCount = updatedData.participantsSummary.adminCount {
|
||||
updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedAdminCount(max(0, adminCount - 1)))
|
||||
}
|
||||
}
|
||||
|
||||
if isMember != wasMember {
|
||||
if let memberCount = updatedData.participantsSummary.memberCount {
|
||||
updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedMemberCount(max(0, memberCount + (isMember ? 1 : -1))))
|
||||
}
|
||||
|
||||
if !isMember, let topParticipants = updatedData.topParticipants {
|
||||
var updatedParticipants = topParticipants.participants
|
||||
if let index = updatedParticipants.index(where: { $0.peerId == memberId }) {
|
||||
updatedParticipants.remove(at: index)
|
||||
|
||||
updatedData = updatedData.withUpdatedTopParticipants(CachedChannelParticipants(participants: updatedParticipants))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updatedData
|
||||
} else {
|
||||
return cachedData
|
||||
}
|
||||
})
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
var presences: [PeerId: PeerPresence] = [:]
|
||||
peers[memberPeer.id] = memberPeer
|
||||
if let presence = transaction.getPeerPresence(peerId: memberPeer.id) {
|
||||
presences[memberPeer.id] = presence
|
||||
}
|
||||
if case let .member(_, _, _, maybeBanInfo) = updatedParticipant, let banInfo = maybeBanInfo {
|
||||
if let peer = transaction.getPeer(banInfo.restrictedBy) {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
return (currentParticipant, RenderedChannelParticipant(participant: updatedParticipant, peer: memberPeer, peers: peers, presences: presences))
|
||||
}
|
||||
}
|
||||
updatedParticipant = ChannelParticipant.member(id: memberId, invitedAt: invitedAt, adminInfo: nil, banInfo: banInfo)
|
||||
} else {
|
||||
return .complete()
|
||||
let banInfo: ChannelParticipantBannedInfo?
|
||||
if let rights = rights, !rights.flags.isEmpty {
|
||||
banInfo = ChannelParticipantBannedInfo(rights: rights, restrictedBy: account.peerId, isMember: false)
|
||||
} else {
|
||||
banInfo = nil
|
||||
}
|
||||
updatedParticipant = ChannelParticipant.member(id: memberId, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: nil, banInfo: banInfo)
|
||||
}
|
||||
} |> switchToLatest
|
||||
|
||||
let effectiveRights: TelegramChannelBannedRights = rights ?? TelegramChannelBannedRights(flags: [], untilDate: 0)
|
||||
|
||||
return account.network.request(Api.functions.channels.editBanned(channel: inputChannel, userId: inputUser, bannedRights: effectiveRights.apiBannedRights))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> in
|
||||
account.stateManager.addUpdates(result)
|
||||
|
||||
return account.postbox.transaction { transaction -> (ChannelParticipant?, RenderedChannelParticipant) in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
var updatedData = cachedData
|
||||
var wasKicked = false
|
||||
var wasBanned = false
|
||||
var wasMember = false
|
||||
var wasAdmin = false
|
||||
if let currentParticipant = currentParticipant {
|
||||
switch currentParticipant {
|
||||
case .creator:
|
||||
break
|
||||
case let .member(_, _, adminInfo, banInfo):
|
||||
if let adminInfo = adminInfo {
|
||||
wasAdmin = true
|
||||
}
|
||||
if let banInfo = banInfo {
|
||||
if banInfo.rights.flags.contains(.banReadMessages) {
|
||||
wasKicked = true
|
||||
} else if !banInfo.rights.flags.isEmpty {
|
||||
wasBanned = true
|
||||
}
|
||||
}
|
||||
wasMember = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var isKicked = false
|
||||
var isBanned = false
|
||||
if effectiveRights.flags.contains(.banReadMessages) {
|
||||
isKicked = true
|
||||
} else if !effectiveRights.flags.isEmpty {
|
||||
isBanned = true
|
||||
}
|
||||
|
||||
let isMember = !wasKicked && !effectiveRights.flags.contains(.banReadMessages)
|
||||
|
||||
if isKicked != wasKicked {
|
||||
if let kickedCount = updatedData.participantsSummary.kickedCount {
|
||||
updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedKickedCount(max(0, kickedCount + (isKicked ? 1 : -1))))
|
||||
}
|
||||
}
|
||||
|
||||
if isBanned != wasBanned {
|
||||
if let bannedCount = updatedData.participantsSummary.bannedCount {
|
||||
updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedBannedCount(max(0, bannedCount + (isBanned ? 1 : -1))))
|
||||
}
|
||||
}
|
||||
|
||||
if wasAdmin {
|
||||
if let adminCount = updatedData.participantsSummary.adminCount {
|
||||
updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedAdminCount(max(0, adminCount - 1)))
|
||||
}
|
||||
}
|
||||
|
||||
if isMember != wasMember {
|
||||
if let memberCount = updatedData.participantsSummary.memberCount {
|
||||
updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedMemberCount(max(0, memberCount + (isMember ? 1 : -1))))
|
||||
}
|
||||
|
||||
if !isMember, let topParticipants = updatedData.topParticipants {
|
||||
var updatedParticipants = topParticipants.participants
|
||||
if let index = updatedParticipants.index(where: { $0.peerId == memberId }) {
|
||||
updatedParticipants.remove(at: index)
|
||||
|
||||
updatedData = updatedData.withUpdatedTopParticipants(CachedChannelParticipants(participants: updatedParticipants))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updatedData
|
||||
} else {
|
||||
return cachedData
|
||||
}
|
||||
})
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
var presences: [PeerId: PeerPresence] = [:]
|
||||
peers[memberPeer.id] = memberPeer
|
||||
if let presence = transaction.getPeerPresence(peerId: memberPeer.id) {
|
||||
presences[memberPeer.id] = presence
|
||||
}
|
||||
if case let .member(_, _, _, maybeBanInfo) = updatedParticipant, let banInfo = maybeBanInfo {
|
||||
if let peer = transaction.getPeer(banInfo.restrictedBy) {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
return (currentParticipant, RenderedChannelParticipant(participant: updatedParticipant, peer: memberPeer, peers: peers, presences: presences))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
}
|
||||
|
@ -70,12 +70,35 @@ func syncContactsOnce(network: Network, postbox: Postbox) -> Signal<Never, NoErr
|
||||
let appliedUpdatedPeers = updatedPeers
|
||||
|> mapToSignal { peersAndPresences -> Signal<Never, NoError> in
|
||||
if let (peers, peerPresences, totalCount) = peersAndPresences {
|
||||
return postbox.transaction { transaction in
|
||||
updatePeers(transaction: transaction, peers: peers, update: { return $1 })
|
||||
transaction.updatePeerPresences(peerPresences)
|
||||
transaction.replaceContactPeerIds(Set(peers.map { $0.id }))
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
let previousIds = transaction.getContactPeerIds()
|
||||
let wasEmpty = previousIds.isEmpty
|
||||
|
||||
transaction.replaceRemoteContactCount(totalCount)
|
||||
|
||||
transaction.updatePeerPresences(peerPresences)
|
||||
|
||||
if wasEmpty {
|
||||
var insertSignal: Signal<Void, NoError> = .complete()
|
||||
for s in stride(from: 0, to: peers.count, by: 100) {
|
||||
let partPeers = Array(peers[s ..< min(s + 100, peers.count)])
|
||||
let partSignal = postbox.transaction { transaction -> Void in
|
||||
updatePeers(transaction: transaction, peers: partPeers, update: { return $1 })
|
||||
var updatedIds = transaction.getContactPeerIds()
|
||||
updatedIds.formUnion(partPeers.map { $0.id })
|
||||
transaction.replaceContactPeerIds(updatedIds)
|
||||
}
|
||||
|> delay(0.1, queue: Queue.concurrentDefaultQueue())
|
||||
insertSignal = insertSignal |> then(partSignal)
|
||||
}
|
||||
|
||||
return insertSignal
|
||||
} else {
|
||||
transaction.replaceContactPeerIds(Set(peers.map { $0.id }))
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
|> ignoreValues
|
||||
} else {
|
||||
return .complete()
|
||||
|
@ -30,6 +30,10 @@ public func createSecretChat(account: Account, peerId: PeerId) -> Signal<PeerId,
|
||||
let aData = a.makeData()
|
||||
let ga = MTExp(g, aData, p)!
|
||||
|
||||
if !MTCheckIsSafeGAOrB(ga, p) {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.messages.requestEncryption(userId: inputUser, randomId: Int32(bitPattern: arc4random()), gA: Buffer(data: ga)))
|
||||
|> mapError { _ -> CreateSecretChatError in
|
||||
return .generic
|
||||
|
@ -39,6 +39,90 @@ enum FetchMessageHistoryHoleSource {
|
||||
}
|
||||
}
|
||||
|
||||
func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHistoryHoleSource, storeMessages: [StoreMessage], _ f: @escaping (Transaction, [Peer], [StoreMessage]) -> Void) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
var storedIds = Set<MessageId>()
|
||||
var referencedIds = Set<MessageId>()
|
||||
for message in storeMessages {
|
||||
guard case let .Id(id) = message.id else {
|
||||
continue
|
||||
}
|
||||
storedIds.insert(id)
|
||||
for attribute in message.attributes {
|
||||
referencedIds.formUnion(attribute.associatedMessageIds)
|
||||
}
|
||||
}
|
||||
referencedIds.subtract(storedIds)
|
||||
referencedIds = transaction.filterStoredMessageIds(referencedIds)
|
||||
|
||||
if referencedIds.isEmpty {
|
||||
f(transaction, [], [])
|
||||
return .complete()
|
||||
} else {
|
||||
var signals: [Signal<([Api.Message], [Api.Chat], [Api.User]), NoError>] = []
|
||||
for (peerId, messageIds) in messagesIdsGroupedByPeerId(referencedIds) {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
var signal: Signal<Api.messages.Messages, MTRpcError>?
|
||||
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
signal = source.request(Api.functions.messages.getMessages(id: messageIds.map({ Api.InputMessage.inputMessageID(id: $0.id) })))
|
||||
} else if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
if let inputChannel = apiInputChannel(peer) {
|
||||
signal = source.request(Api.functions.channels.getMessages(channel: inputChannel, id: messageIds.map({ Api.InputMessage.inputMessageID(id: $0.id) })))
|
||||
}
|
||||
}
|
||||
if let signal = signal {
|
||||
signals.append(signal
|
||||
|> map { result in
|
||||
switch result {
|
||||
case let .messages(messages, chats, users):
|
||||
return (messages, chats, users)
|
||||
case let .messagesSlice(_, messages, chats, users):
|
||||
return (messages, chats, users)
|
||||
case let .channelMessages(_, _, _, messages, chats, users):
|
||||
return (messages, chats, users)
|
||||
case .messagesNotModified:
|
||||
return ([], [], [])
|
||||
}
|
||||
}
|
||||
|> `catch` { _ in
|
||||
return Signal<([Api.Message], [Api.Chat], [Api.User]), NoError>.single(([], [], []))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let fetchMessages = combineLatest(signals)
|
||||
|
||||
return fetchMessages
|
||||
|> mapToSignal { results -> Signal<Void, NoError> in
|
||||
var additionalPeers: [Peer] = []
|
||||
var additionalMessages: [StoreMessage] = []
|
||||
for (messages, chats, users) in results {
|
||||
if !messages.isEmpty {
|
||||
for message in messages {
|
||||
if let message = StoreMessage(apiMessage: message) {
|
||||
additionalMessages.append(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
for chat in chats {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
additionalPeers.append(peer)
|
||||
}
|
||||
}
|
||||
for user in users {
|
||||
additionalPeers.append(TelegramUser(user: user))
|
||||
}
|
||||
}
|
||||
return postbox.transaction { transaction -> Void in
|
||||
f(transaction, additionalPeers, additionalMessages)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Postbox, hole: MessageHistoryHole, direction: MessageHistoryViewRelativeHoleDirection, tagMask: MessageTags?, limit: Int = 100) -> Signal<Void, NoError> {
|
||||
assert(tagMask == nil || tagMask!.rawValue != 0)
|
||||
return postbox.loadedPeerWithId(hole.maxIndex.id.peerId)
|
||||
@ -164,104 +248,106 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
|
||||
}
|
||||
|
||||
return combineLatest(request |> retryRequest, maxIndexRequest |> retryRequest)
|
||||
|> mapToSignal { result, maxIndexResult in
|
||||
let messages: [Api.Message]
|
||||
let chats: [Api.Chat]
|
||||
let users: [Api.User]
|
||||
var channelPts: Int32?
|
||||
switch result {
|
||||
case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .messagesSlice(_, messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .channelMessages(_, pts, _, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
channelPts = pts
|
||||
|> mapToSignal { result, maxIndexResult in
|
||||
let messages: [Api.Message]
|
||||
let chats: [Api.Chat]
|
||||
let users: [Api.User]
|
||||
var channelPts: Int32?
|
||||
switch result {
|
||||
case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .messagesSlice(_, messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .channelMessages(_, pts, _, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
channelPts = pts
|
||||
case .messagesNotModified:
|
||||
messages = []
|
||||
chats = []
|
||||
users = []
|
||||
}
|
||||
var updatedMaxIndex: MessageIndex?
|
||||
if let maxIndexResult = maxIndexResult {
|
||||
let maxIndexMessages: [Api.Message]
|
||||
switch maxIndexResult {
|
||||
case let .messages(apiMessages, _, _):
|
||||
maxIndexMessages = apiMessages
|
||||
case let .messagesSlice(_, apiMessages, _, _):
|
||||
maxIndexMessages = apiMessages
|
||||
case let .channelMessages(_, _, _, apiMessages, _, _):
|
||||
maxIndexMessages = apiMessages
|
||||
case .messagesNotModified:
|
||||
messages = []
|
||||
chats = []
|
||||
users = []
|
||||
maxIndexMessages = []
|
||||
}
|
||||
var updatedMaxIndex: MessageIndex?
|
||||
if let maxIndexResult = maxIndexResult {
|
||||
let maxIndexMessages: [Api.Message]
|
||||
switch maxIndexResult {
|
||||
case let .messages(apiMessages, _, _):
|
||||
maxIndexMessages = apiMessages
|
||||
case let .messagesSlice(_, apiMessages, _, _):
|
||||
maxIndexMessages = apiMessages
|
||||
case let .channelMessages(_, _, _, apiMessages, _, _):
|
||||
maxIndexMessages = apiMessages
|
||||
case .messagesNotModified:
|
||||
maxIndexMessages = []
|
||||
if !maxIndexMessages.isEmpty {
|
||||
assert(maxIndexMessages.count == 1)
|
||||
if let storeMessage = StoreMessage(apiMessage: maxIndexMessages[0]), case let .Id(id) = storeMessage.id {
|
||||
updatedMaxIndex = MessageIndex(id: id, timestamp: storeMessage.timestamp)
|
||||
}
|
||||
if !maxIndexMessages.isEmpty {
|
||||
assert(maxIndexMessages.count == 1)
|
||||
if let storeMessage = StoreMessage(apiMessage: maxIndexMessages[0]), case let .Id(id) = storeMessage.id {
|
||||
updatedMaxIndex = MessageIndex(id: id, timestamp: storeMessage.timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
return postbox.transaction { transaction in
|
||||
var storeMessages: [StoreMessage] = []
|
||||
|
||||
for message in messages {
|
||||
if let storeMessage = StoreMessage(apiMessage: message) {
|
||||
if let channelPts = channelPts {
|
||||
var attributes = storeMessage.attributes
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
|
||||
storeMessages.append(storeMessage.withUpdatedAttributes(attributes))
|
||||
} else {
|
||||
storeMessages.append(storeMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let fillDirection: HoleFillDirection
|
||||
switch direction {
|
||||
case .UpperToLower:
|
||||
fillDirection = .UpperToLower(updatedMinIndex: nil, clippingMaxIndex: nil)
|
||||
case .LowerToUpper:
|
||||
fillDirection = .LowerToUpper(updatedMaxIndex: updatedMaxIndex, clippingMinIndex: nil)
|
||||
case let .AroundId(id):
|
||||
fillDirection = .AroundId(id, lowerComplete: false, upperComplete: false)
|
||||
case let .AroundIndex(index):
|
||||
fillDirection = .AroundId(index.id, lowerComplete: false, upperComplete: false)
|
||||
}
|
||||
|
||||
transaction.fillMultipleHoles(hole, fillType: HoleFill(complete: messages.count == 0 || implicitelyFillHole, direction: fillDirection), tagMask: tagMask, messages: storeMessages)
|
||||
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
for chat in chats {
|
||||
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(groupOrChannel)
|
||||
}
|
||||
}
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
transaction.updatePeerPresences(peerPresences)
|
||||
|
||||
print("fetchMessageHistoryHole for \(peer.displayTitle) done")
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var storeMessages: [StoreMessage] = []
|
||||
|
||||
for message in messages {
|
||||
if let storeMessage = StoreMessage(apiMessage: message) {
|
||||
if let channelPts = channelPts {
|
||||
var attributes = storeMessage.attributes
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
|
||||
storeMessages.append(storeMessage.withUpdatedAttributes(attributes))
|
||||
} else {
|
||||
storeMessages.append(storeMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return withResolvedAssociatedMessages(postbox: postbox, source: source, storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages in
|
||||
let fillDirection: HoleFillDirection
|
||||
switch direction {
|
||||
case .UpperToLower:
|
||||
fillDirection = .UpperToLower(updatedMinIndex: nil, clippingMaxIndex: nil)
|
||||
case .LowerToUpper:
|
||||
fillDirection = .LowerToUpper(updatedMaxIndex: updatedMaxIndex, clippingMinIndex: nil)
|
||||
case let .AroundId(id):
|
||||
fillDirection = .AroundId(id, lowerComplete: false, upperComplete: false)
|
||||
case let .AroundIndex(index):
|
||||
fillDirection = .AroundId(index.id, lowerComplete: false, upperComplete: false)
|
||||
}
|
||||
|
||||
transaction.fillMultipleHoles(hole, fillType: HoleFill(complete: messages.count == 0 || implicitelyFillHole, direction: fillDirection), tagMask: tagMask, messages: storeMessages)
|
||||
let _ = transaction.addMessages(additionalMessages, location: .Random)
|
||||
|
||||
var peers: [Peer] = additionalPeers
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
for chat in chats {
|
||||
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(groupOrChannel)
|
||||
}
|
||||
}
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
transaction.updatePeerPresences(peerPresences)
|
||||
|
||||
print("fetchMessageHistoryHole for \(peer.displayTitle) done")
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
@ -468,7 +554,7 @@ func fetchChatListHole(postbox: Postbox, network: Network, groupId: PeerGroupId?
|
||||
}
|
||||
return fetchChatList(postbox: postbox, network: network, location: location, upperBound: hole.index)
|
||||
|> mapToSignal { fetchedChats -> Signal<Void, NoError> in
|
||||
return postbox.transaction { transaction in
|
||||
return withResolvedAssociatedMessages(postbox: postbox, source: .network(network), storeMessages: fetchedChats.storeMessages, { transaction, additionalPeers, additionalMessages in
|
||||
for peer in fetchedChats.peers {
|
||||
updatePeers(transaction: transaction, peers: [peer], update: { _, updated -> Peer in
|
||||
return updated
|
||||
@ -506,7 +592,7 @@ func fetchChatListHole(postbox: Postbox, network: Network, groupId: PeerGroupId?
|
||||
for (peerId, summary) in fetchedChats.mentionTagSummaries {
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,47 +124,47 @@ func managedSecretChatOutgoingOperations(postbox: Postbox, network: Network) ->
|
||||
|
||||
for (entry, disposable) in beginOperations {
|
||||
let signal = takenImmutableOperation(postbox: postbox, peerId: entry.peerId, tagLocalIndex: entry.tagLocalIndex)
|
||||
|> mapToSignal { entry -> Signal<Void, NoError> in
|
||||
if let entry = entry {
|
||||
if let operation = entry.contents as? SecretChatOutgoingOperation {
|
||||
switch operation.contents {
|
||||
case let .initialHandshakeAccept(gA, accessHash, b):
|
||||
return initialHandshakeAccept(postbox: postbox, network: network, peerId: entry.peerId, accessHash: accessHash, gA: gA, b: b, tagLocalIndex: entry.tagLocalIndex)
|
||||
case let .sendMessage(layer, id, file):
|
||||
return sendMessage(postbox: postbox, network: network, messageId: id, file: file, tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered, layer: layer)
|
||||
case let .reportLayerSupport(layer, actionGloballyUniqueId, layerSupport):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .reportLayerSupport(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, layerSupport: layerSupport), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .deleteMessages(layer, actionGloballyUniqueId, globallyUniqueIds):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .deleteMessages(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, globallyUniqueIds: globallyUniqueIds), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .clearHistory(layer, actionGloballyUniqueId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .clearHistory(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .pfsRequestKey(layer, actionGloballyUniqueId, rekeySessionId, a):
|
||||
return pfsRequestKey(postbox: postbox, network: network, peerId: entry.peerId, layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, rekeySessionId: rekeySessionId, a: a, tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .pfsCommitKey(layer, actionGloballyUniqueId, rekeySessionId, keyFingerprint):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .pfsCommitKey(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, rekeySessionId: rekeySessionId, keyFingerprint: keyFingerprint), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .pfsAcceptKey(layer, actionGloballyUniqueId, rekeySessionId, gA, b):
|
||||
return pfsAcceptKey(postbox: postbox, network: network, peerId: entry.peerId, layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, rekeySessionId: rekeySessionId, gA: gA, b: b, tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .pfsAbortSession(layer, actionGloballyUniqueId, rekeySessionId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .pfsAbortSession(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, rekeySessionId: rekeySessionId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .noop(layer, actionGloballyUniqueId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .noop(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .readMessagesContent(layer, actionGloballyUniqueId, globallyUniqueIds):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .readMessageContents(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, globallyUniqueIds: globallyUniqueIds), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .setMessageAutoremoveTimeout(layer, actionGloballyUniqueId, timeout, messageId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .setMessageAutoremoveTimeout(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, timeout: timeout, messageId: messageId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .resendOperations(layer, actionGloballyUniqueId, fromSeqNo, toSeqNo):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .resendOperations(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, fromSeqNo: fromSeqNo, toSeqNo: toSeqNo), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .screenshotMessages(layer, actionGloballyUniqueId, globallyUniqueIds, messageId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .screenshotMessages(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, globallyUniqueIds: globallyUniqueIds, messageId: messageId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .terminate(reportSpam):
|
||||
return requestTerminateSecretChat(postbox: postbox, network: network, peerId: entry.peerId, tagLocalIndex: entry.tagLocalIndex, reportSpam: reportSpam)
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
|> mapToSignal { entry -> Signal<Void, NoError> in
|
||||
if let entry = entry {
|
||||
if let operation = entry.contents as? SecretChatOutgoingOperation {
|
||||
switch operation.contents {
|
||||
case let .initialHandshakeAccept(gA, accessHash, b):
|
||||
return initialHandshakeAccept(postbox: postbox, network: network, peerId: entry.peerId, accessHash: accessHash, gA: gA, b: b, tagLocalIndex: entry.tagLocalIndex)
|
||||
case let .sendMessage(layer, id, file):
|
||||
return sendMessage(postbox: postbox, network: network, messageId: id, file: file, tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered, layer: layer)
|
||||
case let .reportLayerSupport(layer, actionGloballyUniqueId, layerSupport):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .reportLayerSupport(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, layerSupport: layerSupport), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .deleteMessages(layer, actionGloballyUniqueId, globallyUniqueIds):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .deleteMessages(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, globallyUniqueIds: globallyUniqueIds), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .clearHistory(layer, actionGloballyUniqueId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .clearHistory(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .pfsRequestKey(layer, actionGloballyUniqueId, rekeySessionId, a):
|
||||
return pfsRequestKey(postbox: postbox, network: network, peerId: entry.peerId, layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, rekeySessionId: rekeySessionId, a: a, tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .pfsCommitKey(layer, actionGloballyUniqueId, rekeySessionId, keyFingerprint):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .pfsCommitKey(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, rekeySessionId: rekeySessionId, keyFingerprint: keyFingerprint), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .pfsAcceptKey(layer, actionGloballyUniqueId, rekeySessionId, gA, b):
|
||||
return pfsAcceptKey(postbox: postbox, network: network, peerId: entry.peerId, layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, rekeySessionId: rekeySessionId, gA: gA, b: b, tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .pfsAbortSession(layer, actionGloballyUniqueId, rekeySessionId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .pfsAbortSession(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, rekeySessionId: rekeySessionId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .noop(layer, actionGloballyUniqueId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .noop(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .readMessagesContent(layer, actionGloballyUniqueId, globallyUniqueIds):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .readMessageContents(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, globallyUniqueIds: globallyUniqueIds), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .setMessageAutoremoveTimeout(layer, actionGloballyUniqueId, timeout, messageId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .setMessageAutoremoveTimeout(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, timeout: timeout, messageId: messageId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .resendOperations(layer, actionGloballyUniqueId, fromSeqNo, toSeqNo):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .resendOperations(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, fromSeqNo: fromSeqNo, toSeqNo: toSeqNo), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .screenshotMessages(layer, actionGloballyUniqueId, globallyUniqueIds, messageId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .screenshotMessages(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, globallyUniqueIds: globallyUniqueIds, messageId: messageId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .terminate(reportSpam):
|
||||
return requestTerminateSecretChat(postbox: postbox, network: network, peerId: entry.peerId, tagLocalIndex: entry.tagLocalIndex, reportSpam: reportSpam)
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
disposable.set(signal.start())
|
||||
}
|
||||
})
|
||||
@ -185,9 +185,29 @@ func managedSecretChatOutgoingOperations(postbox: Postbox, network: Network) ->
|
||||
private func initialHandshakeAccept(postbox: Postbox, network: Network, peerId: PeerId, accessHash: Int64, gA: MemoryBuffer, b: MemoryBuffer, tagLocalIndex: Int32) -> Signal<Void, NoError> {
|
||||
return validatedEncryptionConfig(postbox: postbox, network: network)
|
||||
|> mapToSignal { config -> Signal<Void, NoError> in
|
||||
let p = config.p.makeData()
|
||||
|
||||
if !MTCheckIsSafeGAOrB(gA.makeData(), p) {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
let removed = transaction.operationLogRemoveEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: tagLocalIndex)
|
||||
assert(removed)
|
||||
if let state = transaction.getPeerChatState(peerId) as? SecretChatState {
|
||||
var updatedState = state
|
||||
updatedState = updatedState.withUpdatedEmbeddedState(.terminated)
|
||||
transaction.setPeerChatState(peerId, state: updatedState)
|
||||
if let peer = transaction.getPeer(peerId) as? TelegramSecretChat {
|
||||
updatePeers(transaction: transaction, peers: [peer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var gValue: Int32 = config.g.byteSwapped
|
||||
let g = Data(bytes: &gValue, count: 4)
|
||||
let p = config.p.makeData()
|
||||
|
||||
let bData = b.makeData()
|
||||
|
||||
@ -258,7 +278,7 @@ private func initialHandshakeAccept(postbox: Postbox, network: Network, peerId:
|
||||
}
|
||||
|
||||
private func pfsRequestKey(postbox: Postbox, network: Network, peerId: PeerId, layer: SecretChatSequenceBasedLayer, actionGloballyUniqueId: Int64, rekeySessionId: Int64, a: MemoryBuffer, tagLocalIndex: Int32, wasDelivered: Bool) -> Signal<Void, NoError> {
|
||||
return validatedEncryptionConfig(postbox: postbox, network: network)
|
||||
return validatedEncryptionConfig(postbox: postbox, network: network)
|
||||
|> mapToSignal { config -> Signal<Void, NoError> in
|
||||
var gValue: Int32 = config.g.byteSwapped
|
||||
let g = Data(bytes: &gValue, count: 4)
|
||||
|
@ -28,7 +28,21 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha
|
||||
switch result {
|
||||
case let .internalReference(_, id, type, title, description, image, file, message):
|
||||
if type == "game" {
|
||||
return .message(text: "", attributes: attributes, mediaReference: .standalone(media: TelegramMediaGame(gameId: 0, accessHash: 0, name: "", title: title ?? "", description: description ?? "", image: image, file: file)), replyToMessageId: nil, localGroupingKey: nil)
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
let filteredAttributes = attributes.filter { attribute in
|
||||
if let _ = attribute as? ReplyMarkupMessageAttribute {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if let media: Media = file ?? image {
|
||||
return .message(text: caption, attributes: filteredAttributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil)
|
||||
} else {
|
||||
return .message(text: caption, attributes: filteredAttributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)
|
||||
}
|
||||
} else {
|
||||
return .message(text: "", attributes: attributes, mediaReference: .standalone(media: TelegramMediaGame(gameId: 0, accessHash: 0, name: "", title: title ?? "", description: description ?? "", image: image, file: file)), replyToMessageId: nil, localGroupingKey: nil)
|
||||
}
|
||||
} else if let file = file, type == "gif" {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
|
||||
} else if let image = image {
|
||||
|
@ -22,32 +22,32 @@ public func removePeerMember(account: Account, peerId: PeerId, memberId: PeerId)
|
||||
if let peer = transaction.getPeer(peerId), let memberPeer = transaction.getPeer(memberId), let inputUser = apiInputUser(memberPeer) {
|
||||
if let group = peer as? TelegramGroup {
|
||||
return account.network.request(Api.functions.messages.deleteChatUser(chatId: group.id.id, userId: inputUser))
|
||||
|> mapError { error -> Void in
|
||||
return Void()
|
||||
}
|
||||
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
account.stateManager.addUpdates(result)
|
||||
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedGroupData, let participants = cachedData.participants {
|
||||
var updatedParticipants = participants.participants
|
||||
for i in 0 ..< participants.participants.count {
|
||||
if participants.participants[i].peerId == memberId {
|
||||
updatedParticipants.remove(at: i)
|
||||
break
|
||||
}
|
||||
|> mapError { error -> Void in
|
||||
return Void()
|
||||
}
|
||||
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
account.stateManager.addUpdates(result)
|
||||
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedGroupData, let participants = cachedData.participants {
|
||||
var updatedParticipants = participants.participants
|
||||
for i in 0 ..< participants.participants.count {
|
||||
if participants.participants[i].peerId == memberId {
|
||||
updatedParticipants.remove(at: i)
|
||||
break
|
||||
}
|
||||
|
||||
return cachedData.withUpdatedParticipants(CachedGroupParticipants(participants: updatedParticipants, version: participants.version))
|
||||
} else {
|
||||
return cachedData
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return cachedData.withUpdatedParticipants(CachedGroupParticipants(participants: updatedParticipants, version: participants.version))
|
||||
} else {
|
||||
return cachedData
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
|
@ -35,13 +35,28 @@ public final class SecretChatEncryptionConfig: PostboxCoding {
|
||||
|
||||
func validatedEncryptionConfig(postbox: Postbox, network: Network) -> Signal<SecretChatEncryptionConfig, NoError> {
|
||||
return network.request(Api.functions.messages.getDhConfig(version: 0, randomLength: 0))
|
||||
|> retryRequest
|
||||
|> map { result -> SecretChatEncryptionConfig in
|
||||
switch result {
|
||||
case let .dhConfig(g, p, version, _):
|
||||
return SecretChatEncryptionConfig(g: g, p: MemoryBuffer(p), version: version)
|
||||
case .dhConfigNotModified(_):
|
||||
preconditionFailure()
|
||||
}
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<SecretChatEncryptionConfig, NoError> in
|
||||
switch result {
|
||||
case let .dhConfig(g, p, version, _):
|
||||
if !MTCheckIsSafeG(UInt32(g)) {
|
||||
Logger.shared.log("SecretChatEncryptionConfig", "Invalid g")
|
||||
return .complete()
|
||||
}
|
||||
|
||||
if !MTCheckMod(p.makeData(), UInt32(g), network.context.keychain) {
|
||||
Logger.shared.log("SecretChatEncryptionConfig", "Invalid p or g")
|
||||
return .complete()
|
||||
}
|
||||
|
||||
if !MTCheckIsSafePrime(p.makeData(), network.context.keychain) {
|
||||
Logger.shared.log("SecretChatEncryptionConfig", "Invalid p")
|
||||
return .never()
|
||||
}
|
||||
return .single(SecretChatEncryptionConfig(g: g, p: MemoryBuffer(p), version: version))
|
||||
case .dhConfigNotModified(_):
|
||||
assertionFailure()
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,10 +45,12 @@ func secretChatAdvanceRekeySessionIfNeeded(transaction: Transaction, peerId: Pee
|
||||
switch rekeySession.data {
|
||||
case let .requested(a, config):
|
||||
var gValue: Int32 = config.g.byteSwapped
|
||||
let g = Data(bytes: &gValue, count: 4)
|
||||
let p = config.p.makeData()
|
||||
|
||||
let aData = a.makeData()
|
||||
if !MTCheckIsSafeGAOrB(gB.makeData(), p) {
|
||||
return state.withUpdatedEmbeddedState(.terminated)
|
||||
}
|
||||
|
||||
var key = MTExp(gB.makeData(), aData, p)!
|
||||
|
||||
|
@ -26,6 +26,13 @@ func updateSecretChat(accountPeerId: PeerId, transaction: Transaction, chat: Api
|
||||
let pData = p.makeData()
|
||||
let aData = a.makeData()
|
||||
|
||||
if !MTCheckIsSafeGAOrB(gAOrB.makeData(), pData) {
|
||||
var updatedState = currentState
|
||||
updatedState = updatedState.withUpdatedEmbeddedState(.terminated)
|
||||
transaction.setPeerChatState(chat.peerId, state: updatedState)
|
||||
return
|
||||
}
|
||||
|
||||
var key = MTExp(gAOrB.makeData(), aData, pData)!
|
||||
|
||||
if key.count > 256 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user