Merge branch 'master' into gift-resale

# Conflicts:
#	submodules/TelegramUI/Sources/SharedAccountContext.swift
This commit is contained in:
Mikhail Filimonov 2025-04-17 09:39:55 +01:00
commit 27a2f38ff6
62 changed files with 739 additions and 311 deletions

76
.vscode/settings.json vendored
View File

@ -1,5 +1,79 @@
{
"sweetpad.build.xcodeWorkspacePath": "Telegram/Telegram.xcodeproj/project.xcworkspace",
"lldb.library": "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB",
"lldb.launch.expressions": "native"
"lldb.launch.expressions": "native",
"files.associations": {
"__bit_reference": "cpp",
"__hash_table": "cpp",
"__locale": "cpp",
"__node_handle": "cpp",
"__split_buffer": "cpp",
"__threading_support": "cpp",
"__tree": "cpp",
"__verbose_abort": "cpp",
"any": "cpp",
"array": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"cfenv": "cpp",
"charconv": "cpp",
"cinttypes": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"complex": "cpp",
"condition_variable": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"execution": "cpp",
"memory": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"map": "cpp",
"mutex": "cpp",
"new": "cpp",
"optional": "cpp",
"ostream": "cpp",
"print": "cpp",
"queue": "cpp",
"ratio": "cpp",
"regex": "cpp",
"scoped_allocator": "cpp",
"set": "cpp",
"span": "cpp",
"sstream": "cpp",
"stack": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"string": "cpp",
"string_view": "cpp",
"tuple": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"valarray": "cpp",
"variant": "cpp",
"vector": "cpp",
"algorithm": "cpp"
}
}

View File

@ -14182,3 +14182,9 @@ Sorry for the inconvenience.";
"Call.IncomingGroupCallTitle.Multiple_any" = "{} and %d others";
"GroupCall.RevokeLinkText" = "Are you sure you want to revoke this link? Once you do, no one will be able to join the call using it.";
"SendInviteLink.TextCallsRestrictedOneUser" = "%@ does not accept calls.";
"SendInviteLink.TextCallsRestrictedMultipleUsers_1" = "{user_list}, and **%d** more person do not accept calls.";
"SendInviteLink.TextCallsRestrictedMultipleUsers_any" = "{user_list}, and **%d** more people do not accept calls.";
"SendInviteLink.TextCallsRestrictedSendInviteLink" = "You can try to send an invite link instead.";

View File

@ -988,6 +988,11 @@ public enum OldChannelsControllerIntent {
case upgrade
}
public enum SendInviteLinkScreenSubject {
case chat(peer: EnginePeer, link: String?)
case groupCall(link: String)
}
public protocol SharedAccountContext: AnyObject {
var sharedContainerPath: String { get }
var basePath: String { get }
@ -1206,6 +1211,8 @@ public protocol SharedAccountContext: AnyObject {
func makeAccountFreezeInfoScreen(context: AccountContext) -> ViewController
func makeSendInviteLinkScreen(context: AccountContext, subject: SendInviteLinkScreenSubject, peers: [TelegramForbiddenInvitePeer], theme: PresentationTheme?) -> ViewController
func makeDebugSettingsController(context: AccountContext?) -> ViewController?
func openCreateGroupCallUI(context: AccountContext, peerIds: [EnginePeer.Id], parentController: ViewController)

View File

@ -700,10 +700,6 @@ public final class CallListController: TelegramBaseController {
guard case let .conferenceCall(conferenceCall) = action?.action else {
return
}
if conferenceCall.duration != nil {
self.context.sharedContext.openCreateGroupCallUI(context: self.context, peerIds: conferenceCall.otherParticipants, parentController: self)
return
}
if let currentGroupCallController = self.context.sharedContext as? VoiceChatController, case let .group(groupCall) = currentGroupCallController.call, let currentCallId = groupCall.callId, currentCallId == conferenceCall.callId {
self.context.sharedContext.navigateToCurrentCall()

View File

@ -126,7 +126,7 @@ private func mappedInsertEntries(context: AccountContext, presentationData: Item
case let .displayTabInfo(_, text):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: 0), directionHint: entry.directionHint)
case .openNewCall:
let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: PresentationResourcesRootController.callListCallIcon(presentationData.theme), title: presentationData.strings.CallList_NewCall, hasSeparator: false, sectionId: 1, height: .generic, noInsets: true, editing: false, action: {
let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: PresentationResourcesRootController.callListCallIcon(presentationData.theme), title: presentationData.strings.CallList_NewCall, hasSeparator: false, sectionId: 1, height: .generic, noInsets: !showSettings, editing: false, action: {
nodeInteraction.openNewCall()
})
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
@ -150,7 +150,7 @@ private func mappedUpdateEntries(context: AccountContext, presentationData: Item
case let .displayTabInfo(_, text):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: 0), directionHint: entry.directionHint)
case .openNewCall:
let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: PresentationResourcesRootController.callListCallIcon(presentationData.theme), title: presentationData.strings.CallList_NewCall, hasSeparator: false, sectionId: 1, height: .generic, noInsets: true, editing: false, action: {
let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: PresentationResourcesRootController.callListCallIcon(presentationData.theme), title: presentationData.strings.CallList_NewCall, hasSeparator: false, sectionId: 1, height: .generic, noInsets: !showSettings, editing: false, action: {
nodeInteraction.openNewCall()
})
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
@ -525,7 +525,7 @@ final class CallListControllerNode: ASDisplayNode {
|> mapToQueue { (updateAndType, state, groupCalls, showCallsTab, currentGroupCallPeerId) -> Signal<CallListNodeListViewTransition, NoError> in
let (update, type) = updateAndType
let processedView = CallListNodeView(originalView: update.view, filteredEntries: callListNodeEntriesForView(view: update.view, displayOpenNewCall: mode == .tab && type == .all, groupCalls: groupCalls, state: state, showSettings: showSettings, showCallsTab: showCallsTab, isRecentCalls: type == .all, currentGroupCallPeerId: currentGroupCallPeerId), presentationData: state.presentationData)
let processedView = CallListNodeView(originalView: update.view, filteredEntries: callListNodeEntriesForView(view: update.view, displayOpenNewCall: type == .all, groupCalls: groupCalls, state: state, showSettings: showSettings, showCallsTab: showCallsTab, isRecentCalls: type == .all, currentGroupCallPeerId: currentGroupCallPeerId), presentationData: state.presentationData)
let previous = previousView.swap(processedView)
let previousType = previousType.swap(type)

View File

@ -44,14 +44,14 @@ enum CallListNodeEntry: Comparable, Identifiable {
case .openNewCall:
switch rhs {
case .displayTab, .displayTabInfo:
return false
default:
return true
default:
return false
}
case let .groupCall(lhsPeerId, lhsTitle):
switch rhs {
case .displayTab, .displayTabInfo, .openNewCall:
return false
return true
case let .groupCall(rhsPeerId, rhsTitle):
if lhsTitle == rhsTitle {
return lhsPeerId < rhsPeerId
@ -64,7 +64,7 @@ enum CallListNodeEntry: Comparable, Identifiable {
case let .hole(lhsIndex):
switch rhs {
case .displayTab, .displayTabInfo, .groupCall, .openNewCall:
return false
return true
case let .hole(rhsIndex):
return lhsIndex < rhsIndex
case let .message(rhsIndex):
@ -73,7 +73,7 @@ enum CallListNodeEntry: Comparable, Identifiable {
case let .message(lhsIndex):
switch rhs {
case .displayTab, .displayTabInfo, .groupCall, .openNewCall:
return false
return true
case let .hole(rhsIndex):
return lhsIndex < rhsIndex
case let .message(rhsIndex):

View File

@ -306,10 +306,8 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
if conferenceCall.flags.contains(.isMissed) {
messageText = strings.Chat_CallMessage_DeclinedGroupCall
} else if message.timestamp < currentTime - missedTimeout {
} else if conferenceCall.duration == nil && message.timestamp < currentTime - missedTimeout {
messageText = strings.Chat_CallMessage_MissedGroupCall
} else if conferenceCall.duration != nil {
messageText = strings.Chat_CallMessage_CancelledGroupCall
} else {
if incoming {
messageText = strings.Chat_CallMessage_IncomingGroupCall

View File

@ -1247,7 +1247,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
}
if let adminPeer, case let .restricted(forbiddenPeer) = error {
let inviteScreen = SendInviteLinkScreen(context: context, peer: channelPeer, link: exportedInvitation?.link, peers: [forbiddenPeer ?? TelegramForbiddenInvitePeer(peer: adminPeer, canInviteWithPremium: false, premiumRequiredToContact: false)])
let inviteScreen = SendInviteLinkScreen(context: context, subject: .chat(peer: channelPeer, link: exportedInvitation?.link), peers: [forbiddenPeer ?? TelegramForbiddenInvitePeer(peer: adminPeer, canInviteWithPremium: false, premiumRequiredToContact: false)])
pushControllerImpl?(inviteScreen)
dismissImpl?()
@ -1437,7 +1437,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(engine: context.engine, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(rights: updateFlags), rank: updateRank) |> deliverOnMainQueue).start(error: { error in
if case let .addMemberError(addMemberError) = error, case let .restricted(forbiddenPeer) = addMemberError, let admin = adminPeer {
if let channelPeer {
let inviteScreen = SendInviteLinkScreen(context: context, peer: channelPeer, link: exportedInvitation?.link, peers: [forbiddenPeer ?? TelegramForbiddenInvitePeer(peer: admin, canInviteWithPremium: false, premiumRequiredToContact: false)])
let inviteScreen = SendInviteLinkScreen(context: context, subject: .chat(peer: channelPeer, link: exportedInvitation?.link), peers: [forbiddenPeer ?? TelegramForbiddenInvitePeer(peer: admin, canInviteWithPremium: false, premiumRequiredToContact: false)])
pushControllerImpl?(inviteScreen)
dismissImpl?()
@ -1519,7 +1519,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
)
|> deliverOnMainQueue).startStandalone(next: { exportedInvitation in
let _ = exportedInvitation
let inviteScreen = SendInviteLinkScreen(context: context, peer: .legacyGroup(group), link: exportedInvitation?.link, peers: [failedPeer])
let inviteScreen = SendInviteLinkScreen(context: context, subject: .chat(peer: .legacyGroup(group), link: exportedInvitation?.link), peers: [failedPeer])
pushControllerImpl?(inviteScreen)
})
} else {

View File

@ -560,7 +560,7 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
if !failedPeers.isEmpty, let contactsController, let navigationController = contactsController.navigationController as? NavigationController {
var viewControllers = navigationController.viewControllers
if let index = viewControllers.firstIndex(where: { $0 === contactsController }) {
let inviteScreen = SendInviteLinkScreen(context: context, peer: chatPeer, link: exportedInvitation?.link, peers: failedPeers)
let inviteScreen = SendInviteLinkScreen(context: context, subject: .chat(peer: chatPeer, link: exportedInvitation?.link), peers: failedPeers)
viewControllers.remove(at: index)
viewControllers.append(inviteScreen)
navigationController.setViewControllers(viewControllers, animated: true)

View File

@ -356,8 +356,8 @@ private final class ConferenceCallE2EContextStateImpl: ConferenceCallE2EContextS
return self.call.takeOutgoingBroadcastBlocks()
}
func encrypt(message: Data, channelId: Int32) -> Data? {
return self.call.encrypt(message, channelId: channelId)
func encrypt(message: Data, channelId: Int32, plaintextPrefixLength: Int) -> Data? {
return self.call.encrypt(message, channelId: channelId, plaintextPrefixLength: plaintextPrefixLength)
}
func decrypt(message: Data, userId: Int64) -> Data? {
@ -374,9 +374,9 @@ class OngoingGroupCallEncryptionContextImpl: OngoingGroupCallEncryptionContext {
self.channelId = channelId
}
func encrypt(message: Data) -> Data? {
func encrypt(message: Data, plaintextPrefixLength: Int) -> Data? {
let channelId = self.channelId
return self.e2eCall.with({ $0.state?.encrypt(message: message, channelId: channelId) })
return self.e2eCall.with({ $0.state?.encrypt(message: message, channelId: channelId, plaintextPrefixLength: plaintextPrefixLength) })
}
func decrypt(message: Data, userId: Int64) -> Data? {
@ -3620,14 +3620,24 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
let presentationData = self.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
var errorText = presentationData.strings.Login_UnknownError
switch error {
case let .privacy(peer):
if let peer {
errorText = presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).string
if let currentInviteLinks = self.currentInviteLinks {
let inviteLinkScreen = self.accountContext.sharedContext.makeSendInviteLinkScreen(context: self.accountContext, subject: .groupCall(link: currentInviteLinks.listenerLink), peers: [TelegramForbiddenInvitePeer(peer: peer, canInviteWithPremium: false, premiumRequiredToContact: false)], theme: defaultDarkColorPresentationTheme)
if let navigationController = self.accountContext.sharedContext.mainWindow?.viewController as? NavigationController {
navigationController.pushViewController(inviteLinkScreen)
}
return
} else {
errorText = presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).string
}
}
default:
break
}
self.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})
]), on: .root, blockInteraction: false, completion: {})
@ -3752,6 +3762,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
}
public var currentInviteLinks: GroupCallInviteLinks?
private var currentMyAudioLevel: Float = 0.0
private var currentMyAudioLevelTimestamp: Double = 0.0
private var isSendingTyping: Bool = false

View File

@ -1530,6 +1530,9 @@ final class VideoChatScreenComponent: Component {
return
}
self.inviteLinks = value
if case let .group(groupCall) = self.currentCall, let groupCall = groupCall as? PresentationGroupCallImpl {
groupCall.currentInviteLinks = value
}
})
self.reconnectedAsEventsDisposable?.dispose()

View File

@ -13,7 +13,7 @@ public protocol ConferenceCallE2EContextState: AnyObject {
func takeOutgoingBroadcastBlocks() -> [Data]
func encrypt(message: Data, channelId: Int32) -> Data?
func encrypt(message: Data, channelId: Int32, plaintextPrefixLength: Int) -> Data?
func decrypt(message: Data, userId: Int64) -> Data?
}
@ -379,7 +379,7 @@ public final class ConferenceCallE2EContext {
// Peer ids that are in the blockchain but not in the server list
var removedPeerIds = blockchainPeerIds.filter { blockchainPeerId in
return !result.participants.contains(where: { participant in
if case let .peer(id) = participant.id, id.namespace == Namespaces.Peer.CloudChannel, id.id._internalGetInt64Value() == blockchainPeerId {
if case let .peer(id) = participant.id, id.namespace == Namespaces.Peer.CloudUser, id.id._internalGetInt64Value() == blockchainPeerId {
return true
} else {
return false

View File

@ -200,7 +200,7 @@ func _internal_joinCallInvitationInformation(account: Account, messageId: Messag
}
var members: [EnginePeer] = []
for participant in call.topParticipants {
if let peer = participant.peer {
if let peer = participant.peer, peer.id != account.peerId {
members.append(peer)
}
}

View File

@ -625,10 +625,8 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
if conferenceCall.flags.contains(.isMissed) {
titleString = strings.Chat_CallMessage_DeclinedGroupCall
} else if message.timestamp < currentTime - missedTimeout {
} else if conferenceCall.duration == nil && message.timestamp < currentTime - missedTimeout {
titleString = strings.Chat_CallMessage_MissedGroupCall
} else if conferenceCall.duration != nil {
titleString = strings.Chat_CallMessage_CancelledGroupCall
} else {
if incoming {
titleString = strings.Chat_CallMessage_IncomingGroupCall

View File

@ -174,10 +174,8 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
let currentTime = Int32(Date().timeIntervalSince1970)
if conferenceCall.flags.contains(.isMissed) {
titleString = item.presentationData.strings.Chat_CallMessage_DeclinedGroupCall
} else if item.message.timestamp < currentTime - missedTimeout {
} else if conferenceCall.duration == nil && item.message.timestamp < currentTime - missedTimeout {
titleString = item.presentationData.strings.Chat_CallMessage_MissedGroupCall
} else if conferenceCall.duration != nil {
titleString = item.presentationData.strings.Chat_CallMessage_CancelledGroupCall
} else {
if incoming {
titleString = item.presentationData.strings.Chat_CallMessage_IncomingGroupCall

View File

@ -14182,7 +14182,7 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa
if !failedPeers.isEmpty, let contactsController, let navigationController = contactsController.navigationController as? NavigationController {
var viewControllers = navigationController.viewControllers
if let index = viewControllers.firstIndex(where: { $0 === contactsController }) {
let inviteScreen = SendInviteLinkScreen(context: context, peer: EnginePeer(groupPeer), link: exportedInvitation?.link, peers: failedPeers)
let inviteScreen = SendInviteLinkScreen(context: context, subject: .chat(peer: EnginePeer(groupPeer), link: exportedInvitation?.link), peers: failedPeers)
viewControllers.remove(at: index)
viewControllers.append(inviteScreen)
navigationController.setViewControllers(viewControllers, animated: true)

View File

@ -23,23 +23,20 @@ private final class SendInviteLinkScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let peer: EnginePeer
let link: String?
let subject: SendInviteLinkScreenSubject
let peers: [TelegramForbiddenInvitePeer]
let peerPresences: [EnginePeer.Id: EnginePeer.Presence]
let sendPaidMessageStars: [EnginePeer.Id: StarsAmount]
init(
context: AccountContext,
peer: EnginePeer,
link: String?,
subject: SendInviteLinkScreenSubject,
peers: [TelegramForbiddenInvitePeer],
peerPresences: [EnginePeer.Id: EnginePeer.Presence],
sendPaidMessageStars: [EnginePeer.Id: StarsAmount]
) {
self.context = context
self.peer = peer
self.link = link
self.subject = subject
self.peers = peers
self.peerPresences = peerPresences
self.sendPaidMessageStars = sendPaidMessageStars
@ -49,9 +46,6 @@ private final class SendInviteLinkScreenComponent: Component {
if lhs.context !== rhs.context {
return false
}
if lhs.link != rhs.link {
return false
}
if lhs.peers != rhs.peers {
return false
}
@ -315,7 +309,12 @@ private final class SendInviteLinkScreenComponent: Component {
if self.component == nil {
for peer in component.peers {
if component.link != nil && !peer.premiumRequiredToContact {
switch component.subject {
case let .chat(_, link):
if link != nil && !peer.premiumRequiredToContact {
self.selectedItems.insert(peer.peer.id)
}
case .groupCall:
self.selectedItems.insert(peer.peer.id)
}
}
@ -329,10 +328,15 @@ private final class SendInviteLinkScreenComponent: Component {
return peer.canInviteWithPremium
}
var hasInviteLink = true
if premiumRestrictedUsers.count == component.peers.count && component.link == nil {
hasInviteLink = false
} else if component.link != nil && !premiumRestrictedUsers.isEmpty && component.peers.allSatisfy({ $0.premiumRequiredToContact }) {
hasInviteLink = false
switch component.subject {
case let .chat(_, link):
if premiumRestrictedUsers.count == component.peers.count && link == nil {
hasInviteLink = false
} else if link != nil && !premiumRestrictedUsers.isEmpty && component.peers.allSatisfy({ $0.premiumRequiredToContact }) {
hasInviteLink = false
}
case .groupCall:
hasInviteLink = true
}
if themeUpdated {
@ -453,48 +457,85 @@ private final class SendInviteLinkScreenComponent: Component {
contentHeight += 8.0
let text: String
if premiumRestrictedUsers.count == 1 {
if case let .channel(channel) = component.peer, case .broadcast = channel.info {
text = environment.strings.SendInviteLink_ChannelTextContactsAndPremiumOneUser(premiumRestrictedUsers[0].peer.compactDisplayTitle).string
} else {
text = environment.strings.SendInviteLink_TextContactsAndPremiumOneUser(premiumRestrictedUsers[0].peer.compactDisplayTitle).string
}
} else {
let extraCount = premiumRestrictedUsers.count - 3
var peersTextArray: [String] = []
for i in 0 ..< min(3, premiumRestrictedUsers.count) {
peersTextArray.append("**\(premiumRestrictedUsers[i].peer.compactDisplayTitle)**")
}
var peersText = ""
if #available(iOS 13.0, *) {
let listFormatter = ListFormatter()
listFormatter.locale = localeWithStrings(environment.strings)
if let value = listFormatter.string(from: peersTextArray) {
peersText = value
switch component.subject {
case let .chat(peer, _):
if premiumRestrictedUsers.count == 1 {
if case let .channel(channel) = peer, case .broadcast = channel.info {
text = environment.strings.SendInviteLink_ChannelTextContactsAndPremiumOneUser(premiumRestrictedUsers[0].peer.compactDisplayTitle).string
} else {
text = environment.strings.SendInviteLink_TextContactsAndPremiumOneUser(premiumRestrictedUsers[0].peer.compactDisplayTitle).string
}
}
if peersText.isEmpty {
for i in 0 ..< peersTextArray.count {
if i != 0 {
peersText.append(", ")
} else {
let extraCount = premiumRestrictedUsers.count - 3
var peersTextArray: [String] = []
for i in 0 ..< min(3, premiumRestrictedUsers.count) {
peersTextArray.append("**\(premiumRestrictedUsers[i].peer.compactDisplayTitle)**")
}
var peersText = ""
if #available(iOS 13.0, *) {
let listFormatter = ListFormatter()
listFormatter.locale = localeWithStrings(environment.strings)
if let value = listFormatter.string(from: peersTextArray) {
peersText = value
}
}
if peersText.isEmpty {
for i in 0 ..< peersTextArray.count {
if i != 0 {
peersText.append(", ")
}
peersText.append(peersTextArray[i])
}
}
if extraCount >= 1 {
if case let .channel(channel) = peer, case .broadcast = channel.info {
text = environment.strings.SendInviteLink_ChannelTextContactsAndPremiumMultipleUsers(Int32(extraCount)).replacingOccurrences(of: "{user_list}", with: peersText)
} else {
text = environment.strings.SendInviteLink_TextContactsAndPremiumMultipleUsers(Int32(extraCount)).replacingOccurrences(of: "{user_list}", with: peersText)
}
} else {
if case let .channel(channel) = peer, case .broadcast = channel.info {
text = environment.strings.SendInviteLink_ChannelTextContactsAndPremiumOneUser(peersText).string
} else {
text = environment.strings.SendInviteLink_TextContactsAndPremiumOneUser(peersText).string
}
peersText.append(peersTextArray[i])
}
}
if extraCount >= 1 {
if case let .channel(channel) = component.peer, case .broadcast = channel.info {
text = environment.strings.SendInviteLink_ChannelTextContactsAndPremiumMultipleUsers(Int32(extraCount)).replacingOccurrences(of: "{user_list}", with: peersText)
} else {
text = environment.strings.SendInviteLink_TextContactsAndPremiumMultipleUsers(Int32(extraCount)).replacingOccurrences(of: "{user_list}", with: peersText)
}
case .groupCall:
if premiumRestrictedUsers.count == 1 {
text = environment.strings.SendInviteLink_TextCallsRestrictedOneUser(premiumRestrictedUsers[0].peer.compactDisplayTitle).string
} else {
if case let .channel(channel) = component.peer, case .broadcast = channel.info {
text = environment.strings.SendInviteLink_ChannelTextContactsAndPremiumOneUser(peersText).string
let extraCount = premiumRestrictedUsers.count - 3
var peersTextArray: [String] = []
for i in 0 ..< min(3, premiumRestrictedUsers.count) {
peersTextArray.append("**\(premiumRestrictedUsers[i].peer.compactDisplayTitle)**")
}
var peersText = ""
if #available(iOS 13.0, *) {
let listFormatter = ListFormatter()
listFormatter.locale = localeWithStrings(environment.strings)
if let value = listFormatter.string(from: peersTextArray) {
peersText = value
}
}
if peersText.isEmpty {
for i in 0 ..< peersTextArray.count {
if i != 0 {
peersText.append(", ")
}
peersText.append(peersTextArray[i])
}
}
if extraCount >= 1 {
text = environment.strings.SendInviteLink_TextCallsRestrictedMultipleUsers(Int32(extraCount)).replacingOccurrences(of: "{user_list}", with: peersText)
} else {
text = environment.strings.SendInviteLink_TextContactsAndPremiumOneUser(peersText).string
text = environment.strings.SendInviteLink_TextCallsRestrictedOneUser(peersText).string
}
}
}
@ -693,11 +734,19 @@ private final class SendInviteLinkScreenComponent: Component {
actionButton = ComponentView()
self.actionButton = actionButton
}
let titleText: String
switch component.subject {
case let .chat(_, link):
titleText = link != nil ? environment.strings.SendInviteLink_InviteTitle : environment.strings.SendInviteLink_LinkUnavailableTitle
case .groupCall:
titleText = environment.strings.SendInviteLink_InviteTitle
}
let titleSize = title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: component.link != nil ? environment.strings.SendInviteLink_InviteTitle : environment.strings.SendInviteLink_LinkUnavailableTitle, font: Font.semibold(24.0), textColor: environment.theme.list.itemPrimaryTextColor))
text: .plain(NSAttributedString(string: titleText, font: Font.semibold(24.0), textColor: environment.theme.list.itemPrimaryTextColor))
)),
environment: {},
containerSize: CGSize(width: availableSize.width - leftButtonFrame.maxX * 2.0, height: 100.0)
@ -714,30 +763,35 @@ private final class SendInviteLinkScreenComponent: Component {
contentHeight += 8.0
let text: String
if !premiumRestrictedUsers.isEmpty {
if component.link != nil {
text = environment.strings.SendInviteLink_TextSendInviteLink
} else {
if component.peers.count == 1 {
text = environment.strings.SendInviteLink_TextUnavailableSingleUser(component.peers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)).string
switch component.subject {
case let .chat(_, link):
if !premiumRestrictedUsers.isEmpty {
if link != nil {
text = environment.strings.SendInviteLink_TextSendInviteLink
} else {
text = environment.strings.SendInviteLink_TextUnavailableMultipleUsers(Int32(component.peers.count))
}
}
} else {
if component.link != nil {
if component.peers.count == 1 {
text = environment.strings.SendInviteLink_TextAvailableSingleUser(component.peers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)).string
} else {
text = environment.strings.SendInviteLink_TextAvailableMultipleUsers(Int32(component.peers.count))
if component.peers.count == 1 {
text = environment.strings.SendInviteLink_TextUnavailableSingleUser(component.peers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)).string
} else {
text = environment.strings.SendInviteLink_TextUnavailableMultipleUsers(Int32(component.peers.count))
}
}
} else {
if component.peers.count == 1 {
text = environment.strings.SendInviteLink_TextUnavailableSingleUser(component.peers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)).string
if link != nil {
if component.peers.count == 1 {
text = environment.strings.SendInviteLink_TextAvailableSingleUser(component.peers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)).string
} else {
text = environment.strings.SendInviteLink_TextAvailableMultipleUsers(Int32(component.peers.count))
}
} else {
text = environment.strings.SendInviteLink_TextUnavailableMultipleUsers(Int32(component.peers.count))
if component.peers.count == 1 {
text = environment.strings.SendInviteLink_TextUnavailableSingleUser(component.peers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)).string
} else {
text = environment.strings.SendInviteLink_TextUnavailableMultipleUsers(Int32(component.peers.count))
}
}
}
case .groupCall:
text = environment.strings.SendInviteLink_TextCallsRestrictedSendInviteLink
}
let body = MarkdownAttributeSet(font: Font.regular(15.0), textColor: environment.theme.list.itemPrimaryTextColor)
@ -793,7 +847,13 @@ private final class SendInviteLinkScreenComponent: Component {
}
let itemSubtitle: PeerListItemComponent.Subtitle
let canBeSelected = component.link != nil && !peer.premiumRequiredToContact
let canBeSelected : Bool
switch component.subject {
case let .chat(_, link):
canBeSelected = link != nil && !peer.premiumRequiredToContact
case .groupCall:
canBeSelected = true
}
if peer.premiumRequiredToContact {
itemSubtitle = .text(text: environment.strings.SendInviteLink_StatusAvailableToPremiumOnly, icon: .lock)
} else {
@ -862,16 +922,24 @@ private final class SendInviteLinkScreenComponent: Component {
initialContentHeight += 24.0
let actionButtonTitle: String
if component.link != nil {
actionButtonTitle = self.selectedItems.isEmpty ? environment.strings.SendInviteLink_ActionSkip : environment.strings.SendInviteLink_ActionInvite
} else {
actionButtonTitle = environment.strings.SendInviteLink_ActionClose
let actionButtonBadge: String?
switch component.subject {
case let.chat(_, link):
if link != nil {
actionButtonTitle = self.selectedItems.isEmpty ? environment.strings.SendInviteLink_ActionSkip : environment.strings.SendInviteLink_ActionInvite
} else {
actionButtonTitle = environment.strings.SendInviteLink_ActionClose
}
actionButtonBadge = (self.selectedItems.isEmpty || link == nil) ? nil : "\(self.selectedItems.count)"
case .groupCall:
actionButtonTitle = environment.strings.SendInviteLink_ActionInvite
actionButtonBadge = self.selectedItems.isEmpty ? nil : "\(self.selectedItems.count)"
}
let actionButtonSize = actionButton.update(
transition: transition,
component: AnyComponent(SolidRoundedButtonComponent(
title: actionButtonTitle,
badge: (self.selectedItems.isEmpty || component.link == nil) ? nil : "\(self.selectedItems.count)",
badge: actionButtonBadge,
theme: SolidRoundedButtonComponent.Theme(theme: environment.theme),
font: .bold,
fontSize: 17.0,
@ -885,9 +953,18 @@ private final class SendInviteLinkScreenComponent: Component {
guard let self, let component = self.component, let controller = self.environment?.controller() else {
return
}
let link: String?
switch component.subject {
case let .chat(_, linkValue):
link = linkValue
case let .groupCall(linkValue):
link = linkValue
}
if self.selectedItems.isEmpty {
controller.dismiss()
} else if let link = component.link {
} else if let link {
let selectedPeers = component.peers.filter { self.selectedItems.contains($0.peer.id) }
self.presentPaidMessageAlertIfNeeded(
@ -1012,15 +1089,21 @@ public class SendInviteLinkScreen: ViewControllerComponentContainer {
private var presenceDisposable: Disposable?
public init(context: AccountContext, peer: EnginePeer, link: String?, peers: [TelegramForbiddenInvitePeer]) {
public init(context: AccountContext, subject: SendInviteLinkScreenSubject, peers: [TelegramForbiddenInvitePeer], theme: PresentationTheme? = nil) {
self.context = context
var link = link
if link == nil, let addressName = peer.addressName {
link = "https://t.me/\(addressName)"
switch subject {
case let .chat(peer, link):
var link = link
if link == nil, let addressName = peer.addressName {
link = "https://t.me/\(addressName)"
}
self.link = link
case let .groupCall(link):
self.link = link
}
#if DEBUG
#if DEBUG && false
var peers = peers
if !"".isEmpty {
@ -1136,10 +1219,9 @@ public class SendInviteLinkScreen: ViewControllerComponentContainer {
}
#endif
self.link = link
self.peers = peers
super.init(context: context, component: SendInviteLinkScreenComponent(context: context, peer: peer, link: link, peers: peers, peerPresences: [:], sendPaidMessageStars: [:]), navigationBarAppearance: .none)
super.init(context: context, component: SendInviteLinkScreenComponent(context: context, subject: subject, peers: peers, peerPresences: [:], sendPaidMessageStars: [:]), navigationBarAppearance: .none, theme: theme.flatMap { .custom($0) } ?? .default)
self.statusBar.statusBarStyle = .Ignore
self.navigationPresentation = .flatModal
@ -1169,7 +1251,7 @@ public class SendInviteLinkScreen: ViewControllerComponentContainer {
parsedSendPaidMessageStars[id] = sendPaidMessageStars
}
}
self.updateComponent(component: AnyComponent(SendInviteLinkScreenComponent(context: context, peer: peer, link: link, peers: peers, peerPresences: parsedPresences, sendPaidMessageStars: parsedSendPaidMessageStars)), transition: .immediate)
self.updateComponent(component: AnyComponent(SendInviteLinkScreenComponent(context: context, subject: subject, peers: peers, peerPresences: parsedPresences, sendPaidMessageStars: parsedSendPaidMessageStars)), transition: .immediate)
})
}

View File

@ -2893,10 +2893,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard case let .conferenceCall(conferenceCall) = action?.action else {
return
}
if conferenceCall.duration != nil {
self.context.sharedContext.openCreateGroupCallUI(context: self.context, peerIds: conferenceCall.otherParticipants, parentController: self)
return
}
if let currentGroupCallController = self.context.sharedContext.currentGroupCallController as? VoiceChatController, case let .group(groupCall) = currentGroupCallController.call, let currentCallId = groupCall.callId, currentCallId == conferenceCall.callId {
self.context.sharedContext.navigateToCurrentCall()

View File

@ -2733,7 +2733,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
var messageIdsWithUnseenPersonalMention: [MessageId] = []
var messageIdsWithUnseenReactions: [MessageId] = []
var messageIdsWithInactiveExtendedMedia = Set<MessageId>()
var messageIdsWithGroupCalls: [MessageId] = []
var downloadableResourceIds: [(messageId: MessageId, resourceId: String)] = []
var allVisibleAnchorMessageIds: [(MessageId, Int)] = []
var visibleAdOpaqueIds: [Data] = []
@ -2832,13 +2831,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
storiesRequiredValidation = true
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, let _ = content.story {
storiesRequiredValidation = true
} else if let media = media as? TelegramMediaAction {
if case let .conferenceCall(conferenceCall) = media.action {
if conferenceCall.duration != nil {
} else {
messageIdsWithGroupCalls.append(message.id)
}
}
}
}
if contentRequiredValidation {
@ -3096,9 +3088,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
if !peerIdsWithRefreshStories.isEmpty {
self.context.account.viewTracker.refreshStoryStatsForPeerIds(peerIds: peerIdsWithRefreshStories)
}
if !messageIdsWithGroupCalls.isEmpty {
self.inlineGroupCallsProcessingManager.add(messageIdsWithGroupCalls.map { MessageAndThreadId(messageId: $0, threadId: nil) })
}
self.currentEarlierPrefetchMessages = toEarlierMediaMessages
self.currentLaterPrefetchMessages = toLaterMediaMessages

View File

@ -850,7 +850,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|> deliverOnMainQueue).start(next: { peer in
if let peer, let exportedInvitation, let link = exportedInvitation.link {
let inviteScreen = SendInviteLinkScreen(context: context, peer: peer, link: link, peers: result.result.forbiddenPeers)
let inviteScreen = SendInviteLinkScreen(context: context, subject: .chat(peer: peer, link: link), peers: result.result.forbiddenPeers)
controller?.push(inviteScreen)
}
})

View File

@ -82,6 +82,8 @@ import JoinSubjectScreen
import OldChannelsController
import InviteLinksUI
import GiftStoreScreen
import SendInviteLinkScreen
private final class AccountUserInterfaceInUseContext {
let subscribers = Bag<(Bool) -> Void>()
@ -3833,6 +3835,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
public func makeAccountFreezeInfoScreen(context: AccountContext) -> ViewController {
return AccountFreezeInfoScreen(context: context)
}
public func makeSendInviteLinkScreen(context: AccountContext, subject: SendInviteLinkScreenSubject, peers: [TelegramForbiddenInvitePeer], theme: PresentationTheme?) -> ViewController {
return SendInviteLinkScreen(context: context, subject: subject, peers: peers, theme: theme)
}
}
private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? {

View File

@ -217,7 +217,7 @@ final class OngoingGroupCallBroadcastPartTaskImpl: NSObject, OngoingGroupCallBro
}
public protocol OngoingGroupCallEncryptionContext: AnyObject {
func encrypt(message: Data) -> Data?
func encrypt(message: Data, plaintextPrefixLength: Int) -> Data?
func decrypt(message: Data, userId: Int64) -> Data?
}
@ -645,9 +645,9 @@ public final class OngoingGroupCallContext {
isConference: isConference,
isActiveByDefault: audioIsActiveByDefault,
encryptDecrypt: encryptionContext.flatMap { encryptionContext in
return { data, userId, isEncrypt in
return { data, userId, isEncrypt, plaintextPrefixLength in
if isEncrypt {
return encryptionContext.encrypt(message: data)
return encryptionContext.encrypt(message: data, plaintextPrefixLength: Int(plaintextPrefixLength))
} else {
return encryptionContext.decrypt(message: data, userId: userId)
}
@ -755,9 +755,9 @@ public final class OngoingGroupCallContext {
isConference: isConference,
isActiveByDefault: audioIsActiveByDefault,
encryptDecrypt: encryptionContext.flatMap { encryptionContext in
return { data, userId, isEncrypt in
return { data, userId, isEncrypt, plaintextPrefixLength in
if isEncrypt {
return encryptionContext.encrypt(message: data)
return encryptionContext.encrypt(message: data, plaintextPrefixLength: Int(plaintextPrefixLength))
} else {
return encryptionContext.decrypt(message: data, userId: userId)
}

View File

@ -457,7 +457,7 @@ onMutedSpeechActivityDetected:(void (^ _Nullable)(bool))onMutedSpeechActivityDet
audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice
isConference:(bool)isConference
isActiveByDefault:(bool)isActiveByDefault
encryptDecrypt:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull, int64_t, bool))encryptDecrypt;
encryptDecrypt:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull, int64_t, bool, int32_t))encryptDecrypt;
- (void)stop:(void (^ _Nullable)())completion;

View File

@ -2389,7 +2389,7 @@ onMutedSpeechActivityDetected:(void (^ _Nullable)(bool))onMutedSpeechActivityDet
audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice
isConference:(bool)isConference
isActiveByDefault:(bool)isActiveByDefault
encryptDecrypt:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull, int64_t, bool))encryptDecrypt {
encryptDecrypt:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull, int64_t, bool, int32_t))encryptDecrypt {
self = [super init];
if (self != nil) {
_queue = queue;
@ -2458,12 +2458,12 @@ encryptDecrypt:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull, int64_t, boo
std::string statsLogPathValue(statsLogPath.length == 0 ? "" : statsLogPath.UTF8String);
std::function<std::vector<uint8_t>(std::vector<uint8_t> const &, int64_t, bool)> mappedEncryptDecrypt;
std::function<std::vector<uint8_t>(std::vector<uint8_t> const &, int64_t, bool, int32_t)> mappedEncryptDecrypt;
if (encryptDecrypt) {
NSData * _Nullable (^encryptDecryptBlock)(NSData * _Nonnull, int64_t, bool) = [encryptDecrypt copy];
mappedEncryptDecrypt = [encryptDecryptBlock](std::vector<uint8_t> const &message, int64_t userId, bool isEncrypt) -> std::vector<uint8_t> {
NSData * _Nullable (^encryptDecryptBlock)(NSData * _Nonnull, int64_t, bool, int32_t) = [encryptDecrypt copy];
mappedEncryptDecrypt = [encryptDecryptBlock](std::vector<uint8_t> const &message, int64_t userId, bool isEncrypt, int32_t plaintextPrefixLength) -> std::vector<uint8_t> {
NSData *mappedMessage = [[NSData alloc] initWithBytes:message.data() length:message.size()];
NSData *result = encryptDecryptBlock(mappedMessage, userId, isEncrypt);
NSData *result = encryptDecryptBlock(mappedMessage, userId, isEncrypt, plaintextPrefixLength);
if (!result) {
return std::vector<uint8_t>();
}

View File

@ -42,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable NSData *)generateRemoveParticipantsBlock:(NSArray<NSNumber *> *)participantIds;
- (nullable NSData *)encrypt:(NSData *)message channelId:(int32_t)channelId;
- (nullable NSData *)encrypt:(NSData *)message channelId:(int32_t)channelId plaintextPrefixLength:(NSInteger)plaintextPrefixLength;
- (nullable NSData *)decrypt:(NSData *)message userId:(int64_t)userId;
@end

View File

@ -101,6 +101,42 @@ class ok final : public Object {
void store(TlStorerToString &s, const char *field_name) const final;
};
class e2e_callPacket final : public Object {
std::int32_t get_id() const final {
return ID;
}
public:
static const std::int32_t ID = 1084669673;
static object_ptr<e2e_callPacket> fetch(TlParser &p);
void store(TlStorerCalcLength &s) const final;
void store(TlStorerUnsafe &s) const final;
void store(TlStorerToString &s, const char *field_name) const final;
};
class e2e_callPacketLargeMsgId final : public Object {
std::int32_t get_id() const final {
return ID;
}
public:
static const std::int32_t ID = 484797485;
static object_ptr<e2e_callPacketLargeMsgId> fetch(TlParser &p);
void store(TlStorerCalcLength &s) const final;
void store(TlStorerUnsafe &s) const final;
void store(TlStorerToString &s, const char *field_name) const final;
};
class e2e_HandshakePrivate: public Object {
public:

View File

@ -852,6 +852,23 @@ class authorizationStateWaitPhoneNumber final : public AuthorizationState {
void store(TlStorerToString &s, const char *field_name) const final;
};
class authorizationStateWaitPremiumPurchase final : public AuthorizationState {
std::int32_t get_id() const final {
return ID;
}
public:
string store_product_id_;
authorizationStateWaitPremiumPurchase();
explicit authorizationStateWaitPremiumPurchase(string const &store_product_id_);
static const std::int32_t ID = -862487743;
void store(TlStorerToString &s, const char *field_name) const final;
};
class authorizationStateWaitEmailAddress final : public AuthorizationState {
std::int32_t get_id() const final {
return ID;
@ -8687,6 +8704,7 @@ class forumTopic final : public Object {
public:
object_ptr<forumTopicInfo> info_;
object_ptr<message> last_message_;
int64 order_;
bool is_pinned_;
int32 unread_count_;
int53 last_read_inbox_message_id_;
@ -8698,9 +8716,9 @@ class forumTopic final : public Object {
forumTopic();
forumTopic(object_ptr<forumTopicInfo> &&info_, object_ptr<message> &&last_message_, bool is_pinned_, int32 unread_count_, int53 last_read_inbox_message_id_, int53 last_read_outbox_message_id_, int32 unread_mention_count_, int32 unread_reaction_count_, object_ptr<chatNotificationSettings> &&notification_settings_, object_ptr<draftMessage> &&draft_message_);
forumTopic(object_ptr<forumTopicInfo> &&info_, object_ptr<message> &&last_message_, int64 order_, bool is_pinned_, int32 unread_count_, int53 last_read_inbox_message_id_, int53 last_read_outbox_message_id_, int32 unread_mention_count_, int32 unread_reaction_count_, object_ptr<chatNotificationSettings> &&notification_settings_, object_ptr<draftMessage> &&draft_message_);
static const std::int32_t ID = 303279334;
static const std::int32_t ID = -2094608976;
void store(TlStorerToString &s, const char *field_name) const final;
};
@ -8733,6 +8751,7 @@ class forumTopicInfo final : public Object {
}
public:
int53 chat_id_;
int53 message_thread_id_;
string name_;
object_ptr<forumTopicIcon> icon_;
@ -8745,9 +8764,9 @@ class forumTopicInfo final : public Object {
forumTopicInfo();
forumTopicInfo(int53 message_thread_id_, string const &name_, object_ptr<forumTopicIcon> &&icon_, int32 creation_date_, object_ptr<MessageSender> &&creator_id_, bool is_general_, bool is_outgoing_, bool is_closed_, bool is_hidden_);
forumTopicInfo(int53 chat_id_, int53 message_thread_id_, string const &name_, object_ptr<forumTopicIcon> &&icon_, int32 creation_date_, object_ptr<MessageSender> &&creator_id_, bool is_general_, bool is_outgoing_, bool is_closed_, bool is_hidden_);
static const std::int32_t ID = -1879842914;
static const std::int32_t ID = -654857606;
void store(TlStorerToString &s, const char *field_name) const final;
};
@ -26213,6 +26232,46 @@ class storePaymentPurposeGiftedStars final : public StorePaymentPurpose {
void store(TlStorerToString &s, const char *field_name) const final;
};
class StoreTransaction: public Object {
public:
};
class storeTransactionAppStore final : public StoreTransaction {
std::int32_t get_id() const final {
return ID;
}
public:
bytes receipt_;
storeTransactionAppStore();
explicit storeTransactionAppStore(bytes const &receipt_);
static const std::int32_t ID = 1625562441;
void store(TlStorerToString &s, const char *field_name) const final;
};
class storeTransactionGooglePlay final : public StoreTransaction {
std::int32_t get_id() const final {
return ID;
}
public:
string package_name_;
string store_product_id_;
string purchase_token_;
storeTransactionGooglePlay();
storeTransactionGooglePlay(string const &package_name_, string const &store_product_id_, string const &purchase_token_);
static const std::int32_t ID = 1094018617;
void store(TlStorerToString &s, const char *field_name) const final;
};
class story;
class stories final : public Object {
@ -29909,14 +29968,34 @@ class updateForumTopicInfo final : public Update {
}
public:
int53 chat_id_;
object_ptr<forumTopicInfo> info_;
updateForumTopicInfo();
updateForumTopicInfo(int53 chat_id_, object_ptr<forumTopicInfo> &&info_);
explicit updateForumTopicInfo(object_ptr<forumTopicInfo> &&info_);
static const std::int32_t ID = 1802448073;
static const std::int32_t ID = 1420762696;
void store(TlStorerToString &s, const char *field_name) const final;
};
class updateForumTopic final : public Update {
std::int32_t get_id() const final {
return ID;
}
public:
int53 chat_id_;
int53 message_thread_id_;
bool is_pinned_;
int53 last_read_outbox_message_id_;
object_ptr<chatNotificationSettings> notification_settings_;
updateForumTopic();
updateForumTopic(int53 chat_id_, int53 message_thread_id_, bool is_pinned_, int53 last_read_outbox_message_id_, object_ptr<chatNotificationSettings> &&notification_settings_);
static const std::int32_t ID = 807069971;
void store(TlStorerToString &s, const char *field_name) const final;
};
@ -33817,48 +33896,24 @@ class applyPremiumGiftCode final : public Function {
class StorePaymentPurpose;
class StoreTransaction;
class ok;
class assignAppStoreTransaction final : public Function {
class assignStoreTransaction final : public Function {
std::int32_t get_id() const final {
return ID;
}
public:
bytes receipt_;
object_ptr<StoreTransaction> transaction_;
object_ptr<StorePaymentPurpose> purpose_;
assignAppStoreTransaction();
assignStoreTransaction();
assignAppStoreTransaction(bytes const &receipt_, object_ptr<StorePaymentPurpose> &&purpose_);
assignStoreTransaction(object_ptr<StoreTransaction> &&transaction_, object_ptr<StorePaymentPurpose> &&purpose_);
static const std::int32_t ID = -2030892112;
using ReturnType = object_ptr<ok>;
void store(TlStorerToString &s, const char *field_name) const final;
};
class StorePaymentPurpose;
class ok;
class assignGooglePlayTransaction final : public Function {
std::int32_t get_id() const final {
return ID;
}
public:
string package_name_;
string store_product_id_;
string purchase_token_;
object_ptr<StorePaymentPurpose> purpose_;
assignGooglePlayTransaction();
assignGooglePlayTransaction(string const &package_name_, string const &store_product_id_, string const &purchase_token_, object_ptr<StorePaymentPurpose> &&purpose_);
static const std::int32_t ID = -1992704860;
static const std::int32_t ID = -2046202900;
using ReturnType = object_ptr<ok>;
@ -34274,6 +34329,28 @@ class checkAuthenticationPasswordRecoveryCode final : public Function {
void store(TlStorerToString &s, const char *field_name) const final;
};
class ok;
class checkAuthenticationPremiumPurchase final : public Function {
std::int32_t get_id() const final {
return ID;
}
public:
string currency_;
int53 amount_;
checkAuthenticationPremiumPurchase();
checkAuthenticationPremiumPurchase(string const &currency_, int53 amount_);
static const std::int32_t ID = 1588959934;
using ReturnType = object_ptr<ok>;
void store(TlStorerToString &s, const char *field_name) const final;
};
class chatFolderInviteLinkInfo;
class checkChatFolderInviteLink final : public Function {
@ -47427,6 +47504,32 @@ class setAuthenticationPhoneNumber final : public Function {
void store(TlStorerToString &s, const char *field_name) const final;
};
class StoreTransaction;
class ok;
class setAuthenticationPremiumPurchaseTransaction final : public Function {
std::int32_t get_id() const final {
return ID;
}
public:
object_ptr<StoreTransaction> transaction_;
bool is_restore_;
string currency_;
int53 amount_;
setAuthenticationPremiumPurchaseTransaction();
setAuthenticationPremiumPurchaseTransaction(object_ptr<StoreTransaction> &&transaction_, bool is_restore_, string const &currency_, int53 amount_);
static const std::int32_t ID = -450986887;
using ReturnType = object_ptr<ok>;
void store(TlStorerToString &s, const char *field_name) const final;
};
class NetworkType;
class autoDownloadSettings;

View File

@ -8,9 +8,9 @@
namespace td {
namespace td_api {
void to_json(JsonValueScope &jv, const tl_object_ptr<Object> &value);
void to_json(JsonValueScope &jv, const td_api::object_ptr<Object> &value);
Status from_json(tl_object_ptr<Function> &to, td::JsonValue from);
Status from_json(td_api::object_ptr<Function> &to, td::JsonValue from);
void to_json(JsonValueScope &jv, const Object &object);
@ -166,6 +166,8 @@ Result<int32> tl_constructor_from_string(td_api::StickerType *object, const std:
Result<int32> tl_constructor_from_string(td_api::StorePaymentPurpose *object, const std::string &str);
Result<int32> tl_constructor_from_string(td_api::StoreTransaction *object, const std::string &str);
Result<int32> tl_constructor_from_string(td_api::StoryList *object, const std::string &str);
Result<int32> tl_constructor_from_string(td_api::StoryPrivacySettings *object, const std::string &str);
@ -1294,6 +1296,10 @@ Status from_json(td_api::storePaymentPurposeStars &to, JsonObject &from);
Status from_json(td_api::storePaymentPurposeGiftedStars &to, JsonObject &from);
Status from_json(td_api::storeTransactionAppStore &to, JsonObject &from);
Status from_json(td_api::storeTransactionGooglePlay &to, JsonObject &from);
Status from_json(td_api::storyAreaPosition &to, JsonObject &from);
Status from_json(td_api::storyFullId &to, JsonObject &from);
@ -1580,9 +1586,7 @@ Status from_json(td_api::answerWebAppQuery &to, JsonObject &from);
Status from_json(td_api::applyPremiumGiftCode &to, JsonObject &from);
Status from_json(td_api::assignAppStoreTransaction &to, JsonObject &from);
Status from_json(td_api::assignGooglePlayTransaction &to, JsonObject &from);
Status from_json(td_api::assignStoreTransaction &to, JsonObject &from);
Status from_json(td_api::banChatMember &to, JsonObject &from);
@ -1622,6 +1626,8 @@ Status from_json(td_api::checkAuthenticationPassword &to, JsonObject &from);
Status from_json(td_api::checkAuthenticationPasswordRecoveryCode &to, JsonObject &from);
Status from_json(td_api::checkAuthenticationPremiumPurchase &to, JsonObject &from);
Status from_json(td_api::checkChatFolderInviteLink &to, JsonObject &from);
Status from_json(td_api::checkChatInviteLink &to, JsonObject &from);
@ -2800,6 +2806,8 @@ Status from_json(td_api::setAuthenticationEmailAddress &to, JsonObject &from);
Status from_json(td_api::setAuthenticationPhoneNumber &to, JsonObject &from);
Status from_json(td_api::setAuthenticationPremiumPurchaseTransaction &to, JsonObject &from);
Status from_json(td_api::setAutoDownloadSettings &to, JsonObject &from);
Status from_json(td_api::setAutosaveSettings &to, JsonObject &from);
@ -3264,6 +3272,8 @@ void to_json(JsonValueScope &jv, const td_api::authorizationStateWaitTdlibParame
void to_json(JsonValueScope &jv, const td_api::authorizationStateWaitPhoneNumber &object);
void to_json(JsonValueScope &jv, const td_api::authorizationStateWaitPremiumPurchase &object);
void to_json(JsonValueScope &jv, const td_api::authorizationStateWaitEmailAddress &object);
void to_json(JsonValueScope &jv, const td_api::authorizationStateWaitEmailCode &object);
@ -6140,6 +6150,8 @@ void to_json(JsonValueScope &jv, const td_api::updateQuickReplyShortcutMessages
void to_json(JsonValueScope &jv, const td_api::updateForumTopicInfo &object);
void to_json(JsonValueScope &jv, const td_api::updateForumTopic &object);
void to_json(JsonValueScope &jv, const td_api::updateScopeNotificationSettings &object);
void to_json(JsonValueScope &jv, const td_api::updateReactionNotificationSettings &object);

View File

@ -33,31 +33,57 @@ class AuthManager final : public NetActor {
}
bool is_authorized() const;
bool was_authorized() const;
void get_state(uint64 query_id);
void set_phone_number(uint64 query_id, string phone_number,
td_api::object_ptr<td_api::phoneNumberAuthenticationSettings> settings);
void check_premium_purchase(uint64 query_id, string currency, int64 amount);
void set_premium_purchase_transaction(uint64 query_id, td_api::object_ptr<td_api::StoreTransaction> &&transaction,
bool is_restore, string currency, int64 amount);
void set_firebase_token(uint64 query_id, string token);
void report_missing_code(uint64 query_id, string mobile_network_code);
void set_email_address(uint64 query_id, string email_address);
void resend_authentication_code(uint64 query_id, td_api::object_ptr<td_api::ResendCodeReason> &&reason);
void check_email_code(uint64 query_id, EmailVerification &&code);
void reset_email_address(uint64 query_id);
void check_code(uint64 query_id, string code);
void register_user(uint64 query_id, string first_name, string last_name, bool disable_notification);
void request_qr_code_authentication(uint64 query_id, vector<UserId> other_user_ids);
void check_bot_token(uint64 query_id, string bot_token);
void check_password(uint64 query_id, string password);
void request_password_recovery(uint64 query_id);
void check_password_recovery_code(uint64 query_id, string code);
void recover_password(uint64 query_id, string code, string new_password, string new_hint);
void log_out(uint64 query_id);
void delete_account(uint64 query_id, string reason, string password);
void on_update_login_token();
void on_update_sent_code(telegram_api::object_ptr<telegram_api::auth_SentCode> &&sent_code_ptr);
void on_authorization_lost(string source);
void on_closing(bool destroy_flag);
// can return nullptr if state isn't initialized yet
@ -69,6 +95,7 @@ class AuthManager final : public NetActor {
enum class State : int32 {
None,
WaitPhoneNumber,
WaitPremiumPurchase,
WaitCode,
WaitQrCodeConfirmation,
WaitPassword,
@ -86,6 +113,8 @@ class AuthManager final : public NetActor {
SignIn,
SignUp,
SendCode,
CheckPremiumPurchase,
SetPremiumPurchaseTransaction,
SendEmailCode,
VerifyEmailAddress,
ResetEmailAddress,
@ -133,6 +162,9 @@ class AuthManager final : public NetActor {
int32 api_id_;
string api_hash_;
// State::WaitPremiumPurchase
string store_product_id_;
// State::WaitEmailAddress
bool allow_apple_id_ = false;
bool allow_google_id_ = false;
@ -204,6 +236,8 @@ class AuthManager final : public NetActor {
void on_sent_code(telegram_api::object_ptr<telegram_api::auth_SentCode> &&sent_code_ptr);
void on_send_code_result(NetQueryPtr &&net_query);
void on_check_premium_purchase_result(NetQueryPtr &&net_query);
void on_set_premium_purchase_transaction_result(NetQueryPtr &&net_query);
void on_send_email_code_result(NetQueryPtr &&net_query);
void on_verify_email_address_result(NetQueryPtr &&net_query);
void on_reset_email_address_result(NetQueryPtr &&net_query);

View File

@ -222,8 +222,9 @@ class BusinessConnectionManager final : public Actor {
void do_send_message_album(int64 request_id, BusinessConnectionId business_connection_id, DialogId dialog_id,
MessageInputReplyTo &&input_reply_to, bool disable_notification, bool protect_content,
MessageEffectId effect_id, vector<InputMessageContent> &&message_contents,
Promise<td_api::object_ptr<td_api::businessMessages>> &&promise);
MessageEffectId effect_id, vector<InputMessageContent> &&message_contents);
void fail_send_message_album(int64 request_id, Status error);
void on_upload_message_album_media(int64 request_id, size_t media_pos, Result<UploadMediaResult> &&result);

View File

@ -183,7 +183,7 @@ class CallActor final : public NetQueryCallback {
Status do_update_call(const telegram_api::phoneCallWaiting &call);
Status do_update_call(const telegram_api::phoneCallRequested &call);
Status do_update_call(const telegram_api::phoneCallAccepted &call);
Status do_update_call(const telegram_api::phoneCall &call);
Status do_update_call(telegram_api::phoneCall &call);
Status do_update_call(const telegram_api::phoneCallDiscarded &call);
void on_get_call_id();

View File

@ -45,15 +45,24 @@ class DialogDate {
int64 get_order() const {
return order;
}
DialogId get_dialog_id() const {
return dialog_id;
}
int32 get_date() const {
return static_cast<int32>((order >> 32) & 0x7FFFFFFF);
}
MessageId get_message_id() const {
return MessageId(ServerMessageId(static_cast<int32>(order & 0x7FFFFFFF)));
}
static int64 get_dialog_order(MessageId message_id, int32 message_date) {
CHECK(!message_id.is_scheduled());
return (static_cast<int64>(message_date) << 32) +
message_id.get_prev_server_message_id().get_server_message_id().get();
}
};
const DialogDate MIN_DIALOG_DATE(std::numeric_limits<int64>::max(), DialogId());

View File

@ -117,7 +117,7 @@ class DialogInviteLinkManager final : public Actor {
vector<UserId> participant_user_ids;
string description;
StarSubscriptionPricing subscription_pricing;
int64 subscription_form_id;
int64 subscription_form_id = 0;
CustomEmojiId bot_verification_icon;
bool creates_join_request = false;
bool can_refulfill_subscription = false;

View File

@ -32,6 +32,8 @@ class ForumTopic {
DialogNotificationSettings notification_settings_;
unique_ptr<DraftMessage> draft_message_;
int64 get_forum_topic_order(Td *td, DialogId dialog_id) const;
public:
ForumTopic() = default;
@ -65,6 +67,9 @@ class ForumTopic {
td_api::object_ptr<td_api::forumTopic> get_forum_topic_object(Td *td, DialogId dialog_id,
const ForumTopicInfo &info) const;
td_api::object_ptr<td_api::updateForumTopic> get_update_forum_topic_object(Td *td, DialogId dialog_id,
MessageId top_thread_message_id) const;
template <class StorerT>
void store(StorerT &storer) const;

View File

@ -78,7 +78,7 @@ class ForumTopicInfo {
bool apply_edited_data(const ForumTopicEditedData &edited_data);
td_api::object_ptr<td_api::forumTopicInfo> get_forum_topic_info_object(Td *td) const;
td_api::object_ptr<td_api::forumTopicInfo> get_forum_topic_info_object(Td *td, DialogId dialog_id) const;
template <class StorerT>
void store(StorerT &storer) const;

View File

@ -177,11 +177,18 @@ class ForumTopicManager final : public Actor {
void on_delete_forum_topic(DialogId dialog_id, MessageId top_thread_message_id, Promise<Unit> &&promise);
td_api::object_ptr<td_api::updateForumTopicInfo> get_update_forum_topic_info(DialogId dialog_id,
const ForumTopicInfo *topic_info) const;
td_api::object_ptr<td_api::updateForumTopicInfo> get_update_forum_topic_info_object(
DialogId dialog_id, const ForumTopicInfo *topic_info) const;
void send_update_forum_topic_info(DialogId dialog_id, const ForumTopicInfo *topic_info) const;
td_api::object_ptr<td_api::updateForumTopic> get_update_forum_topic_object(DialogId dialog_id,
const Topic *topic) const;
void send_update_forum_topic(DialogId dialog_id, const Topic *topic) const;
void on_forum_topic_changed(DialogId dialog_id, Topic *topic);
void save_topic_to_database(DialogId dialog_id, const Topic *topic);
void delete_topic_from_database(DialogId dialog_id, MessageId top_thread_message_id, Promise<Unit> &&promise);

View File

@ -87,8 +87,8 @@ struct InputMessageContent {
struct InlineMessageContent {
unique_ptr<MessageContent> message_content;
unique_ptr<ReplyMarkup> message_reply_markup;
bool disable_web_page_preview;
bool invert_media;
bool disable_web_page_preview = false;
bool invert_media = false;
};
void store_message_content(const MessageContent *content, LogEventStorerCalcLength &storer);

View File

@ -533,6 +533,8 @@ class MessagesManager final : public Actor {
vector<DialogId> sort_dialogs_by_order(const vector<DialogId> &dialog_ids, int32 limit) const;
int64 get_message_order(DialogId dialog_id, MessageId message_id) const;
void block_message_sender_from_replies(MessageId message_id, bool need_delete_message, bool need_delete_all_messages,
bool report_spam, Promise<Unit> &&promise);
@ -562,10 +564,10 @@ class MessagesManager final : public Actor {
void get_messages(DialogId dialog_id, const vector<MessageId> &message_ids, Promise<Unit> &&promise);
void get_message_from_server(MessageFullId message_full_id, Promise<Unit> &&promise, const char *source,
tl_object_ptr<telegram_api::InputMessage> input_message = nullptr);
telegram_api::object_ptr<telegram_api::InputMessage> input_message = nullptr);
void get_messages_from_server(vector<MessageFullId> &&message_ids, Promise<Unit> &&promise, const char *source,
tl_object_ptr<telegram_api::InputMessage> input_message = nullptr);
telegram_api::object_ptr<telegram_api::InputMessage> input_message = nullptr);
void get_message_properties(DialogId dialog_id, MessageId message_id,
Promise<td_api::object_ptr<td_api::messageProperties>> &&promise);
@ -1816,9 +1818,9 @@ class MessagesManager final : public Actor {
bool drop_author = false;
bool drop_media_captions = false;
Dialog *from_dialog;
Dialog *from_dialog = nullptr;
MessageId top_thread_message_id;
Dialog *to_dialog;
Dialog *to_dialog = nullptr;
MessageSendOptions message_send_options;
};
@ -2841,8 +2843,6 @@ class MessagesManager final : public Actor {
static NotificationInfo *add_dialog_notification_info(Dialog *d);
static int64 get_dialog_order(MessageId message_id, int32 message_date);
bool is_dialog_sponsored(const Dialog *d) const;
int64 get_dialog_base_order(const Dialog *d) const;
@ -3255,9 +3255,9 @@ class MessagesManager final : public Actor {
struct GetDialogsTask {
DialogListId dialog_list_id;
int64 dialog_list_unique_id;
int32 limit;
int32 retry_count;
int64 dialog_list_unique_id = 0;
int32 limit = 0;
int32 retry_count = 5;
DialogDate last_dialog_date = MIN_DIALOG_DATE;
Promise<td_api::object_ptr<td_api::chats>> promise;
};

View File

@ -38,7 +38,7 @@ class PhoneNumberManager final : public Actor {
void check_code(string code, Promise<Unit> &&promise);
private:
enum class Type : int32 { ChangePhone, VerifyPhone, ConfirmPhone };
enum class Type : int32 { None, ChangePhone, VerifyPhone, ConfirmPhone };
enum class State : int32 { Ok, WaitCode } state_ = State::Ok;
void tear_down() final;
@ -56,7 +56,7 @@ class PhoneNumberManager final : public Actor {
Td *td_;
ActorShared<> parent_;
Type type_;
Type type_ = Type::None;
SendCodeHelper send_code_helper_;
int64 generation_ = 0;
};

View File

@ -67,11 +67,7 @@ void get_premium_giveaway_info(Td *td, MessageFullId message_full_id,
void can_purchase_premium(Td *td, td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose, Promise<Unit> &&promise);
void assign_app_store_transaction(Td *td, const string &receipt,
td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose, Promise<Unit> &&promise);
void assign_play_market_transaction(Td *td, const string &package_name, const string &store_product_id,
const string &purchase_token,
td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose, Promise<Unit> &&promise);
void assign_store_transaction(Td *td, td_api::object_ptr<td_api::StoreTransaction> &&transaction,
td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose, Promise<Unit> &&promise);
} // namespace td

View File

@ -105,6 +105,10 @@ class Requests {
void on_request(uint64 id, td_api::setAuthenticationPhoneNumber &request);
void on_request(uint64 id, td_api::checkAuthenticationPremiumPurchase &request);
void on_request(uint64 id, td_api::setAuthenticationPremiumPurchaseTransaction &request);
void on_request(uint64 id, td_api::sendAuthenticationFirebaseSms &request);
void on_request(uint64 id, td_api::reportAuthenticationCodeMissing &request);
@ -1661,9 +1665,7 @@ class Requests {
void on_request(uint64 id, td_api::canPurchaseFromStore &request);
void on_request(uint64 id, td_api::assignAppStoreTransaction &request);
void on_request(uint64 id, td_api::assignGooglePlayTransaction &request);
void on_request(uint64 id, td_api::assignStoreTransaction &request);
void on_request(uint64 id, const td_api::getBusinessFeatures &request);

View File

@ -642,6 +642,8 @@ class UpdatesManager final : public Actor {
void on_update(tl_object_ptr<telegram_api::updateLoginToken> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateSentPhoneCode> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateBotStopped> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateChatParticipant> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateChannelParticipant> update, Promise<Unit> &&promise);
@ -704,8 +706,6 @@ class UpdatesManager final : public Actor {
// unsupported updates
void on_update(tl_object_ptr<telegram_api::updateNewStoryReaction> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateSentPhoneCode> update, Promise<Unit> &&promise);
};
} // namespace td

View File

@ -239,14 +239,14 @@ class UserManager final : public Actor {
struct BotData {
string username;
bool can_be_edited;
bool can_join_groups;
bool can_read_all_group_messages;
bool has_main_app;
bool is_inline;
bool is_business;
bool need_location;
bool can_be_added_to_attach_menu;
bool can_be_edited = false;
bool can_join_groups = false;
bool can_read_all_group_messages = false;
bool has_main_app = false;
bool is_inline = false;
bool is_business = false;
bool need_location = false;
bool can_be_added_to_attach_menu = false;
};
Result<BotData> get_bot_data(UserId user_id) const TD_WARN_UNUSED_RESULT;

View File

@ -180,7 +180,7 @@ class ConnectionCreator final : public NetQueryCallback {
struct TestProxyRequest {
Proxy proxy_;
int16 dc_id_;
int16 dc_id_ = -1;
ActorOwn<> child_;
Promise<Unit> promise_;

View File

@ -38,7 +38,7 @@ class DcAuthManager final : public NetQueryCallback {
struct DcInfo {
DcId dc_id;
std::shared_ptr<AuthDataShared> shared_auth_data;
AuthKeyState auth_key_state;
AuthKeyState auth_key_state = AuthKeyState::Empty;
enum class State : int32 { Waiting, Export, Import, BeforeOk, Ok };
State state = State::Waiting;

View File

@ -162,7 +162,7 @@ class Session final
double created_at_ = 0;
};
ConnectionInfo *current_info_;
ConnectionInfo *current_info_ = nullptr;
ConnectionInfo main_connection_;
ConnectionInfo long_poll_connection_;
mtproto::ConnectionManager::ConnectionToken connection_token_;
@ -195,7 +195,7 @@ class Session final
enum HandshakeId : int32 { MainAuthKeyHandshake = 0, TmpAuthKeyHandshake = 1 };
std::array<HandshakeInfo, 2> handshake_info_;
double wakeup_at_;
double wakeup_at_ = 0.0;
// mtproto::AuthData should be the last field, because it's size is about 32 KB
mtproto::AuthData auth_data_;

View File

@ -320,6 +320,9 @@ class ClientBlockchain {
td::UInt256 get_last_block_hash() const {
return blockchain_.last_block_hash_;
}
td::UInt256 get_previous_block_hash() const {
return blockchain_.last_block_.prev_block_hash_;
}
td::Status add_proof(td::Slice proof);

View File

@ -55,7 +55,7 @@ struct CallVerificationChain {
friend td::StringBuilder &operator<<(td::StringBuilder &sb, const CallVerificationChain &chain);
private:
td::Status process_broadcast(std::string message, e2e::object_ptr<e2e::e2e_chain_GroupBroadcast> broadcast);
td::Status process_broadcast(td::Slice message, e2e::object_ptr<e2e::e2e_chain_GroupBroadcast> broadcast);
td::Status process_broadcast(e2e::e2e_chain_groupBroadcastNonceCommit &nonce_commit);
td::Status process_broadcast(e2e::e2e_chain_groupBroadcastNonceReveal &nonce_reveal);
@ -88,11 +88,11 @@ struct CallVerificationChain {
class CallEncryption {
public:
CallEncryption(td::int64 user_id, PrivateKey private_key);
td::Status add_shared_key(td::int32 epoch, td::SecureString key, GroupStateRef group_state);
void forget_shared_key(td::int32 epoch);
td::Status add_shared_key(td::int32 epoch, td::UInt256 epoch_hash, td::SecureString key, GroupStateRef group_state);
void forget_shared_key(td::int32 epoch, td::UInt256 epoch_hash);
td::Result<std::string> decrypt(td::int64 expected_user_id, td::int32 expected_channel_id, td::Slice encrypted_data);
td::Result<std::string> encrypt(td::int32 channel_id, td::Slice decrypted_data);
td::Result<std::string> decrypt(td::int64 expected_user_id, td::int32 expected_channel_id, td::Slice packet);
td::Result<std::string> encrypt(td::int32 channel_id, td::Slice data, size_t unencrypted_header_length);
private:
static constexpr double FORGET_EPOCH_DELAY = 10;
@ -101,11 +101,17 @@ class CallEncryption {
PrivateKey private_key_;
struct EpochInfo {
EpochInfo(td::int32 epoch, td::int64 user_id, td::SecureString secret, GroupStateRef group_state)
: epoch_(epoch), user_id_(user_id), secret_(std::move(secret)), group_state_(std::move(group_state)) {
EpochInfo(td::int32 epoch, td::UInt256 epoch_hash, td::int64 user_id, td::SecureString secret,
GroupStateRef group_state)
: epoch_(epoch)
, epoch_hash_(epoch_hash)
, user_id_(user_id)
, secret_(std::move(secret))
, group_state_(std::move(group_state)) {
}
td::int32 epoch_{};
td::UInt256 epoch_hash_{};
td::int64 user_id_{};
td::SecureString secret_;
GroupStateRef group_state_;
@ -113,6 +119,7 @@ class CallEncryption {
std::map<td::int32, td::uint32> seqno_;
std::map<td::int32, EpochInfo> epochs_;
std::map<td::UInt256, td::int32> epoch_by_hash_;
td::VectorQueue<std::pair<td::Timestamp, td::int32>> epochs_to_forget_;
std::map<std::pair<PublicKey, td::int32>, std::set<td::uint32>> seen_;
@ -121,8 +128,9 @@ class CallEncryption {
td::Result<std::string> encrypt_packet_with_secret(td::int32 channel_id, td::Slice header, td::Slice packet,
td::Slice one_time_secret);
td::Result<std::string> decrypt_packet_with_secret(td::int64 expected_user_id, td::int32 expected_channel_id,
td::Slice unencrypted_packet, td::Slice encrypted_packet,
td::Slice one_time_secret, const GroupStateRef &group_state);
td::Slice unencrypted_header, td::Slice unencrypted_prefix,
td::Slice encrypted_packet, td::Slice one_time_secret,
const GroupStateRef &group_state);
td::Status check_not_seen(const PublicKey &public_key, td::int32 channel_id, td::uint32 seqno);
void mark_as_seen(const PublicKey &public_key, td::int32 channel_id, td::uint32 seqno);
static td::Status validate_channel_id(td::int32 channel_id);
@ -182,9 +190,9 @@ struct Call {
TRY_STATUS(get_status());
return call_encryption_.decrypt(user_id, channel_id, encrypted_data);
}
td::Result<std::string> encrypt(td::int32 channel_id, td::Slice decrypted_data) {
td::Result<std::string> encrypt(td::int32 channel_id, td::Slice decrypted_data, size_t unencrypted_prefix_size) {
TRY_STATUS(get_status());
return call_encryption_.encrypt(channel_id, decrypted_data);
return call_encryption_.encrypt(channel_id, decrypted_data, unencrypted_prefix_size);
}
td::Result<std::vector<std::string>> pull_outbound_messages() {

View File

@ -165,10 +165,10 @@ class Container {
auto id = next_id.fetch_add(1, std::memory_order_relaxed);
if constexpr (TI::is_mutable) {
auto value_ptr = std::make_shared<MutableValue<T>>(std::move(value));
storage.values.emplace(id, std::move(o_hash), value_ptr);
storage.values.emplace(id, o_hash, value_ptr);
} else {
auto value_ptr = std::make_shared<T>(std::move(value));
storage.values.emplace(id, std::move(o_hash), value_ptr);
storage.values.emplace(id, o_hash, value_ptr);
}
if constexpr (TI::has_hash) {
@ -243,6 +243,7 @@ td::Result<SharedRef<T>> convert(SharedRef<S> from) {
}
return td::Status::Error(static_cast<int>(tde2e_api::ErrorCode::UnknownError), "TODO");
}
template <class T, class S>
td::Result<UniqueRef<T>> convert(UniqueRef<S> from) {
if (std::holds_alternative<T>(*from)) {

View File

@ -365,7 +365,7 @@ struct EncryptedStorage {
// current blockchain height
td::int64 get_height() const;
// one must only apply blocks from server (TODO: signature from server)
// one should only apply blocks from server (TODO: signature from server?)
td::Result<KeyValueUpdates> apply_block(td::Slice block);
// proof must be from block of current height

View File

@ -10,14 +10,19 @@
#include "td/utils/SharedSlice.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/UInt.h"
namespace tde2e_core {
class MessageEncryption {
public:
static td::SecureString encrypt_data(td::Slice data, td::Slice secret);
static td::Result<td::SecureString> decrypt_data(td::Slice encrypted_data, td::Slice secret);
static td::SecureString encrypt_data(td::Slice data, td::Slice secret, td::Slice additional_data = {},
td::UInt256 *save_large_msg_id = nullptr);
static td::Result<td::SecureString> decrypt_data(td::Slice encrypted_data, td::Slice secret,
td::Slice additional_data = {},
td::UInt256 *save_large_msg_id = nullptr);
static td::SecureString hmac_sha512(td::Slice key, td::Slice message);
static td::SecureString hmac_sha256(td::Slice key, td::Slice message);
static td::SecureString kdf(td::Slice secret, td::Slice password, int iterations);
static td::Result<td::SecureString> encrypt_header(td::Slice decrypted_header, td::Slice encrypted_message,
td::Slice secret);
@ -29,7 +34,8 @@ class MessageEncryption {
static td::SecureString gen_random_prefix(td::int64 data_size, td::int64 min_padding);
static td::SecureString gen_deterministic_prefix(td::int64 data_size, td::int64 min_padding);
static td::SecureString encrypt_data_with_prefix(td::Slice data, td::Slice secret);
static td::SecureString encrypt_data_with_prefix(td::Slice data, td::Slice secret, td::Slice additional_data = {},
td::UInt256 *save_large_msg_id = nullptr);
static td::SecureString kdf_expand(td::Slice random_secret, td::Slice info);

View File

@ -22,13 +22,16 @@
#include <string>
#include <vector>
// Define a custom verbosity name for blockchain-specific logging
extern int VERBOSITY_NAME(blkch);
namespace tde2e_core {
struct Height {
td::int64 height;
td::int64 broadcast_height;
};
// Simple blockchain operation logger that writes to a file
class BlockchainLogger {
public:
@ -57,8 +60,6 @@ class BlockchainLogger {
std::string base64_encode(td::Slice data);
};
// Define a custom verbosity name for blockchain-specific logging
class ServerBlockchain {
public:
ServerBlockchain() = default;
@ -128,6 +129,7 @@ struct BlockBuilder {
bool in_proof = true, td::int32 external_permissions = 0);
BlockBuilder &skip_group_state_proof();
BlockBuilder &with_shared_key(const std::vector<td::int64> &user_ids, bool in_changes = true, bool in_proof = true);
BlockBuilder &with_shared_key(GroupSharedKeyRef shared_key, bool in_changes, bool in_proof);
BlockBuilder &skip_shared_key_proof();
private:

View File

@ -80,16 +80,21 @@ struct TrieNode {
};
td::Result<TrieRef> set(const TrieRef &n, BitString key, td::Slice value, td::Slice snapshot = {});
td::Result<std::string> get(const TrieRef &n, const BitString &key, td::Slice snapshot = {});
td::Result<TrieRef> generate_pruned_tree(const TrieRef &n, td::Span<td::Slice> keys, td::Slice snapshot = {});
std::ostream &operator<<(std::ostream &os, const td::UInt256 &hash);
void print_tree(const TrieRef &node, const std::string &prefix = "", bool is_root = true);
BitString to_key(td::Slice key);
inline td::Result<TrieRef> set(const TrieRef &n, td::Slice key, td::Slice value) {
return set(n, to_key(key), value);
}
inline td::Result<std::string> get(const TrieRef &n, td::Slice key, td::Slice snapshot = {}) {
return get(n, to_key(key), snapshot);
}

View File

@ -175,7 +175,6 @@ Result<Ok> login_destroy(LoginId login_id);
Result<Ok> login_destroy_all();
// Personal info
// TODO: UserId
// 1. Each entry stored and signed separately
// 2. Signature is never stored, but always is verified
@ -304,7 +303,8 @@ Result<std::string> call_describe_block(Slice block);
Result<std::string> call_describe_message(Slice message);
Result<Bytes> call_create_change_state_block(CallId call_id, const CallState &new_state);
Result<Bytes> call_encrypt(CallId call_id, CallChannelId channel_id, SecureSlice message);
Result<Bytes> call_encrypt(CallId call_id, CallChannelId channel_id, SecureSlice message,
size_t unencrypted_prefix_size);
Result<SecureBytes> call_decrypt(CallId call_id, UserId user_id, CallChannelId channel_id, Slice message);
Result<int> call_get_height(CallId call_id);
@ -329,7 +329,9 @@ struct CallVerificationWords {
int height{};
std::vector<std::string> words;
};
Result<CallVerificationWords> call_get_verification_words(CallId call_id);
Result<Ok> call_destroy(CallId call_id);
Result<Ok> call_destroy_all();
} // namespace tde2e_api

View File

@ -25,6 +25,7 @@ enum class ErrorCode : int {
InvalidBlock_InvalidStateProof_Secret = 206,
InvalidBlock_NoPermissions = 207,
InvalidBlock_InvalidGroupState = 208,
InvalidBlock_InvalidSharedSecret = 209,
InvalidCallGroupState_NotParticipant = 300,
InvalidCallGroupState_WrongUserId = 301,
Decrypt_UnknownEpoch = 400,
@ -37,7 +38,8 @@ enum class ErrorCode : int {
InvalidBroadcast_InvalidReveal = 505,
InvalidBroadcast_InvalidBlockHash = 506,
InvalidCallChannelId = 600,
CallFailed = 601
CallFailed = 601,
CallKeyAlreadyUsed = 602
};
inline std::string_view error_string(ErrorCode error_code) {
switch (error_code) {
@ -67,6 +69,8 @@ inline std::string_view error_string(ErrorCode error_code) {
return "INVALID_BLOCK__INVALID_STATE_PROOF__SECRET";
case ErrorCode::InvalidBlock_InvalidGroupState:
return "INVALID_BLOCK__INVALID_GROUP_STATE";
case ErrorCode::InvalidBlock_InvalidSharedSecret:
return "INVALID_BLOCK__INVALID_SHARED_SECRET";
case ErrorCode::InvalidBlock_NoPermissions:
return "INVALID_BLOCK__NO_PERMISSIONS";
case ErrorCode::InvalidCallGroupState_NotParticipant:
@ -93,6 +97,8 @@ inline std::string_view error_string(ErrorCode error_code) {
return "INVALID_BROADCAST__INVALID_BLOCK_HASH";
case ErrorCode::CallFailed:
return "CALL_FAILED";
case ErrorCode::CallKeyAlreadyUsed:
return "CALL_KEY_ALREADY_USED";
case ErrorCode::InvalidCallChannelId:
return "INVALID_CALL_CHANNEL_ID";
}

View File

@ -23,7 +23,9 @@
#include <string>
#include <utility>
namespace tde2e_api {
inline Error to_error(const td::Status &status) {
auto error_code = ErrorCode(status.code());
if (error_string(error_code) == "UNKNOWN_ERROR") {
@ -39,6 +41,7 @@ Result<T> to_result(td::Result<T> &value) {
}
return Result<T>(to_error(value.error()));
}
template <typename T>
Result<T>::Result(td::Result<T> &&value) : Result(to_result(value)) {
}
@ -50,11 +53,14 @@ Result<T>::Result(td::Status &&status) : Result(to_error(status)) {
} // namespace tde2e_api
namespace tde2e_core {
using E = tde2e_api::ErrorCode;
inline td::Status Error(E error_code) {
auto msg = tde2e_api::error_string(error_code);
return td::Status::Error(static_cast<int>(error_code), td::Slice(msg.data(), msg.size()));
}
inline td::Status Error(E error_code, td::Slice message) {
auto msg = tde2e_api::error_string(error_code);
return td::Status::Error(static_cast<int>(error_code), PSLICE()

View File

@ -15,6 +15,7 @@ struct TestVector {
std::string name;
std::string secret;
std::string data;
std::string extra;
std::string header;
std::string encrypted;
std::string encrypted_header;
@ -22,59 +23,65 @@ struct TestVector {
inline std::vector<TestVector> get_test_vectors() {
return {
{"empty_message", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", "",
"a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f",
"9e2476ad849d22a44d9135c5c3c5e8b52d4f88473ae8745f3a9cec4d54780caf",
"de3539a5e10b20a3a0cffc24dbbd76b3a7e0eeab402cb38396d64785a3ab7c25"},
{"simple_message", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a",
"48656c6c6f2c20576f726c6421", "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f",
"872b141f6d1e3554ead471dffd0fee5c04a0b04260eafcca9187158ce84c4487e9429df876706753913de61029402478",
"013037e02dc8dbf13598d96eb333a69a930efe043bac7dce0d6edfd1abc6bd2f"},
{"long_message", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a",
{"empty_message", "f9fb473b9887e50ea38eef7380c82361432cd4b22c5f9b3700809990d8ed344c", "", "",
"bd29703cf44551710ca14d091a6c98ee347931b2b8140faaaef2dbb40719df12",
"d28eb3e3d1328f06dafedabd67a353d5ea6e164d2f34c162a16f8a1164663a03",
"4060edd7bcacca6dd0f4fe81d6ec63a8859fa9d520598043bc4748919f3fdeda"},
{"simple_message", "f9fb473b9887e50ea38eef7380c82361432cd4b22c5f9b3700809990d8ed344c",
"48656c6c6f2c20576f726c6421", "", "bd29703cf44551710ca14d091a6c98ee347931b2b8140faaaef2dbb40719df12",
"967f5245b03e07ab7be6044174306a4af811e96708ae3ad2ab427aa5495508b1c319ca0353531c0a2921e307f2455856",
"9e7910949e526b6ad51a59aad8022c826b00f379e28592ed3216aabc6be252e0"},
{"long_message", "f9fb473b9887e50ea38eef7380c82361432cd4b22c5f9b3700809990d8ed344c",
"787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787"
"878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878"
"787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787"
"8787878787878787878787878787878787878787878787878787878787878787878",
"a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f",
"0fd497802c755b9c6fb9d58d38b9fb5fbd7dc9ccf78af3eb003a22d46bfa38894135d976bbec0f3cadc2b61a4d8648120dada26b2c3153d"
"ef20fb9e370def31c802e202846946b5cb1bc2c01a7b46605292d6043ffb8f4040aaf18914e1c93fe9e683f088e23ee5e1551f00068a23f"
"a3ebb8d6b9dcbf7a9072b12323b1a64247ba9bc7d277b08cfcc37387a0d24afca170dc027d8f0212eb62bccd9555de98936047c9bfc6a03"
"aa539073f458795bd94b9b43003fe2299805f90c1d30ca631c8054242687e41e890bf4d744b529d7e96ea48a5bcec700993b5e980173049"
"cf9df6f93d62ccc06d933fd6d7063890fa2d",
"697558ba60e789622ee90dffc55f1f1148cfea568c573b257fcf2083cf8aad0a"},
{"random_message", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a",
"e61d2b05302c49faa9fac6a893957a846a2b30801dc171cf62ffec92297b10bfc4a82445839ecb4c5800ffb37c5356d4b95fc565ddb2e7f"
"3e21f2936a952373c",
"a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f",
"515f0223f27302ca5e952ec978ec66bcdf04e7f72ae3a8e011e21457b355891d12e158c9b2dfb921520a0e5f531e6a20e95d42635b084a0"
"b38a6e658f4a4181f85ea83756d316cea538cc34592491eef3e3530c34c63a693e3372cbdd0076628",
"b5bdfe6a3400c5d299d94756af4c18bbb8cca4a2635beeba3d89bbaa0025d9d3"},
{"very_long_message", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a",
"de6e0dbde23e04b6be3d4c46868a1171c6b879227b19e370765823390501b195783c356af2156e828e511473d5698c9bd1cb4a70b7e209b"
"bb02f8dda044af02ab6869478b211e3a17d72fbe6289c1b2e6132cb141e89cf72cf70defd67a23fbfe8e718a09a9c6a345565ebdc73e1f5"
"9927744801eb4e6f0b30d2705ea181e02a4030252330ac73bc4a4d51d2ccafb2f3f62abe3e81163be325ac823571c8dfae739f70bff3916"
"4e3cec7f53b88f97735dc25ac0c0630b1b41a131a979ba5164ab92e103716e9096e2fb5a6434b31d2c3673fca7e54dcdcce3807bfd43ee7"
"bc3422868094305a9847ce7666bf57e49fb3c2009cb30eb3ba955942b923ec2c2a4e0341d86b524b198974bdce9cf863ab3526e9e03e533"
"99dad20fc218554567c440536a31e05573f4cb930ba6a",
"a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f",
"e296a98cd96154b27d4b94cc520a8f028c61bd5ea69d22f0c3c13658c4bd5db0bd6661d0d17fac791c5cc06b3a54a853916bd7bf8a00644"
"acc53cca43b6f51c66bc6a6cb98cf8d9f23dcd639089d1d9d3dfbc8829a1a81638317bbd3edb070c1dae181d97605eb42a6111b8696a16c"
"c3e42639e38e93f872111fd67e934740f73a57df0e6edc6726c9aff99682bfcb7ddd99a3bef30da70d3c21e590fd02defc23be9f7f243e4"
"5a56b13562b8ecea09a14ac5af3a0500cb52f73f1bffea4a6624644da0bc4d112e5ee684b13a2ae8dbf401a5a8e581295a9dc876eaeb8ae"
"4d732fb78d50a92c302d15c0a2308e43fc6e147ec162b28a534d6c95a2020fa141f3ce7f7dd25ff000d35f732a145abf31b3ff4d0da015d"
"39da3b0fc70e692b567a9507be59e8c91a63fe809c495d76e70ec857cc24978771fc9314251e2bc4b1b24e0448514f97a6d438255cba8c1"
"854019",
"8dab56b50ce6a4315b6e0b4eb32c39dfe817cb462ee9e070e2a60bb843c16107"},
{"message_with_special_chars", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a",
"2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
"a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f",
"2014d1125081316a334896cbe5aed251d9fba6f3422afbc7e7bc9019e10e184241d18f71ef86603b5ce03e1c351e3dfff8adfb8f3498300"
"a712ec134b367c533",
"6f5a5de410db3606ff94fa14d0e084a6ba5a51241a2abf45ac593e4748c477ce"},
{"message_with_unicode", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a",
"48656c6c6f2c20e4b896e7958c21", "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f",
"7ae70cb905f23109477b4d758d3907238ff4c37e2f351f086268ba3e85cef0257af58a70d8838c7b9c044f30382c2ccf",
"7dfb2d2e76df39b2fafdf01de009088a7e4d045b8630941986111ef2010d7c4a"},
"", "bd29703cf44551710ca14d091a6c98ee347931b2b8140faaaef2dbb40719df12",
"8202a46a19de7111166f6c244127c84dbdc1c3a63ca6526dc699c6cbbc6f8236ee82a0172ed1115cb4a2ba8e27cfa8089822c7e9070ec2f"
"5c7cee77cc979447c1db9758119ad28a05b2edfc1c98b486985bb60fb6f1fefb4b5f7ecea19d59b8018f865a7be3771c7f6fe6092b34b78"
"a1bddefc8d07f2f61351a4247c41a58cb068ebe9110245de4fda076f0ff73aede4e9811678424f648b8054b921b53f0612dfbdb7173a86b"
"ce3eba73955afef435599ae34825d295e8d298d6d3a1fc07084740c0b1c3a24cebedbd26b631cbbd1a352c1a499ba3576a628a74ab14eb1"
"d180e5af7e9eac4020b889fafc4f7bfb2e24",
"641620351a1e4d76711385d5cf3b0eed07308c9cafc06ef09ed0c1f57ebb5f42"},
{"random_message", "f9fb473b9887e50ea38eef7380c82361432cd4b22c5f9b3700809990d8ed344c",
"fed306c137017ab008c22d1f74bc104e5138d3c19b42fb303c768b083912a1102d06ac1e0f4440e3b32b9144a50e6fc0f190273bd4dec7e"
"f847bf7d46680bb67",
"736d616c6c206578747261", "bd29703cf44551710ca14d091a6c98ee347931b2b8140faaaef2dbb40719df12",
"ee198e4860c888ab18bcecba5083eb539f402be1fbde51e0c49e398145d40ebb7b5a52bdc83d6200e63a70c47cc6a5e6fa7a71f6e24b722"
"b5f0314b6b52768dbd2e438a582d1cf2a54d4de7ba30e36e68afd8379b63a345483dbcc33380fd07f",
"f282da8a41f17a5fa7f793c6c134c5bb2b960a2fea43bdc15b58a69cb7dafb43"},
{"very_long_message", "f9fb473b9887e50ea38eef7380c82361432cd4b22c5f9b3700809990d8ed344c",
"d16554889d83850ffb42d119e0c69d8b68ee07ff021f0a2cb7beb70d0b1cc62e3d8fe2dff95e674893393b5da015a965108c785d8935a3e"
"e58e3df9505016020b558687ee535f9bcfa94450ded18ac3e8145879af43e66eeacfee1d9f9c9c78824cf34639af50fb0b93de73aa9362c"
"f2732e2d8c652111ec1246c8ded3b19e93d154d04cc8a4bd927332136d7627e71e6be2c97dd62235dfd998d1e630588d10beeab791e0919"
"9bfa8bab3b9e6dbcdfba9f9dd76110f7f6c7fd1fbccc421e7ad093e8fd385e53e3c03f7f0a79296962de1e752eea5f8c5e6325ef406aef5"
"62d8ef0b9431defeb46fb93ee3c3409af0e3a4f7e63af4efbfb5f4b61a104c1158247877a28f9538d6cf8c5e243ece977cc2a0a0bcf602c"
"d16df445cb71a4f6a0494a3b6a1149725c169dc40eb10",
"ebc6b1176ca69b8bb769bcc68add44fbba1c79d2771ef412eccad3ee4f7afe595f8fd2052f8d1d8b8fea209c568eb6a4c6aea6d88c583df"
"25ed3f38260c2f95c1f0244219d55e658498b34f7e7a527c60723b6806fe28275337b0c9b64c158825a3c14d8cec6a40bbf8c5a5a8009ca"
"75f2c6f2e7f3ab612ff5d675f2c3b801d4d4e0408b49d8543d8621de0df26a65a49d1fc7a21584d5495a24b2090479870e852766f6de34b"
"724e5941097d19153f4f4d035ae0c978ec6354ba452cf465581cd4afd7045bfa4c54383796587d19e981da220cd9ca5230161eaf64d8a1b"
"406a2f8afc7faeb0ec7634c3c14aa63736c955b56c48c61ba58b109775ac252f3837e8bcebdb40f4ca2ce32609619b0063cb421a268f80c"
"60ffc7c99963f74033d22283a6d2ab3095f65cb49a17e",
"bd29703cf44551710ca14d091a6c98ee347931b2b8140faaaef2dbb40719df12",
"c96e7fadcf4e51f5c0dc03aeed33352d7f984c2d49791d173caad17d724b98155ff6b3dc6e082b90063434e9f85941c085dd8573fb4f23d"
"d0867615249e8e8c567ba74d4e6739919c46afc0a6b19b26c0e37e1810952dcb859b8a2df9ed322da89c4e7821166939809d2561980ff77"
"d3b797f1ecb1ed78e39614e096c72bed4587ac3229929ae4e164da9b00323410f8b17abed5cc8455656ee73114119e20b529294f8c578f7"
"f9492327ff40f9f1255abe84f7445c87c8b048e98eac746f6d58fb3261f61eb039e5e88da46c9fc5e35baeb0c1180e9913f49ab7aac5f59"
"76be1e384071470d80ddbf77c52e781f954d77978697cf555d1586469ce21ccd45f43283eeec3b976d6bd897f436ef9ccacde5da73f298b"
"d1b99c10e988befeb2988f8f03f96215a746590d35ff0a6a85fa1102d63a00cd71e3cb80753bfe98bf6744f2aec697993dab51cce21f823"
"656870",
"97b6b5b2082a66182783c0be6940ca9d63e931195b6cc84dbf9158e9b39834ac"},
{"message_with_special_chars", "f9fb473b9887e50ea38eef7380c82361432cd4b22c5f9b3700809990d8ed344c",
"2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "",
"bd29703cf44551710ca14d091a6c98ee347931b2b8140faaaef2dbb40719df12",
"b862c41221e3242c07c375dfded48e302aaebed7fbc91bbfe3b7c88345a58d3a13d83cfb87f08250b2d66f4590b5dd2bb2b08fabd4328d1"
"04f7b4e1bad80931c",
"7557d10f233e47a56f74a57458b5169e8cce5c4c98e3a3da02f6e49a3db4c2b3"},
{"message_with_unicode", "f9fb473b9887e50ea38eef7380c82361432cd4b22c5f9b3700809990d8ed344c",
"48656c6c6f2c20e4b896e7958c21", "", "bd29703cf44551710ca14d091a6c98ee347931b2b8140faaaef2dbb40719df12",
"cb0d460ca3daf8e3fd5623965b39b5c1de840e92d39f6caf4662b7a7983c53b29fe644bf45acea2644507ac01f0617a2",
"ebe8636326b11d90f9a670e63086e2fcd02b78c0aa5cacdb4f887e511d1ae4c9"},
};
}

View File

@ -138,7 +138,7 @@ class ChainScheduler final : public ChainSchedulerBase {
struct Task {
enum class State { Pending, Active, Paused } state{State::Pending};
vector<TaskChainInfo> chains;
ExtraT extra;
ExtraT extra{};
};
FlatHashMap<ChainId, unique_ptr<ChainInfo>> chains_;
FlatHashMap<ChainId, TaskId> limited_tasks_;

View File

@ -209,12 +209,15 @@ class ConcurrentHashMap {
int migrate_generation_{0};
HashMap *migrate_from_hash_map_{nullptr};
HashMap *migrate_to_hash_map_{nullptr};
struct Task {
size_t begin;
size_t end;
bool empty() const {
return begin >= end;
}
size_t size() const {
if (empty()) {
return 0;
@ -224,9 +227,10 @@ class ConcurrentHashMap {
};
struct TaskCreator {
size_t chunk_size;
size_t size;
size_t chunk_size{0};
size_t size{0};
std::atomic<size_t> pos{0};
Task create() {
auto i = pos++;
auto begin = i * chunk_size;

View File

@ -12,6 +12,8 @@
#include "td/utils/Status.h"
#include "td/utils/UInt.h"
#include <memory>
#if TD_HAVE_OPENSSL
namespace td {
@ -45,7 +47,7 @@ class Ed25519 {
}
static Result<PublicKey> from_slice(Slice slice) {
if (slice.size() != 32) {
if (slice.size() != LENGTH) {
return Status::Error("Invalid slice size");
}
return PublicKey(SecureString(slice));
@ -65,18 +67,24 @@ class Ed25519 {
SecureString octet_string_;
};
struct PreparedPrivateKey;
class PrivateKey {
public:
static constexpr size_t LENGTH = 32;
explicit PrivateKey(SecureString octet_string);
Result<std::shared_ptr<const PreparedPrivateKey>> prepare() const;
SecureString as_octet_string() const;
Result<PublicKey> get_public_key() const;
Result<SecureString> sign(Slice data) const;
static Result<SecureString> sign(const PreparedPrivateKey &prepared_private_key, Slice data);
Result<SecureString> as_pem(Slice password) const;
static Result<PrivateKey> from_pem(Slice pem, Slice password);

View File

@ -68,7 +68,7 @@ struct MaskPortable {
static MaskIterator<1> equal_mask(uint8 *bytes, uint8 needle) {
uint64 res = 0;
for (int i = 0; i < 16; i++) {
res |= (bytes[i] == needle) << i;
res |= static_cast<int>(bytes[i] == needle) << i;
}
return {res & ((1u << 14) - 1)};
}

View File

@ -287,9 +287,9 @@ static NSString *hexStringFromData(NSData *data) {
return [[NSData alloc] initWithBytes:result.value().data() length:result.value().size()];
}
- (nullable NSData *)encrypt:(NSData *)message channelId:(int32_t)channelId {
- (nullable NSData *)encrypt:(NSData *)message channelId:(int32_t)channelId plaintextPrefixLength:(NSInteger)plaintextPrefixLength {
std::string mappedMessage((uint8_t *)message.bytes, ((uint8_t *)message.bytes) + message.length);
auto result = tde2e_api::call_encrypt(_callId, channelId, mappedMessage);
auto result = tde2e_api::call_encrypt(_callId, channelId, mappedMessage, (size_t)plaintextPrefixLength);
if (!result.is_ok()) {
return nil;
}