From d0d8f7dcd47006e641c2d230e5b0c5155a09466f Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Tue, 29 Jan 2019 14:04:17 +0400 Subject: [PATCH] Support for updated accounts API Implemented Notification Service Extension --- .../NotificationViewController.swift | 105 +++-- NotificationService/AccountData.swift | 118 +++++ NotificationService/Crypto.h | 7 + NotificationService/Crypto.m | 73 +++ NotificationService/Data.swift | 31 ++ NotificationService/ImageData.swift | 421 ++++++++++++++++++ NotificationService/Info.plist | 4 +- .../NotificationService-Bridging-Header.h | 7 + NotificationService/NotificationService.swift | 262 ++++++++++- NotificationService/Serialization.swift | 42 ++ Share/ShareRootController.swift | 20 +- SiriIntents/IntentHandler.swift | 48 +- Telegram-iOS.xcodeproj/project.pbxproj | 60 ++- .../xcschemes/NotificationContent.xcscheme | 8 +- Telegram-iOS/AppDelegate.swift | 64 +-- Telegram-iOS/ApplicationContext.swift | 92 ++-- Telegram-iOS/LegacyDataImport.swift | 10 +- Telegram-iOS/LegacyPreferencesImport.swift | 265 +++++------ Telegram-iOS/NotificationManager.swift | 4 +- Telegram-iOS/WakeupManager.swift | 17 +- Telegram-iOS/WatchCommunicationManager.swift | 7 +- Widget/TodayViewController.swift | 18 +- submodules/Display | 2 +- submodules/MtProtoKit | 2 +- submodules/Postbox | 2 +- submodules/TelegramCore | 2 +- submodules/TelegramUI | 2 +- 27 files changed, 1342 insertions(+), 351 deletions(-) create mode 100644 NotificationService/AccountData.swift create mode 100644 NotificationService/Crypto.h create mode 100644 NotificationService/Crypto.m create mode 100644 NotificationService/Data.swift create mode 100644 NotificationService/ImageData.swift create mode 100644 NotificationService/NotificationService-Bridging-Header.h create mode 100644 NotificationService/Serialization.swift diff --git a/NotificationContent/NotificationViewController.swift b/NotificationContent/NotificationViewController.swift index ca8eba750f..728dd477f3 100644 --- a/NotificationContent/NotificationViewController.swift +++ b/NotificationContent/NotificationViewController.swift @@ -11,7 +11,7 @@ private enum NotificationContentAuthorizationError { case unauthorized } -private var accountCache: (Account, AccountManager)? +private var sharedAccountContext: SharedAccountContext? private var installedSharedLogger = false @@ -22,9 +22,27 @@ private func setupSharedLogger(_ path: String) { } } +private func parseFileLocationResource(_ dict: [AnyHashable: Any]) -> TelegramMediaResource? { + guard let datacenterId = dict["datacenterId"] as? Int32 else { + return nil + } + guard let volumeId = dict["volumeId"] as? Int64 else { + return nil + } + guard let localId = dict["localId"] as? Int32 else { + return nil + } + guard let secret = dict["secret"] as? Int64 else { + return nil + } + var fileReference: Data? + if let fileReferenceString = dict["fileReference"] as? String { + fileReference = dataWithHexString(fileReferenceString) + } + return CloudFileMediaResource(datacenterId: Int(datacenterId), volumeId: volumeId, localId: localId, secret: secret, size: nil, fileReference: fileReference) +} + class NotificationViewController: UIViewController, UNNotificationContentExtension { - private let accountPromise = Promise() - private let imageNode = TransformImageNode() private var imageDimensions: CGSize? @@ -66,47 +84,37 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi setupSharedLogger(logsPath) - let account: Signal<(Account, AccountManager), NotificationContentAuthorizationError> - if let accountCache = accountCache { - account = .single(accountCache) - } else { + if sharedAccountContext == nil { + initializeAccountManagement() + let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata") + + let applicationBindings = TelegramApplicationBindings(isMainApp: false, containerPath: appGroupUrl.path, appSpecificScheme: BuildConfig.shared().appSpecificUrlScheme, openUrl: { _ in + }, openUniversalUrl: { _, completion in + completion.completion(false) + return + }, canOpenUrl: { _ in + return false + }, getTopWindow: { + return nil + }, displayNotification: { _ in + + }, applicationInForeground: .single(false), applicationIsActive: .single(false), clearMessageNotifications: { _ in + }, pushIdleTimerExtension: { + return EmptyDisposable + }, openSettings: {}, openAppStorePage: {}, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: { + return nil + }, presentNativeController: { _ in + }, dismissNativeController: { + }) + let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown" - initializeAccountManagement() - account = accountManager(basePath: rootPath + "/accounts-metadata") - |> take(1) - |> introduceError(NotificationContentAuthorizationError.self) - |> mapToSignal { accountManager -> Signal<(Account, AccountManager), NotificationContentAuthorizationError> in - return currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: telegramAccountAuxiliaryMethods) - |> introduceError(NotificationContentAuthorizationError.self) - |> mapToSignal { account -> Signal<(Account, AccountManager), NotificationContentAuthorizationError> in - if let account = account { - switch account { - case .upgrading: - return .complete() - case let .authorized(account): - setupAccount(account) - accountCache = (account, accountManager) - return .single((account, accountManager)) - case .unauthorized: - return .fail(.unauthorized) - } - } else { - return .complete() - } - } - } - |> take(1) + sharedAccountContext = SharedAccountContext(accountManager: accountManager, applicationBindings: applicationBindings, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never()) } - self.accountPromise.set(account - |> map { $0.0 } - |> `catch` { _ -> Signal in - return .complete() - }) } func didReceive(_ notification: UNNotification) { - if 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] { + 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] { let messageId = MessageId(peerId: PeerId(peerIdValue), namespace: messageIdNamespace, id: messageIdId) if let imageInfo = dict["image"] as? [String: Any] { @@ -139,19 +147,36 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi |> map { $0.1 }) } - self.applyDisposable.set((self.accountPromise.get() + guard let sharedAccountContext = sharedAccountContext else { + return + } + + self.applyDisposable.set((sharedAccountContext.activeAccounts + |> map { _, accounts, _ -> Account? in + return accounts[AccountRecordId(rawValue: accountIdValue)] + } + |> filter { account in + return account != nil + } |> take(1) |> mapToSignal { account -> Signal<(Account, ImageMediaReference?), NoError> in + guard let account = account else { + return .complete() + } return account.postbox.messageAtId(messageId) |> take(1) |> map { message in var imageReference: ImageMediaReference? - if let message = message { + if let message = message, false { for media in message.media { if let image = media as? TelegramMediaImage { imageReference = .message(message: MessageReference(message), media: image) } } + } 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)) + } } return (account, imageReference) } diff --git a/NotificationService/AccountData.swift b/NotificationService/AccountData.swift new file mode 100644 index 0000000000..3f5ee72d42 --- /dev/null +++ b/NotificationService/AccountData.swift @@ -0,0 +1,118 @@ +import Foundation +import CommonCrypto + +struct MasterNotificationKey: Codable { + let id: Data + let data: Data +} + +struct AccountDatacenterKey: Codable { + let id: Int64 + let data: Data +} + +struct AccountDatacenterInfo: Codable { + let masterKey: AccountDatacenterKey +} + +struct StoredAccountInfo: Codable { + let primaryId: Int32 + let isTestingEnvironment: Bool + let datacenters: [Int32: AccountDatacenterInfo] +} + +struct AccountData { + let id: Int64 + let isTestingEnvironment: Bool + let basePath: String + let datacenterId: Int32 + let datacenters: [Int32: AccountDatacenterInfo] + let notificationKey: MasterNotificationKey? +} + +func loadAccountsData(rootPath: String) -> [Int64: AccountData] { + var result: [Int64: AccountData] = [:] + if let contents = try? FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: rootPath), includingPropertiesForKeys: nil, options: [.skipsSubdirectoryDescendants]) { + for url in contents { + let directoryName = url.lastPathComponent + if directoryName.hasPrefix("account-"), let id = UInt64(directoryName[directoryName.index(directoryName.startIndex, offsetBy: "account-".count)...]) { + var notificationKey: MasterNotificationKey? + if let data = try? Data(contentsOf: URL(fileURLWithPath: url.path + "/notificationsKey")), let value = try? JSONDecoder().decode(MasterNotificationKey.self, from: data) { + notificationKey = value + } + var storedInfo: StoredAccountInfo? + if let data = try? Data(contentsOf: URL(fileURLWithPath: url.path + "/storedInfo")), let value = try? JSONDecoder().decode(StoredAccountInfo.self, from: data) { + storedInfo = value + } + if let storedInfo = storedInfo { + result[Int64(bitPattern: id)] = AccountData(id: Int64(bitPattern: id), isTestingEnvironment: storedInfo.isTestingEnvironment, basePath: url.path, datacenterId: storedInfo.primaryId, datacenters: storedInfo.datacenters, notificationKey: notificationKey) + } + } + } + } + return result +} + +private func sha256Digest(_ data: Data) -> Data { + let length = data.count + return data.withUnsafeBytes { (bytes: UnsafePointer) -> Data in + var result = Data(count: Int(CC_SHA256_DIGEST_LENGTH)) + result.withUnsafeMutableBytes { (destBytes: UnsafeMutablePointer) -> Void in + CC_SHA256(bytes, UInt32(length), destBytes) + } + return result + } +} + +func decryptedNotificationPayload(accounts: [Int64: AccountData], data: Data) -> (AccountData, [AnyHashable: Any])? { + if data.count < 8 + 16 { + return nil + } + + for (_, account) in accounts { + guard let notificationKey = account.notificationKey else { + continue + } + if data.subdata(in: 0 ..< 8) != notificationKey.id { + continue + } + + let x = 8 + let msgKey = data.subdata(in: 8 ..< (8 + 16)) + let rawData = data.subdata(in: (8 + 16) ..< data.count) + let sha256_a = sha256Digest(msgKey + notificationKey.data.subdata(in: x ..< (x + 36))) + let sha256_b = sha256Digest(notificationKey.data.subdata(in: (40 + x) ..< (40 + x + 36)) + msgKey) + let aesKey = sha256_a.subdata(in: 0 ..< 8) + sha256_b.subdata(in: 8 ..< (8 + 16)) + sha256_a.subdata(in: 24 ..< (24 + 8)) + let aesIv = sha256_b.subdata(in: 0 ..< 8) + sha256_a.subdata(in: 8 ..< (8 + 16)) + sha256_b.subdata(in: 24 ..< (24 + 8)) + + guard let data = MTAesDecrypt(rawData, aesKey, aesIv), data.count > 4 else { + return nil + } + + var dataLength: Int32 = 0 + data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + memcpy(&dataLength, bytes, 4) + } + + if dataLength < 0 || dataLength > data.count - 4 { + return nil + } + + let checkMsgKeyLarge = sha256Digest(notificationKey.data.subdata(in: (88 + x) ..< (88 + x + 32)) + data) + let checkMsgKey = checkMsgKeyLarge.subdata(in: 8 ..< (8 + 16)) + + if checkMsgKey != msgKey { + return nil + } + + let contentData = data.subdata(in: 4 ..< (4 + Int(dataLength))) + guard let result = try? JSONSerialization.jsonObject(with: contentData, options: []) else { + return nil + } + guard let dict = result as? [AnyHashable: Any] else { + return nil + } + return (account, dict) + } + return nil +} diff --git a/NotificationService/Crypto.h b/NotificationService/Crypto.h new file mode 100644 index 0000000000..02e13c6c8e --- /dev/null +++ b/NotificationService/Crypto.h @@ -0,0 +1,7 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +NSData *MTAesDecrypt(NSData *data, NSData *key, NSData *iv); + +NS_ASSUME_NONNULL_END diff --git a/NotificationService/Crypto.m b/NotificationService/Crypto.m new file mode 100644 index 0000000000..d5a8f0a0b4 --- /dev/null +++ b/NotificationService/Crypto.m @@ -0,0 +1,73 @@ +#import "Crypto.h" +#import + +#define AES_BLOCK_SIZE 16 + +#define N_WORDS (AES_BLOCK_SIZE / sizeof(unsigned long)) +typedef struct { + unsigned long data[N_WORDS]; +} aes_block_t; + +static void MyAesIgeDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv) { + unsigned char aesIv[AES_BLOCK_SIZE]; + memcpy(aesIv, iv, AES_BLOCK_SIZE); + unsigned char ccIv[AES_BLOCK_SIZE]; + memcpy(ccIv, iv + AES_BLOCK_SIZE, AES_BLOCK_SIZE); + + assert(((size_t)inBytes | (size_t)outBytes | (size_t)aesIv | (size_t)ccIv) % sizeof(long) == + 0); + + CCCryptorRef decryptor = NULL; + CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES128, kCCOptionECBMode, key, keyLength, nil, &decryptor); + if (decryptor != NULL) { + int len; + size_t n; + + len = length / AES_BLOCK_SIZE; + + aes_block_t *ivp = (aes_block_t *)(aesIv); + aes_block_t *iv2p = (aes_block_t *)(ccIv); + + while (len) { + aes_block_t tmp; + aes_block_t *inp = (aes_block_t *)inBytes; + aes_block_t *outp = (aes_block_t *)outBytes; + + for (n = 0; n < N_WORDS; ++n) + tmp.data[n] = inp->data[n] ^ iv2p->data[n]; + + size_t dataOutMoved = 0; + CCCryptorStatus result = CCCryptorUpdate(decryptor, &tmp, AES_BLOCK_SIZE, outBytes, AES_BLOCK_SIZE, &dataOutMoved); + assert(result == kCCSuccess); + assert(dataOutMoved == AES_BLOCK_SIZE); + + for (n = 0; n < N_WORDS; ++n) + outp->data[n] ^= ivp->data[n]; + + ivp = inp; + iv2p = outp; + + inBytes += AES_BLOCK_SIZE; + outBytes += AES_BLOCK_SIZE; + + --len; + } + + memcpy(iv, ivp->data, AES_BLOCK_SIZE); + memcpy(iv + AES_BLOCK_SIZE, iv2p->data, AES_BLOCK_SIZE); + + CCCryptorRelease(decryptor); + } +} + +NSData *MTAesDecrypt(NSData *data, NSData *key, NSData *iv) { + assert(key != nil && iv != nil); + + NSMutableData *resultData = [[NSMutableData alloc] initWithLength:data.length]; + + unsigned char aesIv[16 * 2]; + memcpy(aesIv, iv.bytes, iv.length); + MyAesIgeDecrypt(data.bytes, (int)data.length, resultData.mutableBytes, key.bytes, (int)key.length, aesIv); + + return resultData; +} diff --git a/NotificationService/Data.swift b/NotificationService/Data.swift new file mode 100644 index 0000000000..86dba1c072 --- /dev/null +++ b/NotificationService/Data.swift @@ -0,0 +1,31 @@ +import Foundation + +enum Namespaces { + struct Peer { + static let CloudUser: PeerId.Namespace = 0 + static let CloudGroup: PeerId.Namespace = 1 + static let CloudChannel: PeerId.Namespace = 2 + } +} + +struct PeerId { + typealias Namespace = Int32 + typealias Id = Int32 + + public let namespace: Namespace + public let id: Id + + public init(namespace: Namespace, id: Id) { + self.namespace = namespace + self.id = id + } + + public init(_ n: Int64) { + self.namespace = Int32((n >> 32) & 0x7fffffff) + self.id = Int32(bitPattern: UInt32(n & 0xffffffff)) + } + + public func toInt64() -> Int64 { + return (Int64(self.namespace) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: self.id))) + } +} diff --git a/NotificationService/ImageData.swift b/NotificationService/ImageData.swift new file mode 100644 index 0000000000..7b55736938 --- /dev/null +++ b/NotificationService/ImageData.swift @@ -0,0 +1,421 @@ +import Foundation +import MtProtoKitDynamic + +struct ImageResource { + let datacenterId: Int + let volumeId: Int64 + let localId: Int32 + let secret: Int64 + let fileReference: Data? + + var resourceId: String { + return "telegram-cloud-file-\(self.datacenterId)-\(self.volumeId)-\(self.localId)-\(self.secret)" + } +} + +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] = [:] + + func setObject(_ object: Any!, forKey aKey: String!, group: String!) { + let data = NSKeyedArchiver.archivedData(withRootObject: object) + self.dict[group + ":" + aKey] = data + } + + func object(forKey aKey: String!, group: String!) -> Any! { + if let data = self.dict[group + ":" + aKey] { + return NSKeyedUnarchiver.unarchiveObject(with: data as Data) + } + return nil + } + + func removeObject(forKey aKey: String!, group: String!) { + self.dict.removeValue(forKey: group + ":" + aKey) + } + + func dropGroup(_ group: String!) { + } +} + +private final class ParsedFile: NSObject { + let data: Data? + + init(data: Data?) { + self.data = data + + super.init() + } +} + +func fetchImageWithAccount(account: AccountData, resource: ImageResource, completion: @escaping (Data?) -> Void) -> () -> Void { + MTLogSetEnabled(true) + MTLogSetLoggingFunction({ str, args in + //let string = NSString(format: str! as NSString, args!) + print("MT: \(str!)") + }) + + let serialization = Serialization() + + var apiEnvironment = MTApiEnvironment() + + apiEnvironment.apiId = BuildConfig.shared().apiId + apiEnvironment.langPack = "ios" + apiEnvironment.layer = NSNumber(value: Int(serialization.currentLayer())) + apiEnvironment.disableUpdates = true + apiEnvironment = apiEnvironment.withUpdatedLangPackCode("en") + + let context = MTContext(serialization: serialization, apiEnvironment: apiEnvironment, isTestingEnvironment: account.isTestingEnvironment, useTempAuthKeys: false)! + + let seedAddressList: [Int: [String]] + + if account.isTestingEnvironment { + seedAddressList = [ + 1: ["149.154.175.10"], + 2: ["149.154.167.40"] + ] + } else { + seedAddressList = [ + 1: ["149.154.175.50", "2001:b28:f23d:f001::a"], + 2: ["149.154.167.50", "2001:67c:4e8:f002::a"], + 3: ["149.154.175.100", "2001:b28:f23d:f003::a"], + 4: ["149.154.167.91", "2001:67c:4e8:f004::a"], + 5: ["149.154.171.5", "2001:b28:f23f:f005::a"] + ] + } + + for (id, ips) in seedAddressList { + context.setSeedAddressSetForDatacenterWithId(id, seedAddressSet: MTDatacenterAddressSet(addressList: ips.map { MTDatacenterAddress(ip: $0, port: 443, preferForMedia: false, restrictToTcp: false, cdn: false, preferForProxy: false, secret: nil) })) + } + + let keychain = Keychain() + context.keychain = keychain + + for (id, info) in account.datacenters { + 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)! + mtProto.useTempAuthKeys = context.useTempAuthKeys + mtProto.checkForProxyConnectionIssues = false + + let requestService = MTRequestMessageService(context: context)! + mtProto.add(requestService) + + let request = MTRequest() + + 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) + + buffer.appendInt32(0) + buffer.appendInt32(32 * 1024) + + request.setPayload(buffer.makeData(), metadata: "getFile", responseParser: { response in + let reader = BufferReader(Buffer(data: response)) + guard let signature = reader.readInt32() else { + return ParsedFile(data: nil) + } + guard signature == 157948117 else { + return ParsedFile(data: nil) + } + reader.skip(4) //type + reader.skip(4) //mtime + guard let bytes = parseBytes(reader) else { + return ParsedFile(data: nil) + } + return ParsedFile(data: bytes.makeData()) + }) + + request.dependsOnPasswordEntry = false + request.shouldContinueExecutionWithErrorContext = { errorContext in + guard let _ = errorContext else { + return true + } + return true + } + + request.completed = { (boxedResponse, timestamp, error) -> () in + if let _ = error { + completion(nil) + } else { + if let result = boxedResponse as? ParsedFile { + completion(result.data) + } else { + completion(nil) + } + } + } + + requestService.add(request) + mtProto.resume() + + let internalId = request.internalId + return { + requestService.removeRequest(byInternalId: internalId) + context.performBatchUpdates({}) + mtProto.stop() + } +} diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index 1d4d66d4fd..909f8e4f4c 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 1.0 + 5.2 CFBundleVersion - 1 + ${BUILD_NUMBER} NSExtension NSExtensionPointIdentifier diff --git a/NotificationService/NotificationService-Bridging-Header.h b/NotificationService/NotificationService-Bridging-Header.h new file mode 100644 index 0000000000..0d49aeee92 --- /dev/null +++ b/NotificationService/NotificationService-Bridging-Header.h @@ -0,0 +1,7 @@ +#ifndef NotificationService_BridgingHeader_h +#define NotificationService_BridgingHeader_h + +#import "../Telegram-iOS/BuildConfig.h" +#import "Crypto.h" + +#endif diff --git a/NotificationService/NotificationService.swift b/NotificationService/NotificationService.swift index b31f25c7b1..bca16c1a00 100644 --- a/NotificationService/NotificationService.swift +++ b/NotificationService/NotificationService.swift @@ -1,26 +1,280 @@ +import Foundation import UserNotifications +private func dataWithHexString(_ string: String) -> Data { + var hex = string + if hex.count % 2 != 0 { + return Data() + } + 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 + } +} + +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 { + 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 +} + +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) + } + return result +} + class NotificationService: UNNotificationServiceExtension { + private let rootPath: String? + var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? + var cancelFetch: (() -> Void)? + override init() { + let appBundleIdentifier = Bundle.main.bundleIdentifier! + if let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) { + let appGroupName = "group.\(appBundleIdentifier[.. Void) { + let accountsData = self.rootPath.flatMap({ rootPath in + loadAccountsData(rootPath: rootPath) + }) ?? [:] + self.contentHandler = contentHandler self.bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent - if let bestAttemptContent = self.bestAttemptContent { - bestAttemptContent.title = "\(bestAttemptContent.title) [modified]" - contentHandler(bestAttemptContent) + 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 (account, dict) = encryptedData.flatMap({ decryptedNotificationPayload(accounts: accountsData, data: $0) }) { + var userInfo = self.bestAttemptContent?.userInfo ?? [:] + userInfo["accountId"] = account.id + + var peerId: PeerId? + var messageId: Int32? + + if let msgId = dict["msg_id"] as? String { + userInfo["msg_id"] = msgId + messageId = Int32(msgId) + } + if let fromId = dict["from_id"] as? String { + userInfo["from_id"] = fromId + if let id = Int32(fromId) { + peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: id) + } + } + if let chatId = dict["chat_id"] as? String { + userInfo["chat_id"] = chatId + if let id = Int32(chatId) { + peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: id) + } + } + if let channelId = dict["channel_id"] as? String { + userInfo["channel_id"] = channelId + if let id = Int32(channelId) { + peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: id) + } + } + + 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) + } + + let imagesPath = NSTemporaryDirectory() + "aps-data" + let _ = try? FileManager.default.createDirectory(atPath: imagesPath, withIntermediateDirectories: true, attributes: nil) + + let mediaBoxPath = account.basePath + "/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)" }) + + if let aps = dict["aps"] as? [AnyHashable: Any] { + if let alert = aps["alert"] as? String { + self.bestAttemptContent?.title = "" + self.bestAttemptContent?.body = alert + } else if let alert = aps["alert"] as? [AnyHashable: Any] { + self.bestAttemptContent?.title = alert["title"] as? String ?? "" + self.bestAttemptContent?.subtitle = alert["subtitle"] as? String ?? "" + self.bestAttemptContent?.body = alert["body"] as? String ?? "" + } + + if let threadId = aps["thread-id"] as? String { + self.bestAttemptContent?.threadIdentifier = threadId + } + if let sound = aps["sound"] as? String { + self.bestAttemptContent?.sound = UNNotificationSound(named: UNNotificationSoundName(sound)) + } + 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 { + 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] + + if category == "r" { + self.bestAttemptContent?.categoryIdentifier = "withReplyMedia" + } else if category == "m" { + self.bestAttemptContent?.categoryIdentifier = "withMuteMedia" + } + } + } + } + + self.bestAttemptContent?.userInfo = userInfo + + self.cancelFetch?() + if let mediaBoxThumbnailImagePath = mediaBoxThumbnailImagePath, let tempImagePath = tempImagePath, let thumbnailImage = thumbnailImage { + self.cancelFetch = fetchImageWithAccount(account: account, resource: thumbnailImage, completion: { [weak self] data in + DispatchQueue.main.async { + guard let strongSelf = self else { + return + } + strongSelf.cancelFetch?() + strongSelf.cancelFetch = nil + if let data = data { + let _ = try? data.write(to: URL(fileURLWithPath: mediaBoxThumbnailImagePath)) + if let _ = try? data.write(to: URL(fileURLWithPath: tempImagePath)) { + if let attachment = try? UNNotificationAttachment(identifier: "image", url: URL(fileURLWithPath: tempImagePath)) { + strongSelf.bestAttemptContent?.attachments = [attachment] + } + } + } + if let bestAttemptContent = strongSelf.bestAttemptContent { + contentHandler(bestAttemptContent) + } + } + }) + } else { + if let bestAttemptContent = self.bestAttemptContent { + contentHandler(bestAttemptContent) + } + } + } else { + if let bestAttemptContent = self.bestAttemptContent { + contentHandler(bestAttemptContent) + } } } override func serviceExtensionTimeWillExpire() { - if let contentHandler = self.contentHandler, let bestAttemptContent = self.bestAttemptContent { + self.cancelFetch?() + self.cancelFetch = nil + + if let contentHandler = self.contentHandler, let bestAttemptContent = self.bestAttemptContent { contentHandler(bestAttemptContent) } } } + + diff --git a/NotificationService/Serialization.swift b/NotificationService/Serialization.swift new file mode 100644 index 0000000000..c38751ce23 --- /dev/null +++ b/NotificationService/Serialization.swift @@ -0,0 +1,42 @@ +import Foundation +import MtProtoKitDynamic + +public class BoxedMessage: NSObject { + public let body: Any + public init(_ body: Any) { + self.body = body + } +} + +public class Serialization: NSObject, MTSerialization { + public func currentLayer() -> UInt { + return 93 + } + + public func parseMessage(_ data: Data!) -> Any! { + return nil + } + + public func exportAuthorization(_ datacenterId: Int32, data: AutoreleasingUnsafeMutablePointer) -> MTExportAuthorizationResponseParser! + { + return { data -> MTExportedAuthorizationData? in + return nil + } + } + + public func importAuthorization(_ authId: Int32, bytes: Data!) -> Data! { + return Data() + } + + public func requestDatacenterAddress(with data: AutoreleasingUnsafeMutablePointer) -> MTRequestDatacenterAddressListParser! { + return { response -> MTDatacenterAddressListData? in + return nil + } + } + + public func requestNoop(_ data: AutoreleasingUnsafeMutablePointer!) -> MTRequestNoopParser! { + return { response -> AnyObject? in + return nil + } + } +} diff --git a/Share/ShareRootController.swift b/Share/ShareRootController.swift index c98ac3c643..b5b21cce3e 100644 --- a/Share/ShareRootController.swift +++ b/Share/ShareRootController.swift @@ -143,14 +143,11 @@ class ShareRootController: UIViewController { let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown" initializeAccountManagement() - account = accountManager(basePath: rootPath + "/accounts-metadata") - |> take(1) - |> mapToSignal { accountManager -> Signal<(SharedAccountContext, LoggingSettings), NoError> in - let sharedContext = SharedAccountContext(accountManager: accountManager, applicationBindings: applicationBindings, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never()) + let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata") + let sharedContext = SharedAccountContext(accountManager: accountManager, applicationBindings: applicationBindings, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never()) - return accountManager.transaction { transaction -> (SharedAccountContext, LoggingSettings) in - return (sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings) - } + account = accountManager.transaction { transaction -> (SharedAccountContext, LoggingSettings) in + return (sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings) } |> introduceError(ShareAuthorizationError.self) |> mapToSignal { sharedContext, loggingSettings -> Signal<(SharedAccountContext, Account), ShareAuthorizationError> in @@ -181,19 +178,18 @@ class ShareRootController: UIViewController { |> take(1) } - let preferencesKey: PostboxViewKey = .preferences(keys: Set([ApplicationSpecificPreferencesKeys.presentationPasscodeSettings])) - let shouldBeMaster = self.shouldBeMaster let applicationInterface = account |> mapToSignal { sharedContext, account -> Signal<(AccountContext, PostboxAccessChallengeData), ShareAuthorizationError> in - return combineLatest(currentPresentationDataAndSettings(postbox: account.postbox), account.postbox.combinedView(keys: [.accessChallengeData, preferencesKey]) |> take(1)) + return combineLatest(sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), currentPresentationDataAndSettings(accountManager: sharedContext.accountManager, postbox: account.postbox), sharedContext.accountManager.accessChallengeData()) + |> take(1) |> deliverOnMainQueue |> introduceError(ShareAuthorizationError.self) - |> map { dataAndSettings, data -> (AccountContext, PostboxAccessChallengeData) in + |> map { sharedData, dataAndSettings, data -> (AccountContext, PostboxAccessChallengeData) in accountCache = (sharedContext, account) updateLegacyLocalization(strings: dataAndSettings.presentationData.strings) let context = AccountContext(sharedContext: sharedContext, account: account, initialPresentationDataAndSettings: dataAndSettings) - return (context, (data.views[.accessChallengeData] as! AccessChallengeDataView).data) + return (context, data.data) } } |> deliverOnMainQueue diff --git a/SiriIntents/IntentHandler.swift b/SiriIntents/IntentHandler.swift index 5bf6e62b29..59cb28dfbf 100644 --- a/SiriIntents/IntentHandler.swift +++ b/SiriIntents/IntentHandler.swift @@ -82,32 +82,30 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag account = .single(accountCache) } else { initializeAccountManagement() - account = accountManager(basePath: rootPath + "/accounts-metadata") - |> take(1) - |> mapToSignal { accountManager -> Signal in - return currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods) - |> mapToSignal { account -> Signal in - if let account = account { - switch account { - case .upgrading: - return .complete() - case let .authorized(account): - return applicationSettings(accountManager: accountManager) - |> deliverOnMainQueue - |> map { settings -> Account in - accountCache = account - Logger.shared.logToFile = settings.logging.logToFile - Logger.shared.logToConsole = settings.logging.logToConsole - - Logger.shared.redactSensitiveData = settings.logging.redactSensitiveData - return account - } - case .unauthorized: - return .complete() - } - } else { - return .complete() + let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata") + + account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods) + |> mapToSignal { account -> Signal in + if let account = account { + switch account { + case .upgrading: + return .complete() + case let .authorized(account): + return applicationSettings(accountManager: accountManager) + |> deliverOnMainQueue + |> map { settings -> Account in + accountCache = account + Logger.shared.logToFile = settings.logging.logToFile + Logger.shared.logToConsole = settings.logging.logToConsole + + Logger.shared.redactSensitiveData = settings.logging.redactSensitiveData + return account + } + case .unauthorized: + return .complete() } + } else { + return .complete() } } |> take(1) diff --git a/Telegram-iOS.xcodeproj/project.pbxproj b/Telegram-iOS.xcodeproj/project.pbxproj index 47e842ce6d..37d3913a06 100644 --- a/Telegram-iOS.xcodeproj/project.pbxproj +++ b/Telegram-iOS.xcodeproj/project.pbxproj @@ -213,7 +213,6 @@ 09D304382174344900C00567 /* TGBridgeWebPageMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 09C572F52172953500BDF00F /* TGBridgeWebPageMediaAttachment.m */; }; 09D304392174344900C00567 /* TGBridgeMessageEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = 09C572F42172953500BDF00F /* TGBridgeMessageEntities.m */; }; 09FDAEE62140477F00BF856F /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09FDAEE52140477F00BF856F /* MtProtoKitDynamic.framework */; }; - D000CACB21FB6E380011B15D /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D000CACA21FB6E380011B15D /* NotificationService.swift */; }; D000CACF21FB6E380011B15D /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D000CAC821FB6E370011B15D /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; D001D5AA1F878DA300DF975A /* PhoneCountries.txt in Resources */ = {isa = PBXBuildFile; fileRef = D001D5A91F878DA300DF975A /* PhoneCountries.txt */; }; D006CF9F21A8D11B00FDCD32 /* ModernProto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D006CF9E21A8D11B00FDCD32 /* ModernProto.framework */; }; @@ -300,6 +299,8 @@ D06706611D51185400DED3E3 /* TelegramCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D06706601D51185400DED3E3 /* TelegramCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D06706621D5118F500DED3E3 /* TelegramCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D06706601D51185400DED3E3 /* TelegramCore.framework */; }; D06E4C2F21347D9200088087 /* UIImage+ImageEffects.m in Sources */ = {isa = PBXBuildFile; fileRef = D06E4C2E21347D9200088087 /* UIImage+ImageEffects.m */; }; + D073E52021FF7CE900742DDD /* Crypto.m in Sources */ = {isa = PBXBuildFile; fileRef = D073E51F21FF7CE900742DDD /* Crypto.m */; }; + D073E52222003E1E00742DDD /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D073E52122003E1E00742DDD /* Data.swift */; }; D084023220E1883500065674 /* ApplicationShortcutItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D084023120E1883500065674 /* ApplicationShortcutItem.swift */; }; D08410451FABDC5D008FFE92 /* TGItemProviderSignals.m in Sources */ = {isa = PBXBuildFile; fileRef = D08410441FABDC5C008FFE92 /* TGItemProviderSignals.m */; }; D08410481FABDCF2008FFE92 /* LegacyComponents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D08410491FABDCF2008FFE92 /* LegacyComponents.framework */; }; @@ -419,6 +420,12 @@ D0ECCB7F1FE9C38500609802 /* Telegram_iOS_UITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ECCB7E1FE9C38500609802 /* Telegram_iOS_UITests.swift */; }; D0ECCB8A1FE9C4AC00609802 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ECCB891FE9C4AC00609802 /* SnapshotHelper.swift */; }; D0ECCB8D1FE9CE3F00609802 /* SnapshotChatList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ECCB8C1FE9CE3F00609802 /* SnapshotChatList.swift */; }; + D0ED633821FF3E1E001D4648 /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0CAF2F21D75FFAB0011F558 /* MtProtoKitDynamic.framework */; }; + D0ED633A21FF3EDF001D4648 /* AccountData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED633921FF3EDF001D4648 /* AccountData.swift */; }; + D0ED633B21FF3EFD001D4648 /* BuildConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = D09250011FE52D2A003F693F /* BuildConfig.m */; }; + D0ED633D21FF4580001D4648 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0400EE41D5B912E007931CE /* NotificationService.swift */; }; + D0ED633F21FF46E4001D4648 /* ImageData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED633E21FF46E4001D4648 /* ImageData.swift */; }; + D0ED634121FF4786001D4648 /* Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED634021FF4786001D4648 /* Serialization.swift */; }; D0F575132083B96B00F1C1E1 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F575122083B96B00F1C1E1 /* CloudKit.framework */; }; D0FC1948201D2DA800FEDBB2 /* SFCompactRounded-Semibold.otf in Resources */ = {isa = PBXBuildFile; fileRef = D0FC1947201D2DA700FEDBB2 /* SFCompactRounded-Semibold.otf */; }; /* End PBXBuildFile section */ @@ -899,8 +906,6 @@ D000CAC221FB6E170011B15D /* NotificationService-AppStore.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NotificationService-AppStore.entitlements"; sourceTree = ""; }; D000CAC321FB6E170011B15D /* NotificationService-AppStoreLLC.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NotificationService-AppStoreLLC.entitlements"; sourceTree = ""; }; D000CAC821FB6E370011B15D /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; - D000CACA21FB6E380011B15D /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; - D000CACC21FB6E380011B15D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D001D5A91F878DA300DF975A /* PhoneCountries.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = PhoneCountries.txt; path = "Telegram-iOS/Resources/PhoneCountries.txt"; sourceTree = ""; }; D006CF9E21A8D11B00FDCD32 /* ModernProto.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ModernProto.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D006CFA121A8D12600FDCD32 /* ModernProto.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ModernProto.framework; path = "../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-ffbqcdyqpehxdvcwhyaorlehrrdc/Build/Products/Debug Hockeyapp-iphoneos/ModernProto.framework"; sourceTree = ""; }; @@ -1031,6 +1036,9 @@ D06706601D51185400DED3E3 /* TelegramCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = TelegramCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D06E4C2D21347D9200088087 /* UIImage+ImageEffects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ImageEffects.h"; sourceTree = ""; }; D06E4C2E21347D9200088087 /* UIImage+ImageEffects.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+ImageEffects.m"; sourceTree = ""; }; + D073E51E21FF7CE900742DDD /* Crypto.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Crypto.h; sourceTree = ""; }; + D073E51F21FF7CE900742DDD /* Crypto.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Crypto.m; sourceTree = ""; }; + D073E52122003E1E00742DDD /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; }; D079FD001F06BBD10038FADE /* Telegram-iOS-AppStore.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Telegram-iOS-AppStore.entitlements"; sourceTree = ""; }; D079FD011F06BBD60038FADE /* Config-Hockeyapp.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Config-Hockeyapp.xcconfig"; sourceTree = ""; }; D079FD021F06BBD60038FADE /* Config-AppStore.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Config-AppStore.xcconfig"; sourceTree = ""; }; @@ -1166,6 +1174,10 @@ D0ECCB801FE9C38500609802 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D0ECCB891FE9C4AC00609802 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = ""; }; D0ECCB8C1FE9CE3F00609802 /* SnapshotChatList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotChatList.swift; sourceTree = ""; }; + D0ED633921FF3EDF001D4648 /* AccountData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountData.swift; sourceTree = ""; }; + D0ED633C21FF3F28001D4648 /* NotificationService-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NotificationService-Bridging-Header.h"; sourceTree = ""; }; + D0ED633E21FF46E4001D4648 /* ImageData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageData.swift; sourceTree = ""; }; + D0ED634021FF4786001D4648 /* Serialization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Serialization.swift; sourceTree = ""; }; D0F575122083B96B00F1C1E1 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; }; D0FC1947201D2DA700FEDBB2 /* SFCompactRounded-Semibold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SFCompactRounded-Semibold.otf"; path = "Telegram-iOS/Resources/SFCompactRounded-Semibold.otf"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1185,6 +1197,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D0ED633821FF3E1E001D4648 /* MtProtoKitDynamic.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1899,15 +1912,6 @@ name = Media; sourceTree = ""; }; - D000CAC921FB6E380011B15D /* NotificationService */ = { - isa = PBXGroup; - children = ( - D000CACA21FB6E380011B15D /* NotificationService.swift */, - D000CACC21FB6E380011B15D /* Info.plist */, - ); - path = NotificationService; - sourceTree = ""; - }; D00859931B28189D00EAF753 = { isa = PBXGroup; children = ( @@ -1919,7 +1923,6 @@ D0D268821D79A70A00C422DA /* SiriIntentsUI */, D02CF5FF215D9ABF00E0F56A /* NotificationContent */, 09C56F8C2172797200BDF00F /* Watch */, - D000CAC921FB6E380011B15D /* NotificationService */, D00859C21B281E0000EAF753 /* Frameworks */, D008599E1B28189D00EAF753 /* Telegram-iOS */, D0400EE31D5B912E007931CE /* NotificationService */, @@ -2151,7 +2154,14 @@ D000CAC121FB6E160011B15D /* NotificationService-Fork.entitlements */, D000CAC021FB6E160011B15D /* NotificationService-HockeyApp.entitlements */, D0400EE41D5B912E007931CE /* NotificationService.swift */, + D0ED633921FF3EDF001D4648 /* AccountData.swift */, + D0ED633E21FF46E4001D4648 /* ImageData.swift */, D0400EE61D5B912E007931CE /* Info.plist */, + D0ED633C21FF3F28001D4648 /* NotificationService-Bridging-Header.h */, + D0ED634021FF4786001D4648 /* Serialization.swift */, + D073E51E21FF7CE900742DDD /* Crypto.h */, + D073E51F21FF7CE900742DDD /* Crypto.m */, + D073E52122003E1E00742DDD /* Data.swift */, ); path = NotificationService; sourceTree = ""; @@ -3172,7 +3182,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D000CACB21FB6E380011B15D /* NotificationService.swift in Sources */, + D073E52222003E1E00742DDD /* Data.swift in Sources */, + D0ED633B21FF3EFD001D4648 /* BuildConfig.m in Sources */, + D0ED633D21FF4580001D4648 /* NotificationService.swift in Sources */, + D0ED633A21FF3EDF001D4648 /* AccountData.swift in Sources */, + D0ED634121FF4786001D4648 /* Serialization.swift in Sources */, + D073E52021FF7CE900742DDD /* Crypto.m in Sources */, + D0ED633F21FF46E4001D4648 /* ImageData.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4702,6 +4718,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + BUILD_NUMBER = 99999; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -4765,6 +4782,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "NotificationService/NotificationService-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; @@ -4775,6 +4793,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + BUILD_NUMBER = 99999; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -4838,6 +4857,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "NotificationService/NotificationService-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; @@ -4848,6 +4868,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + BUILD_NUMBER = 99999; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -4911,6 +4932,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "NotificationService/NotificationService-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; @@ -4921,6 +4943,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + BUILD_NUMBER = 99999; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -4984,6 +5007,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "NotificationService/NotificationService-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; @@ -4994,6 +5018,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + BUILD_NUMBER = 99999; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -5049,6 +5074,7 @@ PROVISIONING_PROFILE_SPECIFIER = "match AppStore org.telegram.TelegramHD.NotificationService"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "NotificationService/NotificationService-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; @@ -5060,6 +5086,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + BUILD_NUMBER = 99999; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -5115,6 +5142,7 @@ PROVISIONING_PROFILE_SPECIFIER = "match AppStore ph.telegra.Telegraph.NotificationService"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "NotificationService/NotificationService-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; @@ -5126,6 +5154,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + BUILD_NUMBER = 99999; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -5181,6 +5210,7 @@ PROVISIONING_PROFILE_SPECIFIER = "match InHouse org.telegram.Telegram-iOS.NotificationService"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "NotificationService/NotificationService-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; @@ -5192,6 +5222,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + BUILD_NUMBER = 99999; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -5247,6 +5278,7 @@ PROVISIONING_PROFILE_SPECIFIER = "match InHouse org.telegram.Telegram-iOS.NotificationService"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "NotificationService/NotificationService-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/Telegram-iOS.xcodeproj/xcshareddata/xcschemes/NotificationContent.xcscheme b/Telegram-iOS.xcodeproj/xcshareddata/xcschemes/NotificationContent.xcscheme index 58e8da4c9c..00951b8760 100644 --- a/Telegram-iOS.xcodeproj/xcshareddata/xcschemes/NotificationContent.xcscheme +++ b/Telegram-iOS.xcodeproj/xcshareddata/xcschemes/NotificationContent.xcscheme @@ -57,7 +57,7 @@ + runnableDebuggingMode = "1" + BundleIdentifier = "com.apple.mobilesafari" + RemotePath = "/Applications/MobileSafari.app"> deliverOnMainQueue - |> mapToSignal { accountManager -> Signal<(SharedAccountContext, LoggingSettings), NoError> in - let sharedContext = SharedAccountContext(accountManager: accountManager, applicationBindings: applicationBindings, networkArguments: networkArguments, rootPath: rootPath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init)) - return accountManager.transaction { transaction -> (SharedAccountContext, LoggingSettings) in - return (sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings) - } + let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata") + let sharedContext = SharedAccountContext(accountManager: accountManager, applicationBindings: applicationBindings, networkArguments: networkArguments, rootPath: rootPath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init)) + + self.sharedContextPromise.set( + accountManager.transaction { transaction -> (SharedAccountContext, LoggingSettings) in + return (sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings) } |> mapToSignal { sharedContext, loggingSettings -> Signal in Logger.shared.logToFile = loggingSettings.logToFile @@ -686,12 +685,12 @@ private enum QueuedWakeup: Int32 { }) |> mapToSignal { account, hasOther -> Signal<(AnyObject, InitialPresentationDataAndSettings, Bool)?, NoError> in if let account = account as? Account { - return currentPresentationDataAndSettings(postbox: account.postbox) + return currentPresentationDataAndSettings(accountManager: sharedContext.accountManager, postbox: account.postbox) |> map { initialSettings in return (account, initialSettings, hasOther) } } else if let account = account as? UnauthorizedAccount { - return currentPresentationDataAndSettings(postbox: account.postbox) + return currentPresentationDataAndSettings(accountManager: sharedContext.accountManager, postbox: account.postbox) |> map { initialSettings in return (account, initialSettings, hasOther) } @@ -1314,14 +1313,6 @@ private enum QueuedWakeup: Int32 { private func openUrl(url: URL) { let _ = (self.context.get() |> flatMap { $0 } - |> filter { context in - switch context { - case .authorized, .unauthorized: - return true - default: - return false - } - } |> take(1) |> deliverOnMainQueue).start(next: { contextValue in switch contextValue { @@ -1331,7 +1322,7 @@ private enum QueuedWakeup: Int32 { if let proxyData = parseProxyUrl(url) { context.rootController.view.endEditing(true) let strings = context.strings - let controller = ProxyServerActionSheetController(theme: defaultPresentationTheme, strings: strings, postbox: context.account.postbox, network: context.account.network, server: proxyData, presentationData: nil) + let controller = ProxyServerActionSheetController(theme: defaultPresentationTheme, strings: strings, accountManager: context.sharedContext.accountManager, postbox: context.account.postbox, network: context.account.network, server: proxyData, presentationData: nil) context.rootController.currentWindow?.present(controller, on: PresentationSurfaceLevel.root, blockInteraction: false, completion: {}) } else if let secureIdData = parseSecureIdUrl(url) { let strings = context.strings @@ -1401,16 +1392,30 @@ private enum QueuedWakeup: Int32 { case .camera: context.openRootCamera() case .savedMessages: - self.openChatWhenReady(peerId: context.context.account.peerId) + self.openChatWhenReady(accountId: nil, peerId: context.context.account.peerId) } } } }) } - private func openChatWhenReady(peerId: PeerId, messageId: MessageId? = nil) { - self.openChatWhenReadyDisposable.set((self.authorizedContext() + private func openChatWhenReady(accountId: AccountRecordId?, peerId: PeerId, messageId: MessageId? = nil) { + let signal = self.sharedContextPromise.get() |> take(1) + |> mapToSignal { sharedContext -> Signal in + if let accountId = accountId { + sharedContext.switchToAccount(id: accountId) + return self.authorizedContext() + |> filter { context in + context.context.account.id == accountId + } + |> take(1) + } else { + return self.authorizedContext() + |> take(1) + } + } + self.openChatWhenReadyDisposable.set((signal |> deliverOnMainQueue).start(next: { context in context.openChatWithPeerId(peerId: peerId, messageId: messageId) })) @@ -1434,7 +1439,7 @@ private enum QueuedWakeup: Int32 { if response.notification.request.content.categoryIdentifier == "watch" { messageId = messageIdFromNotification(peerId: peerId, notification: response.notification) } - self.openChatWhenReady(peerId: peerId, messageId: messageId) + self.openChatWhenReady(accountId: accountIdFromNotification(response.notification), peerId: peerId, messageId: messageId) } completionHandler() } else if response.actionIdentifier == "reply", let peerId = peerIdFromNotification(response.notification) { @@ -1507,8 +1512,8 @@ private enum QueuedWakeup: Int32 { private func registerForNotifications(context: AccountContext, authorize: Bool = true, completion: @escaping (Bool) -> Void = { _ in }) { let presentationData = context.currentPresentationData.with { $0 } - let _ = (context.account.postbox.transaction { transaction -> Bool in - let settings = transaction.getPreferencesEntry(key: ApplicationSpecificPreferencesKeys.inAppNotificationSettings) as? InAppNotificationSettings ?? InAppNotificationSettings.defaultSettings + let _ = (context.sharedContext.accountManager.transaction { transaction -> Bool in + let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.inAppNotificationSettings) as? InAppNotificationSettings ?? InAppNotificationSettings.defaultSettings return settings.displayNameOnLockscreen } |> deliverOnMainQueue).start(next: { displayNames in @@ -1594,7 +1599,7 @@ private enum QueuedWakeup: Int32 { let queuedMutePolling = self.queuedMutePolling self.queuedMutePolling = false - let _ = (context.context.account.postbox.transaction(ignoreDisabled: true, { transaction -> PostboxAccessChallengeData in + let _ = (context.context.sharedContext.accountManager.transaction(ignoreDisabled: true, { transaction -> PostboxAccessChallengeData in return transaction.getAccessChallengeData() }) |> deliverOnMainQueue).start(next: { accessChallengeData in @@ -1726,6 +1731,15 @@ private enum QueuedWakeup: Int32 { } } +@available(iOS 10.0, *) +private func accountIdFromNotification(_ notification: UNNotification) -> AccountRecordId? { + if let id = notification.request.content.userInfo["accountId"] as? Int64 { + return AccountRecordId(rawValue: id) + } else { + return nil + } +} + @available(iOS 10.0, *) private func peerIdFromNotification(_ notification: UNNotification) -> PeerId? { if let peerId = notification.request.content.userInfo["peerId"] as? Int64 { diff --git a/Telegram-iOS/ApplicationContext.swift b/Telegram-iOS/ApplicationContext.swift index f599a2e68a..e8cf63aecd 100644 --- a/Telegram-iOS/ApplicationContext.swift +++ b/Telegram-iOS/ApplicationContext.swift @@ -7,34 +7,6 @@ import TelegramCore import Display import LegacyComponents -/*func applicationContext(networkArguments: NetworkInitializationArguments, applicationBindings: TelegramApplicationBindings, replyFromNotificationsActive: Signal, backgroundAudioActive: Signal, watchManagerArguments: Signal, sharedContext: SharedAccountContext, accountManager: AccountManager, rootPath: String, legacyBasePath: String, mainWindow: Window1, reinitializedNotificationSettings: @escaping () -> Void) -> Signal { - return currentAccount(allocateIfNotExists: true, networkArguments: networkArguments, supplementary: false, manager: accountManager, rootPath: rootPath, auxiliaryMethods: telegramAccountAuxiliaryMethods) - |> filter { $0 != nil } - |> deliverOnMainQueue - |> mapToSignal { account -> Signal in - if let account = account { - switch account { - case .upgrading: - preconditionFailure() - case let .unauthorized(account): - return currentPresentationDataAndSettings(postbox: account.postbox) - |> deliverOnMainQueue - |> map { dataAndSettings -> ApplicationContext? in - return .unauthorized(UnauthorizedApplicationContext(account: account, applicationBindings: applicationBindings)) - } - case let .authorized(account): - return currentPresentationDataAndSettings(postbox: account.postbox) - |> deliverOnMainQueue - |> map { dataAndSettings -> ApplicationContext? in - return .authorized(AuthorizedApplicationContext(mainWindow: mainWindow, replyFromNotificationsActive: replyFromNotificationsActive, backgroundAudioActive: backgroundAudioActive, watchManagerArguments: watchManagerArguments, context: AccountContext(sharedContext: sharedContext, account: account, initialPresentationDataAndSettings: dataAndSettings), accountManager: accountManager, legacyBasePath: legacyBasePath, showCallsTab: dataAndSettings.callListSettings.showTab, reinitializedNotificationSettings: reinitializedNotificationSettings)) - } - } - } else { - return .single(nil) - } - } -}*/ - func isAccessLocked(data: PostboxAccessChallengeData, at timestamp: Int32) -> Bool { if data.isLockable, let autolockDeadline = data.autolockDeadline, autolockDeadline <= timestamp { return true @@ -183,7 +155,7 @@ final class AuthorizedApplicationContext { } var applicationBadge: Signal { - return renderedTotalUnreadCount(postbox: self.context.account.postbox) + return renderedTotalUnreadCount(accountManager: self.context.sharedContext.accountManager, postbox: self.context.account.postbox) |> map { $0.0 } @@ -225,10 +197,9 @@ final class AuthorizedApplicationContext { let runningWatchTasksPromise = Promise(nil) - let downloadPreferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.automaticMediaDownloadSettings])) - let runningDownloadTasks = combineLatest(context.account.postbox.combinedView(keys: [downloadPreferencesKey]), context.account.shouldKeepBackgroundDownloadConnections.get()) - |> map { views, shouldKeepBackgroundDownloadConnections -> Bool in - let settings: AutomaticMediaDownloadSettings = (views.views[downloadPreferencesKey] as? PreferencesView)?.values[ApplicationSpecificPreferencesKeys.automaticMediaDownloadSettings] as? AutomaticMediaDownloadSettings ?? AutomaticMediaDownloadSettings.defaultSettings + let runningDownloadTasks = combineLatest(context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings]), context.account.shouldKeepBackgroundDownloadConnections.get()) + |> map { sharedData, shouldKeepBackgroundDownloadConnections -> Bool in + let settings: AutomaticMediaDownloadSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings] as? AutomaticMediaDownloadSettings ?? AutomaticMediaDownloadSettings.defaultSettings if !settings.downloadInBackground { return false } @@ -236,7 +207,7 @@ final class AuthorizedApplicationContext { } |> distinctUntilChanged - self.wakeupManager = WakeupManager(inForeground: context.sharedContext.applicationBindings.applicationInForeground, runningServiceTasks: context.account.importantTasksRunning, runningBackgroundLocationTasks: runningBackgroundLocationTasks, runningWatchTasks: runningWatchTasksPromise.get(), runningDownloadTasks: runningDownloadTasks) + self.wakeupManager = WakeupManager(accountManager: context.sharedContext.accountManager, inForeground: context.sharedContext.applicationBindings.applicationInForeground, runningServiceTasks: context.account.importantTasksRunning, runningBackgroundLocationTasks: runningBackgroundLocationTasks, runningWatchTasks: runningWatchTasksPromise.get(), runningDownloadTasks: runningDownloadTasks) self.showCallsTab = showCallsTab @@ -248,7 +219,7 @@ final class AuthorizedApplicationContext { context.attachOverlayMediaController(self.overlayMediaController) var presentImpl: ((ViewController, Any?) -> Void)? var openSettingsImpl: (() -> Void)? - let callManager = PresentationCallManager(account: context.account, getDeviceAccessData: { + let callManager = PresentationCallManager(accountManager: context.sharedContext.accountManager, account: context.account, getDeviceAccessData: { return (context.currentPresentationData.with { $0 }, { c, a in presentImpl?(c, a) }, { @@ -377,12 +348,10 @@ final class AuthorizedApplicationContext { let previousPasscodeState = Atomic(value: nil) - let preferencesKey: PostboxViewKey = .preferences(keys: Set([ApplicationSpecificPreferencesKeys.presentationPasscodeSettings])) - - self.passcodeStatusDisposable.set((combineLatest(queue: Queue.mainQueue(), context.account.postbox.combinedView(keys: [.accessChallengeData, preferencesKey]), context.sharedContext.applicationBindings.applicationIsActive) - |> map { view, isActive -> (PostboxAccessChallengeData, PresentationPasscodeSettings?, Bool) in - let accessChallengeData = (view.views[.accessChallengeData] as? AccessChallengeDataView)?.data ?? PostboxAccessChallengeData.none - let passcodeSettings = (view.views[preferencesKey] as! PreferencesView).values[ApplicationSpecificPreferencesKeys.presentationPasscodeSettings] as? PresentationPasscodeSettings + self.passcodeStatusDisposable.set((combineLatest(queue: Queue.mainQueue(), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), context.sharedContext.accountManager.accessChallengeData(), context.sharedContext.applicationBindings.applicationIsActive) + |> map { sharedData, accessChallengeDataView, isActive -> (PostboxAccessChallengeData, PresentationPasscodeSettings?, Bool) in + let accessChallengeData = accessChallengeDataView.data + let passcodeSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationPasscodeSettings] as? PresentationPasscodeSettings return (accessChallengeData, passcodeSettings, isActive) } |> map { accessChallengeData, passcodeSettings, isActive -> PasscodeState in @@ -424,7 +393,7 @@ final class AuthorizedApplicationContext { if previousState?.isActive != updatedState.isActive || isLocked != strongSelf.isLocked { if updatedAutolockDeadline != previousState?.challengeData.autolockDeadline { - let _ = (strongSelf.context.account.postbox.transaction { transaction -> Void in + let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in let data = transaction.getAccessChallengeData().withUpdatedAutolockDeadline(updatedAutolockDeadline) transaction.setAccessChallengeData(data) }).start() @@ -457,7 +426,7 @@ final class AuthorizedApplicationContext { return } if value != nil { - let _ = (strongSelf.context.account.postbox.transaction { transaction -> Void in + let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in let data = transaction.getAccessChallengeData().withUpdatedAutolockDeadline(nil) transaction.setAccessChallengeData(data) }).start() @@ -481,19 +450,19 @@ final class AuthorizedApplicationContext { guard let strongSelf = self else { return } - let _ = strongSelf.context.account.postbox.transaction({ transaction -> Void in + let _ = strongSelf.context.sharedContext.accountManager.transaction({ transaction -> Void in var attempts: AccessChallengeAttempts? if let attemptData = attemptData { attempts = AccessChallengeAttempts(count: Int32(attemptData.numberOfInvalidAttempts), timestamp: Int32(attemptData.dateOfLastInvalidAttempt)) } var data = transaction.getAccessChallengeData() switch data { - case .none: - break - case let .numericalPassword(value, timeout, _): - data = .numericalPassword(value: value, timeout: timeout, attempts: attempts) - case let .plaintextPassword(value, timeout, _): - data = .plaintextPassword(value: value, timeout: timeout, attempts: attempts) + case .none: + break + case let .numericalPassword(value, timeout, _): + data = .numericalPassword(value: value, timeout: timeout, attempts: attempts) + case let .plaintextPassword(value, timeout, _): + data = .plaintextPassword(value: value, timeout: timeout, attempts: attempts) } transaction.setAccessChallengeData(data) }).start() @@ -502,7 +471,7 @@ final class AuthorizedApplicationContext { guard let strongSelf = self else { return } - let _ = (strongSelf.context.account.postbox.transaction { transaction -> Void in + let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in let data = transaction.getAccessChallengeData().withUpdatedAutolockDeadline(nil) transaction.setAccessChallengeData(data) }).start() @@ -619,16 +588,13 @@ final class AuthorizedApplicationContext { } })) - let inAppPreferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.inAppNotificationSettings])) - self.inAppNotificationSettingsDisposable.set(((context.account.postbox.combinedView(keys: [inAppPreferencesKey])) |> deliverOnMainQueue).start(next: { [weak self] views in + self.inAppNotificationSettingsDisposable.set(((context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])) |> deliverOnMainQueue).start(next: { [weak self] sharedData in if let strongSelf = self { - if let view = views.views[inAppPreferencesKey] as? PreferencesView { - if let settings = view.values[ApplicationSpecificPreferencesKeys.inAppNotificationSettings] as? InAppNotificationSettings { - let previousSettings = strongSelf.inAppNotificationSettings - strongSelf.inAppNotificationSettings = settings - if let previousSettings = previousSettings, previousSettings.displayNameOnLockscreen != settings.displayNameOnLockscreen { - reinitializedNotificationSettings() - } + if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings] as? InAppNotificationSettings { + let previousSettings = strongSelf.inAppNotificationSettings + strongSelf.inAppNotificationSettings = settings + if let previousSettings = previousSettings, previousSettings.displayNameOnLockscreen != settings.displayNameOnLockscreen { + reinitializedNotificationSettings() } } } @@ -1038,10 +1004,10 @@ final class AuthorizedApplicationContext { } }) - let showCallsTabSignal = context.account.postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.callListSettings]) - |> map { view -> Bool in + let showCallsTabSignal = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.callListSettings]) + |> map { sharedData -> Bool in var value = true - if let settings = view.values[ApplicationSpecificPreferencesKeys.callListSettings] as? CallListSettings { + if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.callListSettings] as? CallListSettings { value = settings.showTab } return value diff --git a/Telegram-iOS/LegacyDataImport.swift b/Telegram-iOS/LegacyDataImport.swift index 5374c91440..62c8f60c90 100644 --- a/Telegram-iOS/LegacyDataImport.swift +++ b/Telegram-iOS/LegacyDataImport.swift @@ -14,7 +14,7 @@ enum AccountImportProgressType { case media } -private func importedAccountData(basePath: String, documentsPath: String, account: TemporaryAccount, database: SqliteInterface) -> Signal<(AccountImportProgressType, Float), AccountImportError> { +private func importedAccountData(basePath: String, documentsPath: String, accountManager: AccountManager, account: TemporaryAccount, database: SqliteInterface) -> Signal<(AccountImportProgressType, Float), AccountImportError> { return deferred { () -> Signal<(AccountImportProgressType, Float), AccountImportError> in let keychain = MTFileBasedKeychain(name: "Telegram", documentsPath: documentsPath) guard let masterDatacenterId = keychain.object(forKey: "defaultDatacenterId", group: "persistent") as? Int else { @@ -33,7 +33,7 @@ private func importedAccountData(basePath: String, documentsPath: String, accoun let importData = importPreferencesData(documentsPath: documentsPath, masterDatacenterId: Int32(masterDatacenterId), account: account, database: database) |> mapToSignal { accountUserId -> Signal<(AccountImportProgressType, Float), AccountImportError> in - return importDatabaseData(account: account, basePath: basePath, database: database, accountUserId: accountUserId) + return importDatabaseData(accountManager: accountManager, account: account, basePath: basePath, database: database, accountUserId: accountUserId) } return importKeychain @@ -67,7 +67,7 @@ private func importPreferencesData(documentsPath: String, masterDatacenterId: In } } -private func importDatabaseData(account: TemporaryAccount, basePath: String, database: SqliteInterface, accountUserId: Int32) -> Signal<(AccountImportProgressType, Float), AccountImportError> { +private func importDatabaseData(accountManager: AccountManager, account: TemporaryAccount, basePath: String, database: SqliteInterface, accountUserId: Int32) -> Signal<(AccountImportProgressType, Float), AccountImportError> { return deferred { () -> Signal<(AccountImportProgressType, Float), AccountImportError> in var importedAccountUser: Signal = .complete() if let (user, presence) = loadLegacyUser(database: database, id: accountUserId) { @@ -85,7 +85,7 @@ private func importDatabaseData(account: TemporaryAccount, basePath: String, dat /*let importedFiles = loadLegacyFiles(account: account, basePath: basePath, accountPeerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: accountUserId), database: database) |> introduceError(AccountImportError.self)*/ - let importedLegacyPreferences = importLegacyPreferences(account: account, documentsPath: basePath + "/Documents", database: database) + let importedLegacyPreferences = importLegacyPreferences(accountManager: accountManager, account: account, documentsPath: basePath + "/Documents", database: database) |> introduceError(AccountImportError.self) return importedAccountUser @@ -221,7 +221,7 @@ func importedLegacyAccount(basePath: String, accountManager: AccountManager, pre return temporaryAccount(manager: accountManager, rootPath: rootPathForBasePath(basePath)) |> introduceError(AccountImportError.self) |> mapToSignal { account -> Signal in - let actions = importedAccountData(basePath: basePath, documentsPath: documentsPath, account: account, database: database) + let actions = importedAccountData(basePath: basePath, documentsPath: documentsPath, accountManager: accountManager, account: account, database: database) var result = actions |> map { typeAndProgress -> ImportedLegacyAccountEvent in return .progress(typeAndProgress.0, typeAndProgress.1) diff --git a/Telegram-iOS/LegacyPreferencesImport.swift b/Telegram-iOS/LegacyPreferencesImport.swift index 84fe0c42d2..500994470b 100644 --- a/Telegram-iOS/LegacyPreferencesImport.swift +++ b/Telegram-iOS/LegacyPreferencesImport.swift @@ -56,7 +56,7 @@ private func convertLegacyProxyPort(_ value: Int) -> Int32 { } } -func importLegacyPreferences(account: TemporaryAccount, documentsPath: String, database: SqliteInterface) -> Signal { +func importLegacyPreferences(accountManager: AccountManager, account: TemporaryAccount, documentsPath: String, database: SqliteInterface) -> Signal { return deferred { () -> Signal in var presentationState: TGPresentationState? if let value = NSKeyedUnarchiver.unarchiveObject(withFile: documentsPath + "/presentation.dat") as? TGPresentationState { @@ -172,8 +172,8 @@ func importLegacyPreferences(account: TemporaryAccount, documentsPath: String, d localization = NSKeyedUnarchiver.unarchiveObject(withFile: nativeDocumentsPath + "/localization") as? TGLocalization } - return account.postbox.transaction { transaction -> Void in - transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.presentationThemeSettings, { current in + return accountManager.transaction { transaction -> Signal in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { current in var settings = (current as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings if let presentationState = presentationState { switch presentationState.pallete { @@ -249,7 +249,7 @@ func importLegacyPreferences(account: TemporaryAccount, documentsPath: String, d return settings }) - transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.automaticMediaDownloadSettings, { current in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings, { current in var settings: AutomaticMediaDownloadSettings = current as? AutomaticMediaDownloadSettings ?? .defaultSettings if let preferences = autoDownloadPreferences, !preferences.isDefaultPreferences() { @@ -285,7 +285,7 @@ func importLegacyPreferences(account: TemporaryAccount, documentsPath: String, d return settings }) - transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.inAppNotificationSettings, { current in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.inAppNotificationSettings, { current in var settings: InAppNotificationSettings = current as? InAppNotificationSettings ?? .defaultSettings if let soundEnabled = soundEnabled { settings.playSounds = soundEnabled @@ -299,7 +299,7 @@ func importLegacyPreferences(account: TemporaryAccount, documentsPath: String, d return settings }) - transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.voiceCallSettings, { current in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.voiceCallSettings, { current in var settings: VoiceCallSettings = current as? VoiceCallSettings ?? .defaultSettings if let callsDataUsageMode = callsDataUsageMode { switch callsDataUsageMode { @@ -320,7 +320,119 @@ func importLegacyPreferences(account: TemporaryAccount, documentsPath: String, d return settings }) - transaction.updatePreferencesEntry(key: PreferencesKeys.proxySettings, { current in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.callListSettings, { current in + var settings: CallListSettings = current as? CallListSettings ?? .defaultSettings + if let showCallsTab = showCallsTab { + settings.showTab = showCallsTab + } + return settings + }) + + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationPasscodeSettings, { current in + var settings: PresentationPasscodeSettings = current as? PresentationPasscodeSettings ?? .defaultSettings + if let passcodeChallenge = passcodeChallenge { + transaction.setAccessChallengeData(passcodeChallenge) + switch passcodeChallenge { + case .none: + break + case let .numericalPassword(_, timeout, _): + settings.autolockTimeout = timeout + case let .plaintextPassword(_, timeout, _): + settings.autolockTimeout = timeout + } + settings.enableBiometrics = passcodeEnableBiometrics + } + return settings + }) + + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.stickerSettings, { current in + var settings: StickerSettings = current as? StickerSettings ?? .defaultSettings + if let stickersSuggestMode = stickersSuggestMode { + switch stickersSuggestMode { + case 1: + settings.emojiStickerSuggestionMode = .installed + case 2: + settings.emojiStickerSuggestionMode = .none + default: + settings.emojiStickerSuggestionMode = .all + } + } + return settings + }) + + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { current in + var settings: MusicPlaybackSettings = current as? MusicPlaybackSettings ?? .defaultSettings + if let musicPlayerOrderType = musicPlayerOrderType { + switch musicPlayerOrderType { + case 1: + settings.order = .reversed + case 2: + settings.order = .random + default: + settings.order = .regular + } + } + if let musicPlayerRepeatType = musicPlayerRepeatType { + switch musicPlayerRepeatType { + case 1: + settings.looping = .all + case 2: + settings.looping = .item + default: + settings.looping = .none + } + } + return settings + }) + + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.instantPagePresentationSettings, { current in + var settings: InstantPagePresentationSettings = current as? InstantPagePresentationSettings ?? .defaultSettings + if let instantPageFontSize = instantPageFontSize { + switch instantPageFontSize { + case 0.85: + settings.fontSize = .small + case 1.15: + settings.fontSize = .large + case 1.30: + settings.fontSize = .xlarge + case 1.50: + settings.fontSize = .xxlarge + default: + settings.fontSize = .standard + } + } + if let instantPageFontSerif = instantPageFontSerif { + settings.forceSerif = instantPageFontSerif == 1 + } + if let instantPageTheme = instantPageTheme { + switch instantPageTheme { + case 1: + settings.themeType = .sepia + case 2: + settings.themeType = .gray + case 3: + settings.themeType = .dark + default: + settings.themeType = .light + } + } + if let instantPageAutoNightMode = instantPageAutoNightMode { + settings.autoNightMode = instantPageAutoNightMode == 1 + } + return settings + }) + + if let localization = localization { + transaction.updateSharedData(SharedDataKeys.localizationSettings, { _ in + var entries: [LocalizationEntry] = [] + for (key, value) in localization.dict() { + entries.append(LocalizationEntry.string(key: key, value: value)) + } + return LocalizationSettings(primaryComponent: LocalizationComponent(languageCode: localization.code, localizedName: "", localization: Localization(version: 0, entries: entries), customPluralizationCode: nil), secondaryComponent: nil) + }) + } + + transaction.updateSharedData(SharedDataKeys.proxySettings, { current in var settings: ProxySettings = current as? ProxySettings ?? .defaultSettings if let callsUseProxy = callsUseProxy { settings.useForCalls = callsUseProxy @@ -364,134 +476,25 @@ func importLegacyPreferences(account: TemporaryAccount, documentsPath: String, d return settings }) - transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.stickerSettings, { current in - var settings: StickerSettings = current as? StickerSettings ?? .defaultSettings - if let stickersSuggestMode = stickersSuggestMode { - switch stickersSuggestMode { - case 1: - settings.emojiStickerSuggestionMode = .installed - case 2: - settings.emojiStickerSuggestionMode = .none - default: - settings.emojiStickerSuggestionMode = .all + return account.postbox.transaction { transaction -> Void in + transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.contactSynchronizationSettings, { current in + var settings: ContactSynchronizationSettings = current as? ContactSynchronizationSettings ?? .defaultSettings + if let contactsInhibitSync = contactsInhibitSync, contactsInhibitSync { + settings.synchronizeDeviceContacts = false } - } - return settings - }) - - transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.contactSynchronizationSettings, { current in - var settings: ContactSynchronizationSettings = current as? ContactSynchronizationSettings ?? .defaultSettings - if let contactsInhibitSync = contactsInhibitSync, contactsInhibitSync { - settings.synchronizeDeviceContacts = false - } - return settings - }) - - transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.callListSettings, { current in - var settings: CallListSettings = current as? CallListSettings ?? .defaultSettings - if let showCallsTab = showCallsTab { - settings.showTab = showCallsTab - } - return settings - }) - - transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.presentationPasscodeSettings, { current in - var settings: PresentationPasscodeSettings = current as? PresentationPasscodeSettings ?? .defaultSettings - if let passcodeChallenge = passcodeChallenge { - transaction.setAccessChallengeData(passcodeChallenge) - switch passcodeChallenge { - case .none: - break - case let .numericalPassword(_, timeout, _): - settings.autolockTimeout = timeout - case let .plaintextPassword(_, timeout, _): - settings.autolockTimeout = timeout - } - settings.enableBiometrics = passcodeEnableBiometrics - } - return settings - }) - - if let localization = localization { - transaction.updatePreferencesEntry(key: PreferencesKeys.localizationSettings, { _ in - var entries: [LocalizationEntry] = [] - for (key, value) in localization.dict() { - entries.append(LocalizationEntry.string(key: key, value: value)) - } - return LocalizationSettings(primaryComponent: LocalizationComponent(languageCode: localization.code, localizedName: "", localization: Localization(version: 0, entries: entries), customPluralizationCode: nil), secondaryComponent: nil) + return settings }) + + if let secretInlineBotsInitialized = secretInlineBotsInitialized, secretInlineBotsInitialized { + ApplicationSpecificNotice.setSecretChatInlineBotUsage(transaction: transaction) + } + + if let allowSecretWebpagesInitialized = allowSecretWebpagesInitialized, allowSecretWebpagesInitialized, let allowSecretWebpages = allowSecretWebpages { + ApplicationSpecificNotice.setSecretChatLinkPreviews(transaction: transaction, value: allowSecretWebpages) + } } - - if let secretInlineBotsInitialized = secretInlineBotsInitialized, secretInlineBotsInitialized { - ApplicationSpecificNotice.setSecretChatInlineBotUsage(transaction: transaction) - } - - if let allowSecretWebpagesInitialized = allowSecretWebpagesInitialized, allowSecretWebpagesInitialized, let allowSecretWebpages = allowSecretWebpages { - ApplicationSpecificNotice.setSecretChatLinkPreviews(transaction: transaction, value: allowSecretWebpages) - } - - transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.musicPlaybackSettings, { current in - var settings: MusicPlaybackSettings = current as? MusicPlaybackSettings ?? .defaultSettings - if let musicPlayerOrderType = musicPlayerOrderType { - switch musicPlayerOrderType { - case 1: - settings.order = .reversed - case 2: - settings.order = .random - default: - settings.order = .regular - } - } - if let musicPlayerRepeatType = musicPlayerRepeatType { - switch musicPlayerRepeatType { - case 1: - settings.looping = .all - case 2: - settings.looping = .item - default: - settings.looping = .none - } - } - return settings - }) - - transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.instantPagePresentationSettings, { current in - var settings: InstantPagePresentationSettings = current as? InstantPagePresentationSettings ?? .defaultSettings - if let instantPageFontSize = instantPageFontSize { - switch instantPageFontSize { - case 0.85: - settings.fontSize = .small - case 1.15: - settings.fontSize = .large - case 1.30: - settings.fontSize = .xlarge - case 1.50: - settings.fontSize = .xxlarge - default: - settings.fontSize = .standard - } - } - if let instantPageFontSerif = instantPageFontSerif { - settings.forceSerif = instantPageFontSerif == 1 - } - if let instantPageTheme = instantPageTheme { - switch instantPageTheme { - case 1: - settings.themeType = .sepia - case 2: - settings.themeType = .gray - case 3: - settings.themeType = .dark - default: - settings.themeType = .light - } - } - if let instantPageAutoNightMode = instantPageAutoNightMode { - settings.autoNightMode = instantPageAutoNightMode == 1 - } - return settings - }) } + |> switchToLatest |> ignoreValues } } diff --git a/Telegram-iOS/NotificationManager.swift b/Telegram-iOS/NotificationManager.swift index 1d0bce13f0..e0e0bee9c3 100644 --- a/Telegram-iOS/NotificationManager.swift +++ b/Telegram-iOS/NotificationManager.swift @@ -242,9 +242,9 @@ final class NotificationManager { func commitRemoteNotification(context: AccountContext, originalRequestId: NotificationManagedNotificationRequestId?, messageIds: [MessageId]) -> Signal { return context.account.postbox.transaction { transaction -> ([(MessageId, [Message], Bool, PeerMessageSound, Bool)], Bool) in var isLocked = false - if isAccessLocked(data: transaction.getAccessChallengeData(), at: Int32(CFAbsoluteTimeGetCurrent())) { + /*if isAccessLocked(data: transaction.getAccessChallengeData(), at: Int32(CFAbsoluteTimeGetCurrent())) { isLocked = true - } + }*/ var results: [(MessageId, [Message], Bool, PeerMessageSound, Bool)] = [] var updatedMessageIds = messageIds diff --git a/Telegram-iOS/WakeupManager.swift b/Telegram-iOS/WakeupManager.swift index 5785ef6720..08f65e21c2 100644 --- a/Telegram-iOS/WakeupManager.swift +++ b/Telegram-iOS/WakeupManager.swift @@ -45,6 +45,7 @@ private struct CombinedRunningImportantTasks: Equatable { } final class WakeupManager { + private let accountManager: AccountManager private var state = WakeupManagerState() private let isProcessingNotificationsValue = ValuePromise(false, ignoreRepeated: true) @@ -68,7 +69,9 @@ final class WakeupManager { private var wakeupResultSubscribers: [(Int32, ([MessageId]) -> Signal)] = [] - init(inForeground: Signal, runningServiceTasks: Signal, runningBackgroundLocationTasks: Signal, runningWatchTasks: Signal, runningDownloadTasks: Signal) { + init(accountManager: AccountManager, inForeground: Signal, runningServiceTasks: Signal, runningBackgroundLocationTasks: Signal, runningWatchTasks: Signal, runningDownloadTasks: Signal) { + self.accountManager = accountManager + self.inForegroundDisposable = (inForeground |> distinctUntilChanged |> deliverOnMainQueue).start(next: { [weak self] value in if let strongSelf = self { if value { @@ -133,14 +136,20 @@ final class WakeupManager { collectedSignals.append(first.1(messageIds)) } } + let accountManager = self.accountManager return combineLatest(collectedSignals) |> map { _ -> Void in return Void() } |> mapToSignal { _ -> Signal in if !messageIds.isEmpty { - return account.postbox.transaction { transaction -> Int32? in - let (unreadCount, _) = renderedTotalUnreadCount(transaction: transaction) - return unreadCount + return accountManager.transaction { transaction -> InAppNotificationSettings in + return transaction.getSharedData(ApplicationSpecificSharedDataKeys.inAppNotificationSettings) as? InAppNotificationSettings ?? InAppNotificationSettings.defaultSettings + } + |> mapToSignal { inAppSettings -> Signal in + return account.postbox.transaction { transaction -> Int32? in + let (unreadCount, _) = renderedTotalUnreadCount(inAppNotificationSettings: inAppSettings, transaction: transaction) + return unreadCount + } } } else { return .single(nil) diff --git a/Telegram-iOS/WatchCommunicationManager.swift b/Telegram-iOS/WatchCommunicationManager.swift index f8a02236a7..ae07043f86 100644 --- a/Telegram-iOS/WatchCommunicationManager.swift +++ b/Telegram-iOS/WatchCommunicationManager.swift @@ -65,10 +65,9 @@ final class WatchCommunicationManager { strongSelf.server.setMicAccessAllowed(true) strongSelf.server.pushContext() - let watchPresetSettingsKey = ApplicationSpecificPreferencesKeys.watchPresetSettings - strongSelf.presets.set(context.context.account.postbox.preferencesView(keys: [watchPresetSettingsKey]) - |> map({ preferences -> WatchPresetSettings in - return (preferences.values[watchPresetSettingsKey] as? WatchPresetSettings) ?? WatchPresetSettings.defaultSettings + strongSelf.presets.set(context.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.watchPresetSettings]) + |> map({ sharedData -> WatchPresetSettings in + return (sharedData.entries[ApplicationSpecificSharedDataKeys.watchPresetSettings] as? WatchPresetSettings) ?? WatchPresetSettings.defaultSettings })) } else { strongSelf.accountContext.set(.single(nil)) diff --git a/Widget/TodayViewController.swift b/Widget/TodayViewController.swift index b5fa8b7225..4a738f119b 100644 --- a/Widget/TodayViewController.swift +++ b/Widget/TodayViewController.swift @@ -82,13 +82,11 @@ class TodayViewController: UIViewController, NCWidgetProviding { } else { let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown" initializeAccountManagement() - account = accountManager(basePath: rootPath + "/accounts-metadata") - |> take(1) - |> mapToSignal { accountManager -> Signal in - return currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: auxiliaryMethods) - |> mapToSignal { account -> Signal in - if let account = account { - switch account { + let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata") + account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: auxiliaryMethods) + |> mapToSignal { account -> Signal in + if let account = account { + switch account { case .upgrading: return .complete() case let .authorized(account): @@ -96,13 +94,11 @@ class TodayViewController: UIViewController, NCWidgetProviding { return .single(account) case .unauthorized: return .complete() - } - } else { - return .complete() } + } else { + return .complete() } } - |> take(1) } let applicationInterface = account |> afterNext { account in diff --git a/submodules/Display b/submodules/Display index d444a36f98..709df1faef 160000 --- a/submodules/Display +++ b/submodules/Display @@ -1 +1 @@ -Subproject commit d444a36f9875c56902bef4d776098215cb3aa4db +Subproject commit 709df1faeffdd6fefc72bb8a1853b65a5e803bdc diff --git a/submodules/MtProtoKit b/submodules/MtProtoKit index 89531617a0..67e60e8e8b 160000 --- a/submodules/MtProtoKit +++ b/submodules/MtProtoKit @@ -1 +1 @@ -Subproject commit 89531617a0b77014aa69dd8043b67858218e6a1c +Subproject commit 67e60e8e8b4ab11daa5b52e732d1a62232b2d31a diff --git a/submodules/Postbox b/submodules/Postbox index f811d2a647..494ba4dc7b 160000 --- a/submodules/Postbox +++ b/submodules/Postbox @@ -1 +1 @@ -Subproject commit f811d2a6475398acafae51f8b562b65d486b8bfd +Subproject commit 494ba4dc7b8ed5962c64ac1bf3b1efd8b4ec77d7 diff --git a/submodules/TelegramCore b/submodules/TelegramCore index 668207ab55..84231fb6fc 160000 --- a/submodules/TelegramCore +++ b/submodules/TelegramCore @@ -1 +1 @@ -Subproject commit 668207ab55d2e58843b0614260f7738c3d6116c4 +Subproject commit 84231fb6fcbb39f72b50b2812c728cab8c659b52 diff --git a/submodules/TelegramUI b/submodules/TelegramUI index 9cd176142e..3540d5ea00 160000 --- a/submodules/TelegramUI +++ b/submodules/TelegramUI @@ -1 +1 @@ -Subproject commit 9cd176142e2b34e0062e13f09101ac6364f83b52 +Subproject commit 3540d5ea002762f50d38ad9cd01ada7f8af2f023