no message

This commit is contained in:
Peter 2018-09-07 22:49:24 +03:00
parent 3dc3a8d655
commit 0f42fde3f5
17 changed files with 887 additions and 665 deletions

View File

@ -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
}

View File

@ -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))

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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)
}
}

View File

@ -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)))
}
}
})
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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()

View File

@ -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

View File

@ -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)
}
}
})
}
}

View File

@ -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)

View File

@ -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 {

View File

@ -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()

View File

@ -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()
}
}
}

View File

@ -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)!

View File

@ -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 {