From 491e3779435356dba6ccf3a40c3767ab668449b8 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 9 Apr 2019 16:18:57 +0100 Subject: [PATCH] no message --- .../NotificationViewController.swift | 84 +- NotificationService/Api0.swift | 834 ++++++++++++++++++ NotificationService/ImageData.swift | 272 +----- NotificationService/NotificationService.swift | 246 +++--- NotificationService/ReadBuffer.swift | 310 +++++++ NotificationService/Serialization.swift | 2 +- Telegram-iOS.xcodeproj/project.pbxproj | 8 + Telegram-iOS/en.lproj/Localizable.strings | 3 + submodules/LegacyComponents | 2 +- submodules/MtProtoKit | 2 +- submodules/TelegramCore | 2 +- submodules/TelegramUI | 2 +- 12 files changed, 1363 insertions(+), 404 deletions(-) create mode 100644 NotificationService/Api0.swift create mode 100644 NotificationService/ReadBuffer.swift diff --git a/NotificationContent/NotificationViewController.swift b/NotificationContent/NotificationViewController.swift index 7914eda7c9..de65e854b7 100644 --- a/NotificationContent/NotificationViewController.swift +++ b/NotificationContent/NotificationViewController.swift @@ -49,6 +49,8 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi private let applyDisposable = MetaDisposable() private let fetchedDisposable = MetaDisposable() + private var accountsPath: String? + deinit { self.applyDisposable.dispose() self.fetchedDisposable.dispose() @@ -85,6 +87,8 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi setupSharedLogger(logsPath) + accountsPath = rootPath + if sharedAccountContext == nil { initializeAccountManagement() let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata") @@ -130,21 +134,15 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi } func didReceive(_ notification: UNNotification) { - if let accountIdValue = notification.request.content.userInfo["accountId"] as? Int64, let peerIdValue = notification.request.content.userInfo["peerId"] as? Int64, let messageIdNamespace = notification.request.content.userInfo["messageId.namespace"] as? Int32, let messageIdId = notification.request.content.userInfo["messageId.id"] as? Int32, let dict = notification.request.content.userInfo["mediaInfo"] as? [String: Any] { + guard let accountsPath = self.accountsPath else { + return + } + + if let accountIdValue = notification.request.content.userInfo["accountId"] as? Int64, let peerIdValue = notification.request.content.userInfo["peerId"] as? Int64, let messageIdNamespace = notification.request.content.userInfo["messageId.namespace"] as? Int32, let messageIdId = notification.request.content.userInfo["messageId.id"] as? Int32, let mediaDataString = notification.request.content.userInfo["media"] as? String, let mediaData = Data(base64Encoded: mediaDataString), let media = parseMediaData(data: mediaData) { let messageId = MessageId(peerId: PeerId(peerIdValue), namespace: messageIdNamespace, id: messageIdId) - if let imageInfo = dict["image"] as? [String: Any] { - guard let width = imageInfo["width"] as? Int, let height = imageInfo["height"] as? Int else { - return - } - guard let thumbnailInfo = imageInfo["thumbnail"] as? [String: Any] else { - return - } - guard let fullSizeInfo = imageInfo["fullSize"] as? [String: Any] else { - return - } - - let dimensions = CGSize(width: CGFloat(width), height: CGFloat(height)) + if let image = media as? TelegramMediaImage, let thumbnailRepresentation = imageRepresentationLargerThan(image.representations, size: CGSize(width: 120.0, height: 120.0)), let largestRepresentation = largestImageRepresentation(image.representations) { + let dimensions = largestRepresentation.dimensions let fittedSize = dimensions.fitted(CGSize(width: self.view.bounds.width, height: 1000.0)) self.view.frame = CGRect(origin: self.view.frame.origin, size: fittedSize) self.preferredContentSize = fittedSize @@ -152,13 +150,15 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi self.imageDimensions = dimensions self.updateImageLayout(boundingSize: self.view.bounds.size) - if let path = fullSizeInfo["path"] as? String, let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) { + let mediaBoxPath = accountsPath + "/" + accountRecordIdPathName(AccountRecordId(rawValue: accountIdValue)) + "/postbox/media" + + if let data = try? Data(contentsOf: URL(fileURLWithPath: mediaBoxPath + "/\(largestRepresentation.resource.id.uniqueId)"), options: .mappedRead) { self.imageNode.setSignal(chatMessagePhotoInternal(photoData: .single((nil, data, true))) |> map { $0.1 }) return } - if let path = thumbnailInfo["path"] as? String, let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) { + if let data = try? Data(contentsOf: URL(fileURLWithPath: mediaBoxPath + "/\(thumbnailRepresentation.resource.id.uniqueId)"), options: .mappedRead) { self.imageNode.setSignal(chatMessagePhotoInternal(photoData: .single((data, nil, false))) |> map { $0.1 }) } @@ -190,9 +190,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi } } } else { - if let thumbnailFileLocation = thumbnailInfo["fileLocation"] as? [AnyHashable: Any], let thumbnailResource = parseFileLocationResource(thumbnailFileLocation), let fileLocation = fullSizeInfo["fileLocation"] as? [AnyHashable: Any], let resource = parseFileLocationResource(fileLocation) { - imageReference = .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudImage, id: 1), representations: [TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(width), height: CGFloat(height)).fitted(CGSize(width: 320.0, height: 320.0)), resource: thumbnailResource), TelegramMediaImageRepresentation(dimensions: CGSize(width: CGFloat(width), height: CGFloat(height)), resource: resource)], immediateThumbnailData: nil, reference: nil, partialReference: nil)) - } + imageReference = .standalone(media: image) } return (account, imageReference) } @@ -208,6 +206,56 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi strongSelf.fetchedDisposable.set(standaloneChatMessagePhotoInteractiveFetched(account: accountAndImage.0, photoReference: imageReference).start()) } })) + } else if let file = media as? TelegramMediaFile, let dimensions = file.dimensions { + guard let sharedAccountContext = sharedAccountContext else { + return + } + + let fittedSize = dimensions.fitted(CGSize(width: min(300.0, self.view.bounds.width), height: 300.0)) + self.view.frame = CGRect(origin: self.view.frame.origin, size: fittedSize) + self.preferredContentSize = fittedSize + + self.applyDisposable.set((sharedAccountContext.activeAccounts + |> map { _, accounts, _ -> Account? in + return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1 + } + |> filter { account in + return account != nil + } + |> take(1) + |> mapToSignal { account -> Signal<(Account, FileMediaReference?), NoError> in + guard let account = account else { + return .complete() + } + return account.postbox.messageAtId(messageId) + |> take(1) + |> map { message in + var fileReference: FileMediaReference? + if let message = message { + for media in message.media { + if let file = media as? TelegramMediaFile { + fileReference = .message(message: MessageReference(message), media: file) + } + } + } else { + fileReference = .standalone(media: file) + } + return (account, fileReference) + } + } + |> deliverOnMainQueue).start(next: { [weak self] accountAndImage in + guard let strongSelf = self else { + return + } + if let fileReference = accountAndImage.1 { + if file.isSticker { + strongSelf.imageNode.setSignal(chatMessageSticker(account: accountAndImage.0, file: file, small: false)) + + accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true)) + strongSelf.fetchedDisposable.set(freeMediaFileInteractiveFetched(account: accountAndImage.0, fileReference: fileReference).start()) + } + } + })) } } } diff --git a/NotificationService/Api0.swift b/NotificationService/Api0.swift new file mode 100644 index 0000000000..0654582811 --- /dev/null +++ b/NotificationService/Api0.swift @@ -0,0 +1,834 @@ + +fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { + var dict: [Int32 : (BufferReader) -> Any?] = [:] + dict[-1471112230] = { return $0.readInt32() } + dict[570911930] = { return $0.readInt64() } + dict[571523412] = { return $0.readDouble() } + dict[-1255641564] = { return parseString($0) } + dict[590459437] = { return Api.Photo.parse_photoEmpty($0) } + dict[-797637467] = { return Api.Photo.parse_photo($0) } + dict[236446268] = { return Api.PhotoSize.parse_photoSizeEmpty($0) } + dict[2009052699] = { return Api.PhotoSize.parse_photoSize($0) } + dict[-374917894] = { return Api.PhotoSize.parse_photoCachedSize($0) } + dict[-525288402] = { return Api.PhotoSize.parse_photoStrippedSize($0) } + dict[-1132476723] = { return Api.FileLocation.parse_fileLocationToBeDeprecated($0) } + dict[1815593308] = { return Api.DocumentAttribute.parse_documentAttributeImageSize($0) } + dict[297109817] = { return Api.DocumentAttribute.parse_documentAttributeAnimated($0) } + dict[1662637586] = { return Api.DocumentAttribute.parse_documentAttributeSticker($0) } + dict[250621158] = { return Api.DocumentAttribute.parse_documentAttributeVideo($0) } + dict[-1739392570] = { return Api.DocumentAttribute.parse_documentAttributeAudio($0) } + dict[358154344] = { return Api.DocumentAttribute.parse_documentAttributeFilename($0) } + dict[-1744710921] = { return Api.DocumentAttribute.parse_documentAttributeHasStickers($0) } + dict[-4838507] = { return Api.InputStickerSet.parse_inputStickerSetEmpty($0) } + dict[-1645763991] = { return Api.InputStickerSet.parse_inputStickerSetID($0) } + dict[-2044933984] = { return Api.InputStickerSet.parse_inputStickerSetShortName($0) } + dict[1075322878] = { return Api.InputFileLocation.parse_inputPhotoFileLocation($0) } + dict[-1160743548] = { return Api.InputFileLocation.parse_inputDocumentFileLocation($0) } + dict[-1361650766] = { return Api.MaskCoords.parse_maskCoords($0) } + dict[-1683841855] = { return Api.Document.parse_document($0) } + return dict +}() + +struct Api { + static func parse(_ buffer: Buffer) -> Any? { + let reader = BufferReader(buffer) + if let signature = reader.readInt32() { + return parse(reader, signature: signature) + } + return nil + } + + static func parse(_ reader: BufferReader, signature: Int32) -> Any? { + if let parser = parsers[signature] { + return parser(reader) + } + else { + //Logger.shared.log("TL", "Type constructor \(String(signature, radix: 16, uppercase: false)) not found") + return nil + } + } + + static func parseVector(_ reader: BufferReader, elementSignature: Int32, elementType: T.Type) -> [T]? { + if let count = reader.readInt32() { + var array = [T]() + var i: Int32 = 0 + while i < count { + var signature = elementSignature + if elementSignature == 0 { + if let unboxedSignature = reader.readInt32() { + signature = unboxedSignature + } + else { + return nil + } + } + if elementType == Buffer.self { + if let item = parseBytes(reader) as? T { + array.append(item) + } else { + return nil + } + } else { + if let item = Api.parse(reader, signature: signature) as? T { + array.append(item) + } + else { + return nil + } + } + i += 1 + } + return array + } + return nil + } + + static func serializeObject(_ object: Any, buffer: Buffer, boxed: Swift.Bool) { + switch object { + case let _1 as Api.Photo: + _1.serialize(buffer, boxed) + case let _1 as Api.PhotoSize: + _1.serialize(buffer, boxed) + case let _1 as Api.FileLocation: + _1.serialize(buffer, boxed) + case let _1 as Api.DocumentAttribute: + _1.serialize(buffer, boxed) + case let _1 as Api.InputStickerSet: + _1.serialize(buffer, boxed) + case let _1 as Api.InputFileLocation: + _1.serialize(buffer, boxed) + case let _1 as Api.MaskCoords: + _1.serialize(buffer, boxed) + case let _1 as Api.Document: + _1.serialize(buffer, boxed) + default: + break + } + } + +} +extension Api { + enum Photo: TypeConstructorDescription { + case photoEmpty(id: Int64) + case photo(flags: Int32, id: Int64, accessHash: Int64, fileReference: Buffer, date: Int32, sizes: [Api.PhotoSize], dcId: Int32) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .photoEmpty(let id): + if boxed { + buffer.appendInt32(590459437) + } + serializeInt64(id, buffer: buffer, boxed: false) + break + case .photo(let flags, let id, let accessHash, let fileReference, let date, let sizes, let dcId): + if boxed { + buffer.appendInt32(-797637467) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeBytes(fileReference, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(sizes.count)) + for item in sizes { + item.serialize(buffer, true) + } + serializeInt32(dcId, buffer: buffer, boxed: false) + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .photoEmpty(let id): + return ("photoEmpty", [("id", id)]) + case .photo(let flags, let id, let accessHash, let fileReference, let date, let sizes, let dcId): + return ("photo", [("flags", flags), ("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("date", date), ("sizes", sizes), ("dcId", dcId)]) + } + } + + static func parse_photoEmpty(_ reader: BufferReader) -> Photo? { + var _1: Int64? + _1 = reader.readInt64() + let _c1 = _1 != nil + if _c1 { + return Api.Photo.photoEmpty(id: _1!) + } + else { + return nil + } + } + static func parse_photo(_ reader: BufferReader) -> Photo? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: Buffer? + _4 = parseBytes(reader) + var _5: Int32? + _5 = reader.readInt32() + var _6: [Api.PhotoSize]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self) + } + var _7: Int32? + _7 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.Photo.photo(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, sizes: _6!, dcId: _7!) + } + else { + return nil + } + } + + } + enum PhotoSize: TypeConstructorDescription { + case photoSizeEmpty(type: String) + case photoSize(type: String, location: Api.FileLocation, w: Int32, h: Int32, size: Int32) + case photoCachedSize(type: String, location: Api.FileLocation, w: Int32, h: Int32, bytes: Buffer) + case photoStrippedSize(type: String, bytes: Buffer) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .photoSizeEmpty(let type): + if boxed { + buffer.appendInt32(236446268) + } + serializeString(type, buffer: buffer, boxed: false) + break + case .photoSize(let type, let location, let w, let h, let size): + if boxed { + buffer.appendInt32(2009052699) + } + serializeString(type, buffer: buffer, boxed: false) + location.serialize(buffer, true) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + break + case .photoCachedSize(let type, let location, let w, let h, let bytes): + if boxed { + buffer.appendInt32(-374917894) + } + serializeString(type, buffer: buffer, boxed: false) + location.serialize(buffer, true) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + serializeBytes(bytes, buffer: buffer, boxed: false) + break + case .photoStrippedSize(let type, let bytes): + if boxed { + buffer.appendInt32(-525288402) + } + serializeString(type, buffer: buffer, boxed: false) + serializeBytes(bytes, buffer: buffer, boxed: false) + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .photoSizeEmpty(let type): + return ("photoSizeEmpty", [("type", type)]) + case .photoSize(let type, let location, let w, let h, let size): + return ("photoSize", [("type", type), ("location", location), ("w", w), ("h", h), ("size", size)]) + case .photoCachedSize(let type, let location, let w, let h, let bytes): + return ("photoCachedSize", [("type", type), ("location", location), ("w", w), ("h", h), ("bytes", bytes)]) + case .photoStrippedSize(let type, let bytes): + return ("photoStrippedSize", [("type", type), ("bytes", bytes)]) + } + } + + static func parse_photoSizeEmpty(_ reader: BufferReader) -> PhotoSize? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.PhotoSize.photoSizeEmpty(type: _1!) + } + else { + return nil + } + } + static func parse_photoSize(_ reader: BufferReader) -> PhotoSize? { + var _1: String? + _1 = parseString(reader) + var _2: Api.FileLocation? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.FileLocation + } + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.PhotoSize.photoSize(type: _1!, location: _2!, w: _3!, h: _4!, size: _5!) + } + else { + return nil + } + } + static func parse_photoCachedSize(_ reader: BufferReader) -> PhotoSize? { + var _1: String? + _1 = parseString(reader) + var _2: Api.FileLocation? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.FileLocation + } + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Buffer? + _5 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.PhotoSize.photoCachedSize(type: _1!, location: _2!, w: _3!, h: _4!, bytes: _5!) + } + else { + return nil + } + } + static func parse_photoStrippedSize(_ reader: BufferReader) -> PhotoSize? { + var _1: String? + _1 = parseString(reader) + var _2: Buffer? + _2 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.PhotoSize.photoStrippedSize(type: _1!, bytes: _2!) + } + else { + return nil + } + } + + } + enum FileLocation: TypeConstructorDescription { + case fileLocationToBeDeprecated(volumeId: Int64, localId: Int32) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .fileLocationToBeDeprecated(let volumeId, let localId): + if boxed { + buffer.appendInt32(-1132476723) + } + serializeInt64(volumeId, buffer: buffer, boxed: false) + serializeInt32(localId, buffer: buffer, boxed: false) + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .fileLocationToBeDeprecated(let volumeId, let localId): + return ("fileLocationToBeDeprecated", [("volumeId", volumeId), ("localId", localId)]) + } + } + + static func parse_fileLocationToBeDeprecated(_ reader: BufferReader) -> FileLocation? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.FileLocation.fileLocationToBeDeprecated(volumeId: _1!, localId: _2!) + } + else { + return nil + } + } + + } + enum DocumentAttribute: TypeConstructorDescription { + case documentAttributeImageSize(w: Int32, h: Int32) + case documentAttributeAnimated + case documentAttributeSticker(flags: Int32, alt: String, stickerset: Api.InputStickerSet, maskCoords: Api.MaskCoords?) + case documentAttributeVideo(flags: Int32, duration: Int32, w: Int32, h: Int32) + case documentAttributeAudio(flags: Int32, duration: Int32, title: String?, performer: String?, waveform: Buffer?) + case documentAttributeFilename(fileName: String) + case documentAttributeHasStickers + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .documentAttributeImageSize(let w, let h): + if boxed { + buffer.appendInt32(1815593308) + } + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + break + case .documentAttributeAnimated: + if boxed { + buffer.appendInt32(297109817) + } + + break + case .documentAttributeSticker(let flags, let alt, let stickerset, let maskCoords): + if boxed { + buffer.appendInt32(1662637586) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(alt, buffer: buffer, boxed: false) + stickerset.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {maskCoords!.serialize(buffer, true)} + break + case .documentAttributeVideo(let flags, let duration, let w, let h): + if boxed { + buffer.appendInt32(250621158) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(duration, buffer: buffer, boxed: false) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + break + case .documentAttributeAudio(let flags, let duration, let title, let performer, let waveform): + if boxed { + buffer.appendInt32(-1739392570) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(duration, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(performer!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeBytes(waveform!, buffer: buffer, boxed: false)} + break + case .documentAttributeFilename(let fileName): + if boxed { + buffer.appendInt32(358154344) + } + serializeString(fileName, buffer: buffer, boxed: false) + break + case .documentAttributeHasStickers: + if boxed { + buffer.appendInt32(-1744710921) + } + + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .documentAttributeImageSize(let w, let h): + return ("documentAttributeImageSize", [("w", w), ("h", h)]) + case .documentAttributeAnimated: + return ("documentAttributeAnimated", []) + case .documentAttributeSticker(let flags, let alt, let stickerset, let maskCoords): + return ("documentAttributeSticker", [("flags", flags), ("alt", alt), ("stickerset", stickerset), ("maskCoords", maskCoords)]) + case .documentAttributeVideo(let flags, let duration, let w, let h): + return ("documentAttributeVideo", [("flags", flags), ("duration", duration), ("w", w), ("h", h)]) + case .documentAttributeAudio(let flags, let duration, let title, let performer, let waveform): + return ("documentAttributeAudio", [("flags", flags), ("duration", duration), ("title", title), ("performer", performer), ("waveform", waveform)]) + case .documentAttributeFilename(let fileName): + return ("documentAttributeFilename", [("fileName", fileName)]) + case .documentAttributeHasStickers: + return ("documentAttributeHasStickers", []) + } + } + + static func parse_documentAttributeImageSize(_ reader: BufferReader) -> DocumentAttribute? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.DocumentAttribute.documentAttributeImageSize(w: _1!, h: _2!) + } + else { + return nil + } + } + static func parse_documentAttributeAnimated(_ reader: BufferReader) -> DocumentAttribute? { + return Api.DocumentAttribute.documentAttributeAnimated + } + static func parse_documentAttributeSticker(_ reader: BufferReader) -> DocumentAttribute? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Api.InputStickerSet? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.InputStickerSet + } + var _4: Api.MaskCoords? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.MaskCoords + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.DocumentAttribute.documentAttributeSticker(flags: _1!, alt: _2!, stickerset: _3!, maskCoords: _4) + } + else { + return nil + } + } + static func parse_documentAttributeVideo(_ reader: BufferReader) -> DocumentAttribute? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.DocumentAttribute.documentAttributeVideo(flags: _1!, duration: _2!, w: _3!, h: _4!) + } + else { + return nil + } + } + static func parse_documentAttributeAudio(_ reader: BufferReader) -> DocumentAttribute? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } + var _4: String? + if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) } + var _5: Buffer? + if Int(_1!) & Int(1 << 2) != 0 {_5 = parseBytes(reader) } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.DocumentAttribute.documentAttributeAudio(flags: _1!, duration: _2!, title: _3, performer: _4, waveform: _5) + } + else { + return nil + } + } + static func parse_documentAttributeFilename(_ reader: BufferReader) -> DocumentAttribute? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.DocumentAttribute.documentAttributeFilename(fileName: _1!) + } + else { + return nil + } + } + static func parse_documentAttributeHasStickers(_ reader: BufferReader) -> DocumentAttribute? { + return Api.DocumentAttribute.documentAttributeHasStickers + } + + } + enum InputStickerSet: TypeConstructorDescription { + case inputStickerSetEmpty + case inputStickerSetID(id: Int64, accessHash: Int64) + case inputStickerSetShortName(shortName: String) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputStickerSetEmpty: + if boxed { + buffer.appendInt32(-4838507) + } + + break + case .inputStickerSetID(let id, let accessHash): + if boxed { + buffer.appendInt32(-1645763991) + } + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + break + case .inputStickerSetShortName(let shortName): + if boxed { + buffer.appendInt32(-2044933984) + } + serializeString(shortName, buffer: buffer, boxed: false) + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inputStickerSetEmpty: + return ("inputStickerSetEmpty", []) + case .inputStickerSetID(let id, let accessHash): + return ("inputStickerSetID", [("id", id), ("accessHash", accessHash)]) + case .inputStickerSetShortName(let shortName): + return ("inputStickerSetShortName", [("shortName", shortName)]) + } + } + + static func parse_inputStickerSetEmpty(_ reader: BufferReader) -> InputStickerSet? { + return Api.InputStickerSet.inputStickerSetEmpty + } + static func parse_inputStickerSetID(_ reader: BufferReader) -> InputStickerSet? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.InputStickerSet.inputStickerSetID(id: _1!, accessHash: _2!) + } + else { + return nil + } + } + static func parse_inputStickerSetShortName(_ reader: BufferReader) -> InputStickerSet? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.InputStickerSet.inputStickerSetShortName(shortName: _1!) + } + else { + return nil + } + } + + } + enum InputFileLocation: TypeConstructorDescription { + case inputPhotoFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, thumbSize: String) + case inputDocumentFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, thumbSize: String) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputPhotoFileLocation(let id, let accessHash, let fileReference, let thumbSize): + if boxed { + buffer.appendInt32(1075322878) + } + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeBytes(fileReference, buffer: buffer, boxed: false) + serializeString(thumbSize, buffer: buffer, boxed: false) + break + case .inputDocumentFileLocation(let id, let accessHash, let fileReference, let thumbSize): + if boxed { + buffer.appendInt32(-1160743548) + } + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeBytes(fileReference, buffer: buffer, boxed: false) + serializeString(thumbSize, buffer: buffer, boxed: false) + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inputPhotoFileLocation(let id, let accessHash, let fileReference, let thumbSize): + return ("inputPhotoFileLocation", [("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("thumbSize", thumbSize)]) + case .inputDocumentFileLocation(let id, let accessHash, let fileReference, let thumbSize): + return ("inputDocumentFileLocation", [("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("thumbSize", thumbSize)]) + } + } + + static func parse_inputPhotoFileLocation(_ reader: BufferReader) -> InputFileLocation? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + var _3: Buffer? + _3 = parseBytes(reader) + var _4: String? + _4 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.InputFileLocation.inputPhotoFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!) + } + else { + return nil + } + } + static func parse_inputDocumentFileLocation(_ reader: BufferReader) -> InputFileLocation? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + var _3: Buffer? + _3 = parseBytes(reader) + var _4: String? + _4 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.InputFileLocation.inputDocumentFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!) + } + else { + return nil + } + } + + } + enum MaskCoords: TypeConstructorDescription { + case maskCoords(n: Int32, x: Double, y: Double, zoom: Double) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .maskCoords(let n, let x, let y, let zoom): + if boxed { + buffer.appendInt32(-1361650766) + } + serializeInt32(n, buffer: buffer, boxed: false) + serializeDouble(x, buffer: buffer, boxed: false) + serializeDouble(y, buffer: buffer, boxed: false) + serializeDouble(zoom, buffer: buffer, boxed: false) + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .maskCoords(let n, let x, let y, let zoom): + return ("maskCoords", [("n", n), ("x", x), ("y", y), ("zoom", zoom)]) + } + } + + static func parse_maskCoords(_ reader: BufferReader) -> MaskCoords? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Double? + _2 = reader.readDouble() + var _3: Double? + _3 = reader.readDouble() + var _4: Double? + _4 = reader.readDouble() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.MaskCoords.maskCoords(n: _1!, x: _2!, y: _3!, zoom: _4!) + } + else { + return nil + } + } + + } + enum Document: TypeConstructorDescription { + case document(flags: Int32, id: Int64, accessHash: Int64, fileReference: Buffer, date: Int32, mimeType: String, size: Int32, thumbs: [Api.PhotoSize]?, dcId: Int32, attributes: [Api.DocumentAttribute]) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let dcId, let attributes): + if boxed { + buffer.appendInt32(-1683841855) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeBytes(fileReference, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + serializeInt32(size, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(thumbs!.count)) + for item in thumbs! { + item.serialize(buffer, true) + }} + serializeInt32(dcId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(attributes.count)) + for item in attributes { + item.serialize(buffer, true) + } + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let dcId, let attributes): + return ("document", [("flags", flags), ("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("date", date), ("mimeType", mimeType), ("size", size), ("thumbs", thumbs), ("dcId", dcId), ("attributes", attributes)]) + } + } + + static func parse_document(_ reader: BufferReader) -> Document? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: Buffer? + _4 = parseBytes(reader) + var _5: Int32? + _5 = reader.readInt32() + var _6: String? + _6 = parseString(reader) + var _7: Int32? + _7 = reader.readInt32() + var _8: [Api.PhotoSize]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self) + } } + var _9: Int32? + _9 = reader.readInt32() + var _10: [Api.DocumentAttribute]? + if let _ = reader.readInt32() { + _10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DocumentAttribute.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil + let _c9 = _9 != nil + let _c10 = _10 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { + return Api.Document.document(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, mimeType: _6!, size: _7!, thumbs: _8, dcId: _9!, attributes: _10!) + } + else { + return nil + } + } + + } +} +extension Api { + struct functions { + + } +} diff --git a/NotificationService/ImageData.swift b/NotificationService/ImageData.swift index 013ccebf1f..5ecec6aaa3 100644 --- a/NotificationService/ImageData.swift +++ b/NotificationService/ImageData.swift @@ -13,264 +13,6 @@ struct ImageResource { } } -private final class Buffer { - var data: UnsafeMutableRawPointer? - var _size: UInt = 0 - private var capacity: UInt = 0 - private let freeWhenDone: Bool - - var size: Int { - return Int(self._size) - } - - deinit { - if self.freeWhenDone { - free(self.data) - } - } - - init(memory: UnsafeMutableRawPointer?, size: Int, capacity: Int, freeWhenDone: Bool) { - self.data = memory - self._size = UInt(size) - self.capacity = UInt(capacity) - self.freeWhenDone = freeWhenDone - } - - init() { - self.data = nil - self._size = 0 - self.capacity = 0 - self.freeWhenDone = true - } - - convenience init(data: Data?) { - self.init() - - if let data = data { - data.withUnsafeBytes { bytes in - self.appendBytes(bytes, length: UInt(data.count)) - } - } - } - - func makeData() -> Data { - return self.withUnsafeMutablePointer { pointer, size -> Data in - if let pointer = pointer { - return Data(bytes: pointer.assumingMemoryBound(to: UInt8.self), count: Int(size)) - } else { - return Data() - } - } - } - - var description: String { - get { - var string = "" - if let data = self.data { - var i: UInt = 0 - let bytes = data.assumingMemoryBound(to: UInt8.self) - while i < _size && i < 8 { - string += String(format: "%02x", Int(bytes.advanced(by: Int(i)).pointee)) - i += 1 - } - if i < _size { - string += "...\(_size)b" - } - } else { - string += "" - } - return string - } - } - - func appendBytes(_ bytes: UnsafeRawPointer, length: UInt) { - if self.capacity < self._size + length { - self.capacity = self._size + length + 128 - if self.data == nil { - self.data = malloc(Int(self.capacity))! - } - else { - self.data = realloc(self.data, Int(self.capacity))! - } - } - - memcpy(self.data?.advanced(by: Int(self._size)), bytes, Int(length)) - self._size += length - } - - func appendBuffer(_ buffer: Buffer) { - if self.capacity < self._size + buffer._size { - self.capacity = self._size + buffer._size + 128 - if self.data == nil { - self.data = malloc(Int(self.capacity))! - } - else { - self.data = realloc(self.data, Int(self.capacity))! - } - } - - memcpy(self.data?.advanced(by: Int(self._size)), buffer.data, Int(buffer._size)) - } - - func appendInt32(_ value: Int32) { - var v = value - self.appendBytes(&v, length: 4) - } - - func appendInt64(_ value: Int64) { - var v = value - self.appendBytes(&v, length: 8) - } - - func appendDouble(_ value: Double) { - var v = value - self.appendBytes(&v, length: 8) - } - - func withUnsafeMutablePointer(_ f: (UnsafeMutableRawPointer?, UInt) -> R) -> R { - return f(self.data, self._size) - } -} - -private class BufferReader { - private let buffer: Buffer - private(set) var offset: UInt = 0 - - init(_ buffer: Buffer) { - self.buffer = buffer - } - - func reset() { - self.offset = 0 - } - - func skip(_ count: Int) { - self.offset = min(self.buffer._size, self.offset + UInt(count)) - } - - func readInt32() -> Int32? { - if self.offset + 4 <= self.buffer._size { - let value: Int32 = buffer.data!.advanced(by: Int(self.offset)).assumingMemoryBound(to: Int32.self).pointee - self.offset += 4 - return value - } - return nil - } - - func readInt64() -> Int64? { - if self.offset + 8 <= self.buffer._size { - let value: Int64 = buffer.data!.advanced(by: Int(self.offset)).assumingMemoryBound(to: Int64.self).pointee - self.offset += 8 - return value - } - return nil - } - - func readDouble() -> Double? { - if self.offset + 8 <= self.buffer._size { - let value: Double = buffer.data!.advanced(by: Int(self.offset)).assumingMemoryBound(to: Double.self).pointee - self.offset += 8 - return value - } - return nil - } - - func readBytesAsInt32(_ count: Int) -> Int32? { - if count == 0 { - return 0 - } - else if count > 0 && count <= 4 || self.offset + UInt(count) <= self.buffer._size { - var value: Int32 = 0 - memcpy(&value, self.buffer.data?.advanced(by: Int(self.offset)), count) - self.offset += UInt(count) - return value - } - return nil - } - - func readBuffer(_ count: Int) -> Buffer? { - if count >= 0 && self.offset + UInt(count) <= self.buffer._size { - let buffer = Buffer() - buffer.appendBytes((self.buffer.data?.advanced(by: Int(self.offset)))!, length: UInt(count)) - self.offset += UInt(count) - return buffer - } - return nil - } -} - -private func serializeBytes(_ value: Buffer, buffer: Buffer, boxed: Bool) { - if boxed { - buffer.appendInt32(-1255641564) - } - - var length: Int32 = Int32(value.size) - var padding: Int32 = 0 - if (length >= 254) - { - var tmp: UInt8 = 254 - buffer.appendBytes(&tmp, length: 1) - buffer.appendBytes(&length, length: 3) - padding = (((length % 4) == 0 ? length : (length + 4 - (length % 4)))) - length; - } - else - { - buffer.appendBytes(&length, length: 1) - - let e1 = (((length + 1) % 4) == 0 ? (length + 1) : ((length + 1) + 4 - ((length + 1) % 4))) - padding = (e1) - (length + 1) - } - - if value.size != 0 { - buffer.appendBytes(value.data!, length: UInt(length)) - } - - var i: Int32 = 0 - var tmp: UInt8 = 0 - while i < padding { - buffer.appendBytes(&tmp, length: 1) - i += 1 - } -} - -private func roundUp(_ numToRound: Int, multiple: Int) -> Int { - if multiple == 0 { - return numToRound - } - - let remainder = numToRound % multiple - if remainder == 0 { - return numToRound - } - - return numToRound + multiple - remainder -} - -private func parseBytes(_ reader: BufferReader) -> Buffer? { - if let tmp = reader.readBytesAsInt32(1) { - var paddingBytes: Int = 0 - var length: Int = 0 - if tmp == 254 { - if let len = reader.readBytesAsInt32(3) { - length = Int(len) - paddingBytes = roundUp(length, multiple: 4) - length - } - else { - return nil - } - } - else { - length = Int(tmp) - paddingBytes = roundUp(length + 1, multiple: 4) - (length + 1) - } - - let buffer = reader.readBuffer(length) - reader.skip(paddingBytes) - return buffer - } - return nil -} - private class Keychain: NSObject, MTKeychain { var dict: [String: Data] = [:] @@ -304,7 +46,7 @@ private final class ParsedFile: NSObject { } } -func fetchImageWithAccount(proxyConnection: AccountProxyConnection?, account: StoredAccountInfo, resource: ImageResource, completion: @escaping (Data?) -> Void) -> () -> Void { +func fetchImageWithAccount(proxyConnection: AccountProxyConnection?, account: StoredAccountInfo, inputFileLocation: Api.InputFileLocation, datacenterId: Int32, completion: @escaping (Data?) -> Void) -> () -> Void { MTLogSetEnabled(true) MTLogSetLoggingFunction({ str, args in //let string = NSString(format: str! as NSString, args!) @@ -367,7 +109,7 @@ func fetchImageWithAccount(proxyConnection: AccountProxyConnection?, account: St context.updateAuthInfoForDatacenter(withId: Int(id), authInfo: MTDatacenterAuthInfo(authKey: info.masterKey.data, authKeyId: info.masterKey.id, saltSet: [], authKeyAttributes: [:], mainTempAuthKey: nil, mediaTempAuthKey: nil)) } - let mtProto = MTProto(context: context, datacenterId: resource.datacenterId, usageCalculationInfo: nil)! + let mtProto = MTProto(context: context, datacenterId: Int(datacenterId), usageCalculationInfo: nil)! mtProto.useTempAuthKeys = context.useTempAuthKeys mtProto.checkForProxyConnectionIssues = false @@ -378,18 +120,12 @@ func fetchImageWithAccount(proxyConnection: AccountProxyConnection?, account: St let buffer = Buffer() buffer.appendInt32(-475607115) //upload.getFile - - buffer.appendInt32(-539317279) //InputFileLocation.inputFileLocation - buffer.appendInt64(resource.volumeId) - buffer.appendInt32(resource.localId) - buffer.appendInt64(resource.secret) - - serializeBytes(Buffer(data: resource.fileReference), buffer: buffer, boxed: false) + Api.serializeObject(inputFileLocation, buffer: buffer, boxed: true) buffer.appendInt32(0) buffer.appendInt32(32 * 1024) - request.setPayload(buffer.makeData(), metadata: "getFile", responseParser: { response in + request.setPayload(buffer.makeData(), metadata: "getFile", shortMetadata: "getFile", responseParser: { response in let reader = BufferReader(Buffer(data: response)) guard let signature = reader.readInt32() else { return ParsedFile(data: nil) diff --git a/NotificationService/NotificationService.swift b/NotificationService/NotificationService.swift index dde5e21390..ea48894c2c 100644 --- a/NotificationService/NotificationService.swift +++ b/NotificationService/NotificationService.swift @@ -1,5 +1,6 @@ import Foundation import UserNotifications +import MtProtoKitDynamic private var sharedLogger: Logger? @@ -146,92 +147,85 @@ private final class Logger { } } - -private func dataWithHexString(_ string: String) -> Data { - var hex = string - if hex.count % 2 != 0 { - return Data() +private func parseBase64(string: String) -> Data? { + var string = string + string = string.replacingOccurrences(of: "-", with: "+") + string = string.replacingOccurrences(of: "_", with: "/") + while string.count % 4 != 0 { + string.append("=") } - var data = Data() - while hex.count > 0 { - let subIndex = hex.index(hex.startIndex, offsetBy: 2) - let c = String(hex[.. Int64? { - if let value = value as? String { - return Int64(value) - } else if let value = value as? Int64 { - return value - } else { - return nil - } +enum ParsedMediaAttachment { + case document(Api.Document) + case photo(Api.Photo) } -private func parseInt32(_ value: Any?) -> Int32? { - if let value = value as? String { - return Int32(value) - } else if let value = value as? Int32 { - return value - } else { +private func parseAttachment(data: Data) -> (ParsedMediaAttachment, Data)? { + let reader = BufferReader(Buffer(data: data)) + guard let initialSignature = reader.readInt32() else { return nil } -} - -private func parseImageLocation(_ dict: [AnyHashable: Any]) -> (size: (width: Int32, height: Int32)?, resource: ImageResource)? { - guard let datacenterId = parseInt32(dict["dc_id"]) else { - return nil - } - guard let volumeId = parseInt64(dict["volume_id"]) else { - return nil - } - guard let localId = parseInt32(dict["local_id"]) else { - return nil - } - guard let secret = parseInt64(dict["secret"]) else { - return nil - } - var fileReference: Data? - if let fileReferenceString = dict["file_reference"] as? String { - fileReference = Data(base64Encoded: fileReferenceString) - } - var size: (Int32, Int32)? - if let width = parseInt32(dict["w"]), let height = parseInt32(dict["h"]) { - size = (width, height) - } - return (size, ImageResource(datacenterId: Int(datacenterId), volumeId: volumeId, localId: localId, secret: secret, fileReference: fileReference)) -} - -private func hexString(_ data: Data) -> String { - let hexString = NSMutableString() - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - for i in 0 ..< data.count { - hexString.appendFormat("%02x", UInt(bytes.advanced(by: i).pointee)) - } - } - return hexString as String + let buffer: Buffer + if initialSignature == 0x3072cfa1 { + guard let bytes = parseBytes(reader) else { + return nil + } + guard let decompressedData = MTGzip.decompress(bytes.makeData()) else { + return nil + } + buffer = Buffer(data: decompressedData) + } else { + buffer = Buffer(data: data) + } + + if let result = Api.parse(buffer) { + if let photo = result as? Api.Photo { + return (.photo(photo), buffer.makeData()) + } else if let document = result as? Api.Document { + return (.document(document), buffer.makeData()) + } else { + return nil + } + } else { + return nil + } } -private func serializeImageLocation(_ resource: ImageResource) -> [AnyHashable: Any] { - var result: [AnyHashable: Any] = [:] - result["datacenterId"] = Int32(resource.datacenterId) - result["volumeId"] = resource.volumeId - result["localId"] = resource.localId - result["secret"] = resource.secret - if let fileReference = resource.fileReference { - result["fileReference"] = hexString(fileReference) +private func photoSizeDimensions(_ size: Api.PhotoSize) -> CGSize? { + switch size { + case let .photoSize(_, _, w, h, _): + return CGSize(width: CGFloat(w), height: CGFloat(h)) + case let .photoCachedSize(_, _, w, h, _): + return CGSize(width: CGFloat(w), height: CGFloat(h)) + default: + return nil + } +} + +private func photoDimensions(_ photo: Api.Photo) -> CGSize? { + switch photo { + case let .photo(_, _, _, _, _, sizes, _): + for size in sizes.reversed() { + if let dimensions = photoSizeDimensions(size) { + return dimensions + } + } + return nil + case .photoEmpty: + return nil + } +} + +private func photoSizes(_ photo: Api.Photo) -> [Api.PhotoSize] { + switch photo { + case let .photo(_, _, _, _, _, sizes, _): + return sizes + case .photoEmpty: + return [] } - return result } class NotificationService: UNNotificationServiceExtension { @@ -281,13 +275,8 @@ class NotificationService: UNNotificationServiceExtension { self.bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent var encryptedData: Data? - if var encryptedPayload = request.content.userInfo["p"] as? String { - encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+") - encryptedPayload = encryptedPayload.replacingOccurrences(of: "_", with: "/") - while encryptedPayload.count % 4 != 0 { - encryptedPayload.append("=") - } - encryptedData = Data(base64Encoded: encryptedPayload) + if let encryptedPayload = request.content.userInfo["p"] as? String { + encryptedData = parseBase64(string: encryptedPayload) } Logger.shared.log("NotificationService", "received notification \(request), parsed encryptedData \(String(describing: encryptedData))") @@ -323,11 +312,13 @@ class NotificationService: UNNotificationServiceExtension { } } - var thumbnailImage: ImageResource? - var fullSizeImage: (size: (width: Int32, height: Int32)?, resource: ImageResource)? - if let thumbLoc = dict["media_loc"] as? [AnyHashable: Any] { - thumbnailImage = (thumbLoc["thumb"] as? [AnyHashable: Any]).flatMap(parseImageLocation)?.resource - fullSizeImage = (thumbLoc["full"] as? [AnyHashable: Any]).flatMap(parseImageLocation) + var attachment: ParsedMediaAttachment? + var attachmentData: Data? + if let attachmentDataString = dict["attachb64"] as? String, let attachmentDataValue = parseBase64(string: attachmentDataString) { + if let value = parseAttachment(data: attachmentDataValue) { + attachment = value.0 + attachmentData = value.1 + } } let imagesPath = NSTemporaryDirectory() + "aps-data" @@ -337,9 +328,57 @@ class NotificationService: UNNotificationServiceExtension { let mediaBoxPath = accountBasePath + "/postbox/media" - let tempImagePath = thumbnailImage.flatMap({ imagesPath + "/\($0.resourceId).jpg" }) - let mediaBoxThumbnailImagePath = thumbnailImage.flatMap({ mediaBoxPath + "/\($0.resourceId)" }) - let mediaBoxFullSizeImagePath = fullSizeImage.flatMap({ mediaBoxPath + "/\($0.resource.resourceId)" }) + var tempImagePath: String? + var mediaBoxThumbnailImagePath: String? + + var inputFileLocation: (Int32, Api.InputFileLocation)? + var fetchResourceId: String? + + if let attachment = attachment { + switch attachment { + case let .photo(photo): + switch photo { + case let .photo(_, id, accessHash, fileReference, _, sizes, dcId): + loop: for size in sizes { + switch size { + case let .photoSize(type, _, _, _, _): + if type == "m" { + inputFileLocation = (dcId, .inputPhotoFileLocation(id: id, accessHash: accessHash, fileReference: fileReference, thumbSize: type)) + fetchResourceId = "telegram-cloud-photo-size-\(dcId)-\(id)-\(type)" + break loop + } + default: + break + } + } + case .photoEmpty: + break + } + case let .document(document): + switch document { + case let .document(_, id, accessHash, fileReference, _, _, _, thumbs, dcId, _): + if let thumbs = thumbs { + loop: for size in thumbs { + switch size { + case let .photoSize(type, _, _, _, _): + if type == "m" { + inputFileLocation = (dcId, .inputDocumentFileLocation(id: id, accessHash: accessHash, fileReference: fileReference, thumbSize: type)) + fetchResourceId = "telegram-cloud-photo-size-\(dcId)-\(id)-\(type)" + break loop + } + default: + break + } + } + } + } + } + } + + if let fetchResourceId = fetchResourceId { + tempImagePath = imagesPath + "/\(fetchResourceId).jpg" + mediaBoxThumbnailImagePath = mediaBoxPath + "/\(fetchResourceId).jpg" + } if let aps = dict["aps"] as? [AnyHashable: Any] { if let alert = aps["alert"] as? String { @@ -365,31 +404,12 @@ class NotificationService: UNNotificationServiceExtension { } if let category = aps["category"] as? String { self.bestAttemptContent?.categoryIdentifier = category - if let peerId = peerId, let messageId = messageId, let thumbnailResource = thumbnailImage, let (maybeSize, resource) = fullSizeImage, let size = maybeSize { + if let peerId = peerId, let messageId = messageId, let _ = attachment, let attachmentData = attachmentData { userInfo["peerId"] = peerId.toInt64() userInfo["messageId.namespace"] = 0 as Int32 userInfo["messageId.id"] = messageId - var imageInfo: [String: Any] = [:] - imageInfo["width"] = Int(size.width) - imageInfo["height"] = Int(size.height) - - var thumbnail: [String: Any] = [:] - if let mediaBoxThumbnailImagePath = mediaBoxThumbnailImagePath { - thumbnail["path"] = mediaBoxThumbnailImagePath - } - thumbnail["fileLocation"] = serializeImageLocation(thumbnailResource) - - var fullSize: [String: Any] = [:] - if let mediaBoxFullSizeImagePath = mediaBoxFullSizeImagePath { - fullSize["path"] = mediaBoxFullSizeImagePath - } - fullSize["fileLocation"] = serializeImageLocation(resource) - - imageInfo["thumbnail"] = thumbnail - imageInfo["fullSize"] = fullSize - - userInfo["mediaInfo"] = ["image": imageInfo] + userInfo["media"] = attachmentData.base64EncodedString() if category == "r" { self.bestAttemptContent?.categoryIdentifier = "withReplyMedia" @@ -403,8 +423,8 @@ class NotificationService: UNNotificationServiceExtension { self.bestAttemptContent?.userInfo = userInfo self.cancelFetch?() - if let mediaBoxThumbnailImagePath = mediaBoxThumbnailImagePath, let tempImagePath = tempImagePath, let thumbnailImage = thumbnailImage { - self.cancelFetch = fetchImageWithAccount(proxyConnection: accountInfos.proxy, account: account, resource: thumbnailImage, completion: { [weak self] data in + if let mediaBoxThumbnailImagePath = mediaBoxThumbnailImagePath, let tempImagePath = tempImagePath, let (datacenterId, inputFileLocation) = inputFileLocation { + self.cancelFetch = fetchImageWithAccount(proxyConnection: accountInfos.proxy, account: account, inputFileLocation: inputFileLocation, datacenterId: datacenterId, completion: { [weak self] data in DispatchQueue.main.async { guard let strongSelf = self else { return diff --git a/NotificationService/ReadBuffer.swift b/NotificationService/ReadBuffer.swift new file mode 100644 index 0000000000..9781b76896 --- /dev/null +++ b/NotificationService/ReadBuffer.swift @@ -0,0 +1,310 @@ +import Foundation + +class Buffer: CustomStringConvertible { + var data: UnsafeMutableRawPointer? + var _size: UInt = 0 + private var capacity: UInt = 0 + private let freeWhenDone: Bool + + var size: Int { + return Int(self._size) + } + + deinit { + if self.freeWhenDone { + free(self.data) + } + } + + init(memory: UnsafeMutableRawPointer?, size: Int, capacity: Int, freeWhenDone: Bool) { + self.data = memory + self._size = UInt(size) + self.capacity = UInt(capacity) + self.freeWhenDone = freeWhenDone + } + + init() { + self.data = nil + self._size = 0 + self.capacity = 0 + self.freeWhenDone = true + } + + convenience init(data: Data?) { + self.init() + + if let data = data { + data.withUnsafeBytes { bytes in + self.appendBytes(bytes, length: UInt(data.count)) + } + } + } + + + + func makeData() -> Data { + return self.withUnsafeMutablePointer { pointer, size -> Data in + if let pointer = pointer { + return Data(bytes: pointer.assumingMemoryBound(to: UInt8.self), count: Int(size)) + } else { + return Data() + } + } + } + + var description: String { + get { + var string = "" + if let data = self.data { + var i: UInt = 0 + let bytes = data.assumingMemoryBound(to: UInt8.self) + while i < _size && i < 8 { + string += String(format: "%02x", Int(bytes.advanced(by: Int(i)).pointee)) + i += 1 + } + if i < _size { + string += "...\(_size)b" + } + } else { + string += "" + } + return string + } + } + + func appendBytes(_ bytes: UnsafeRawPointer, length: UInt) { + if self.capacity < self._size + length { + self.capacity = self._size + length + 128 + if self.data == nil { + self.data = malloc(Int(self.capacity))! + } + else { + self.data = realloc(self.data, Int(self.capacity))! + } + } + + memcpy(self.data?.advanced(by: Int(self._size)), bytes, Int(length)) + self._size += length + } + + func appendBuffer(_ buffer: Buffer) { + if self.capacity < self._size + buffer._size { + self.capacity = self._size + buffer._size + 128 + if self.data == nil { + self.data = malloc(Int(self.capacity))! + } + else { + self.data = realloc(self.data, Int(self.capacity))! + } + } + + memcpy(self.data?.advanced(by: Int(self._size)), buffer.data, Int(buffer._size)) + } + + func appendInt32(_ value: Int32) { + var v = value + self.appendBytes(&v, length: 4) + } + + func appendInt64(_ value: Int64) { + var v = value + self.appendBytes(&v, length: 8) + } + + func appendDouble(_ value: Double) { + var v = value + self.appendBytes(&v, length: 8) + } + + func withUnsafeMutablePointer(_ f: (UnsafeMutableRawPointer?, UInt) -> R) -> R { + return f(self.data, self._size) + } +} + +class BufferReader { + private let buffer: Buffer + private(set) var offset: UInt = 0 + + init(_ buffer: Buffer) { + self.buffer = buffer + } + + func reset() { + self.offset = 0 + } + + func skip(_ count: Int) { + self.offset = min(self.buffer._size, self.offset + UInt(count)) + } + + func readInt32() -> Int32? { + if self.offset + 4 <= self.buffer._size { + let value: Int32 = buffer.data!.advanced(by: Int(self.offset)).assumingMemoryBound(to: Int32.self).pointee + self.offset += 4 + return value + } + return nil + } + + func readInt64() -> Int64? { + if self.offset + 8 <= self.buffer._size { + let value: Int64 = buffer.data!.advanced(by: Int(self.offset)).assumingMemoryBound(to: Int64.self).pointee + self.offset += 8 + return value + } + return nil + } + + func readDouble() -> Double? { + if self.offset + 8 <= self.buffer._size { + let value: Double = buffer.data!.advanced(by: Int(self.offset)).assumingMemoryBound(to: Double.self).pointee + self.offset += 8 + return value + } + return nil + } + + func readBytesAsInt32(_ count: Int) -> Int32? { + if count == 0 { + return 0 + } + else if count > 0 && count <= 4 || self.offset + UInt(count) <= self.buffer._size { + var value: Int32 = 0 + memcpy(&value, self.buffer.data?.advanced(by: Int(self.offset)), count) + self.offset += UInt(count) + return value + } + return nil + } + + func readBuffer(_ count: Int) -> Buffer? { + if count >= 0 && self.offset + UInt(count) <= self.buffer._size { + let buffer = Buffer() + buffer.appendBytes((self.buffer.data?.advanced(by: Int(self.offset)))!, length: UInt(count)) + self.offset += UInt(count) + return buffer + } + return nil + } + + func withReadBufferNoCopy(_ count: Int, _ f: (Buffer) -> T) -> T? { + if count >= 0 && self.offset + UInt(count) <= self.buffer._size { + return f(Buffer(memory: self.buffer.data!.advanced(by: Int(self.offset)), size: count, capacity: count, freeWhenDone: false)) + } + return nil + } +} + +private func roundUp(_ numToRound: Int, multiple: Int) -> Int { + if multiple == 0 { + return numToRound + } + + let remainder = numToRound % multiple + if remainder == 0 { + return numToRound + } + + return numToRound + multiple - remainder +} + +func parseBytes(_ reader: BufferReader) -> Buffer? { + if let tmp = reader.readBytesAsInt32(1) { + var paddingBytes: Int = 0 + var length: Int = 0 + if tmp == 254 { + if let len = reader.readBytesAsInt32(3) { + length = Int(len) + paddingBytes = roundUp(length, multiple: 4) - length + } + else { + return nil + } + } + else { + length = Int(tmp) + paddingBytes = roundUp(length + 1, multiple: 4) - (length + 1) + } + + let buffer = reader.readBuffer(length) + reader.skip(paddingBytes) + return buffer + } + return nil +} + +func parseString(_ reader: BufferReader) -> String? { + if let buffer = parseBytes(reader) { + return (NSString(data: buffer.makeData() as Data, encoding: String.Encoding.utf8.rawValue) as? String) ?? "" + } + return nil +} + +protocol TypeConstructorDescription { + func descriptionFields() -> (String, [(String, Any)]) +} + +func serializeInt32(_ value: Int32, buffer: Buffer, boxed: Bool) { + if boxed { + buffer.appendInt32(-1471112230) + } + buffer.appendInt32(value) +} + +func serializeInt64(_ value: Int64, buffer: Buffer, boxed: Bool) { + if boxed { + buffer.appendInt32(570911930) + } + buffer.appendInt64(value) +} + +func serializeDouble(_ value: Double, buffer: Buffer, boxed: Bool) { + if boxed { + buffer.appendInt32(571523412) + } + buffer.appendDouble(value) +} + +func serializeString(_ value: String, buffer: Buffer, boxed: Bool) { + let stringBuffer = Buffer() + let data = value.data(using: .utf8, allowLossyConversion: true) ?? Data() + data.withUnsafeBytes { bytes in + stringBuffer.appendBytes(bytes, length: UInt(data.count)) + } + serializeBytes(stringBuffer, buffer: buffer, boxed: boxed) +} + +func serializeBytes(_ value: Buffer, buffer: Buffer, boxed: Bool) { + if boxed { + buffer.appendInt32(-1255641564) + } + + var length: Int32 = Int32(value.size) + var padding: Int32 = 0 + if (length >= 254) + { + var tmp: UInt8 = 254 + buffer.appendBytes(&tmp, length: 1) + buffer.appendBytes(&length, length: 3) + padding = (((length % 4) == 0 ? length : (length + 4 - (length % 4)))) - length; + } + else + { + buffer.appendBytes(&length, length: 1) + + let e1 = (((length + 1) % 4) == 0 ? (length + 1) : ((length + 1) + 4 - ((length + 1) % 4))) + padding = (e1) - (length + 1) + } + + if value.size != 0 { + buffer.appendBytes(value.data!, length: UInt(length)) + } + + var i: Int32 = 0 + var tmp: UInt8 = 0 + while i < padding { + buffer.appendBytes(&tmp, length: 1) + i += 1 + } +} + diff --git a/NotificationService/Serialization.swift b/NotificationService/Serialization.swift index 29ce311a43..d68bc062a9 100644 --- a/NotificationService/Serialization.swift +++ b/NotificationService/Serialization.swift @@ -10,7 +10,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 97 + return 98 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/Telegram-iOS.xcodeproj/project.pbxproj b/Telegram-iOS.xcodeproj/project.pbxproj index b52e9be5ad..10fb6ae027 100644 --- a/Telegram-iOS.xcodeproj/project.pbxproj +++ b/Telegram-iOS.xcodeproj/project.pbxproj @@ -223,6 +223,8 @@ D00859B81B28189D00EAF753 /* Telegram_iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00859B71B28189D00EAF753 /* Telegram_iOSTests.swift */; }; D00ED75A1FE94630001F38BD /* AppIntentVocabulary.plist in Resources */ = {isa = PBXBuildFile; fileRef = D00ED7581FE94630001F38BD /* AppIntentVocabulary.plist */; }; D00ED75D1FE95287001F38BD /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D00ED75B1FE95287001F38BD /* InfoPlist.strings */; }; + D015E011225CCEB300CB9E8A /* ReadBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015E010225CCEB300CB9E8A /* ReadBuffer.swift */; }; + D015E01F225CDF5100CB9E8A /* Api0.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015E01E225CDF5000CB9E8A /* Api0.swift */; }; D01A47551F4DBED700383CC1 /* HockeySDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D01A47541F4DBED700383CC1 /* HockeySDK.framework */; }; D021D4D9219CAEDD0064BEBA /* Config-Fork.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = D021D4D8219CAEDD0064BEBA /* Config-Fork.xcconfig */; }; D02CF5FD215D9ABF00E0F56A /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0AA1A671D568BA400152314 /* UserNotifications.framework */; }; @@ -928,6 +930,8 @@ D00859B71B28189D00EAF753 /* Telegram_iOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Telegram_iOSTests.swift; sourceTree = ""; }; D00ED7591FE94630001F38BD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = en; path = en.lproj/AppIntentVocabulary.plist; sourceTree = ""; }; D00ED75C1FE95287001F38BD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + D015E010225CCEB300CB9E8A /* ReadBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadBuffer.swift; sourceTree = ""; }; + D015E01E225CDF5000CB9E8A /* Api0.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Api0.swift; sourceTree = ""; }; D01A47521F4DBEB100383CC1 /* libHockeySDK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libHockeySDK.a; path = "../../build/HockeySDK-iOS/Support/build/Debug-iphoneos/libHockeySDK.a"; sourceTree = ""; }; D01A47541F4DBED700383CC1 /* HockeySDK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = HockeySDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D021D4D7219CAEDD0064BEBA /* Telegram-iOS-Fork.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Telegram-iOS-Fork.entitlements"; sourceTree = ""; }; @@ -2198,6 +2202,7 @@ D0400EE31D5B912E007931CE /* NotificationService */ = { isa = PBXGroup; children = ( + D015E01E225CDF5000CB9E8A /* Api0.swift */, D000CAC221FB6E170011B15D /* NotificationService-AppStore.entitlements */, D000CAC321FB6E170011B15D /* NotificationService-AppStoreLLC.entitlements */, D000CAC121FB6E160011B15D /* NotificationService-Fork.entitlements */, @@ -2212,6 +2217,7 @@ D073E51F21FF7CE900742DDD /* Crypto.m */, D073E52122003E1E00742DDD /* Data.swift */, D0E2CE632227F0680084E3DD /* ManagedFile.swift */, + D015E010225CCEB300CB9E8A /* ReadBuffer.swift */, ); path = NotificationService; sourceTree = ""; @@ -3243,10 +3249,12 @@ D0ED633B21FF3EFD001D4648 /* BuildConfig.m in Sources */, D09B79C62219C784003B1F9D /* SharedAccountInfo.swift in Sources */, D0ED633D21FF4580001D4648 /* NotificationService.swift in Sources */, + D015E01F225CDF5100CB9E8A /* Api0.swift in Sources */, D0ED633A21FF3EDF001D4648 /* AccountData.swift in Sources */, D0ED634121FF4786001D4648 /* Serialization.swift in Sources */, D073E52021FF7CE900742DDD /* Crypto.m in Sources */, D0ED633F21FF46E4001D4648 /* ImageData.swift in Sources */, + D015E011225CCEB300CB9E8A /* ReadBuffer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Telegram-iOS/en.lproj/Localizable.strings b/Telegram-iOS/en.lproj/Localizable.strings index a98b555946..7cc4293fea 100644 --- a/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram-iOS/en.lproj/Localizable.strings @@ -4218,3 +4218,6 @@ Unused sets are archived when you add more."; "Call.Speaker" = "speaker"; "MemberSearch.BotSection" = "BOTS"; + +"Conversation.PrivateMessageLinkCopied" = "This link will only work for members of this chat."; +"Conversation.ErrorInaccessibleMessage" = "Unfortunately, you can't access this message. You are not a member of the chat where it was posted."; diff --git a/submodules/LegacyComponents b/submodules/LegacyComponents index 4f73ace1d8..596995e181 160000 --- a/submodules/LegacyComponents +++ b/submodules/LegacyComponents @@ -1 +1 @@ -Subproject commit 4f73ace1d8b171e142c01df029488d646b27aad0 +Subproject commit 596995e1811e5202d627ec27e92c298512ba745e diff --git a/submodules/MtProtoKit b/submodules/MtProtoKit index 579d7f6264..e0d4ca79dd 160000 --- a/submodules/MtProtoKit +++ b/submodules/MtProtoKit @@ -1 +1 @@ -Subproject commit 579d7f626427d0cc82209c1b2fc3be7b97eac68c +Subproject commit e0d4ca79dd2545c83577e423143dec0663c879f4 diff --git a/submodules/TelegramCore b/submodules/TelegramCore index ab41021ba7..b91bad40fe 160000 --- a/submodules/TelegramCore +++ b/submodules/TelegramCore @@ -1 +1 @@ -Subproject commit ab41021ba7e7d73e248b30cd5b4277720311fce7 +Subproject commit b91bad40fefafa57313d661c6545d12cf4069176 diff --git a/submodules/TelegramUI b/submodules/TelegramUI index 9db1562966..f11b2bc350 160000 --- a/submodules/TelegramUI +++ b/submodules/TelegramUI @@ -1 +1 @@ -Subproject commit 9db156296688ff0fb5afdf67aa5b3bf6c72baecb +Subproject commit f11b2bc350bd4117d25fd68481bb5ef9231e7115