From 04057b0579645f0ce62186b7168e461f78f2fbe5 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 18 Feb 2021 19:26:11 +0400 Subject: [PATCH] Add support for temporary cloning the database --- .../ApplicationSpecificSharedDataKeys.swift | 3 +- .../Sources/InAppNotificationSettings.swift | 3 +- .../Sources/Namespaces.swift | 3 +- .../Sources/NotificationService.swift | 5 +- .../NotificationService/Sources/Sync.swift | 26 +- .../Sources/TelegramChannel.swift | 3 +- Telegram/SiriIntents/IntentHandler.swift | 6 +- .../WidgetKitWidget/TodayViewController.swift | 432 ++++++------------ submodules/Postbox/Sources/Postbox.swift | 50 +- submodules/Postbox/Sources/TempBox.swift | 65 ++- .../StandaloneAccountTransaction.swift | 4 +- submodules/TelegramCore/Sources/Account.swift | 8 +- .../TelegramCore/Sources/AccountManager.swift | 2 +- 13 files changed, 286 insertions(+), 324 deletions(-) diff --git a/Telegram/NotificationService/Sources/ApplicationSpecificSharedDataKeys.swift b/Telegram/NotificationService/Sources/ApplicationSpecificSharedDataKeys.swift index 4b9afe9be4..a8099363a0 100644 --- a/Telegram/NotificationService/Sources/ApplicationSpecificSharedDataKeys.swift +++ b/Telegram/NotificationService/Sources/ApplicationSpecificSharedDataKeys.swift @@ -1,5 +1,5 @@ import Foundation -import ValueBox +/*import ValueBox private func applicationSpecificSharedDataKey(_ value: Int32) -> ValueBoxKey { let key = ValueBoxKey(length: 4) @@ -14,3 +14,4 @@ private enum ApplicationSpecificSharedDataKeyValues: Int32 { public struct ApplicationSpecificSharedDataKeys { public static let inAppNotificationSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.inAppNotificationSettings.rawValue) } +*/ diff --git a/Telegram/NotificationService/Sources/InAppNotificationSettings.swift b/Telegram/NotificationService/Sources/InAppNotificationSettings.swift index ea6e849c8f..8da59f2aaf 100644 --- a/Telegram/NotificationService/Sources/InAppNotificationSettings.swift +++ b/Telegram/NotificationService/Sources/InAppNotificationSettings.swift @@ -1,5 +1,5 @@ import Foundation -import PostboxCoding +/*import PostboxCoding import PreferencesTable import MessageHistoryMetadataTable import PostboxDataTypes @@ -103,3 +103,4 @@ public struct InAppNotificationSettings: PreferencesEntry, Equatable { } } } +*/ diff --git a/Telegram/NotificationService/Sources/Namespaces.swift b/Telegram/NotificationService/Sources/Namespaces.swift index d8aed1ec40..a8fa068454 100644 --- a/Telegram/NotificationService/Sources/Namespaces.swift +++ b/Telegram/NotificationService/Sources/Namespaces.swift @@ -1,4 +1,4 @@ -import PostboxDataTypes +/*import PostboxDataTypes struct LegacyPeerSummaryCounterTags: OptionSet, Sequence, Hashable { var rawValue: Int32 @@ -52,3 +52,4 @@ struct Namespaces { static let SecretChat: Int32 = 3 } } +*/ diff --git a/Telegram/NotificationService/Sources/NotificationService.swift b/Telegram/NotificationService/Sources/NotificationService.swift index ed38bb5585..a00e09cf1b 100644 --- a/Telegram/NotificationService/Sources/NotificationService.swift +++ b/Telegram/NotificationService/Sources/NotificationService.swift @@ -18,9 +18,10 @@ final class NotificationService: UNNotificationServiceExtension { f() } }, countIncomingMessage: { rootPath, accountId, encryptionParameters, peerId, messageId in - SyncProviderImpl.addIncomingMessage(queue: queue, withRootPath: rootPath, accountId: accountId, encryptionParameters: encryptionParameters, peerId: peerId, messageId: messageId, completion: { count in + /*SyncProviderImpl.addIncomingMessage(queue: queue, withRootPath: rootPath, accountId: accountId, encryptionParameters: encryptionParameters, peerId: peerId, messageId: messageId, completion: { count in completion?(count) - }) + })*/ + completion?(0) }, isLocked: { rootPath in return SyncProviderImpl.isLocked(withRootPath: rootPath) }, lockedMessageText: { rootPath in diff --git a/Telegram/NotificationService/Sources/Sync.swift b/Telegram/NotificationService/Sources/Sync.swift index 1b66718df3..cb2ed09a48 100644 --- a/Telegram/NotificationService/Sources/Sync.swift +++ b/Telegram/NotificationService/Sources/Sync.swift @@ -1,30 +1,30 @@ import Foundation import SwiftSignalKit -import ValueBox -import PostboxDataTypes -import MessageHistoryReadStateTable -import MessageHistoryMetadataTable -import PreferencesTable -import PeerTable -import PostboxCoding +//import ValueBox +//import PostboxDataTypes +//import MessageHistoryReadStateTable +//import MessageHistoryMetadataTable +//import PreferencesTable +//import PeerTable +//import PostboxCoding import AppLockState import NotificationsPresentationData import BuildConfig -private let registeredTypes: Void = { +/*private let registeredTypes: Void = { declareEncodable(InAppNotificationSettings.self, f: InAppNotificationSettings.init(decoder:)) declareEncodable(TelegramChannel.self, f: TelegramChannel.init(decoder:)) -}() +}()*/ private func accountRecordIdPathName(_ id: Int64) -> String { return "account-\(UInt64(bitPattern: id))" } -private final class ValueBoxLoggerImpl: ValueBoxLogger { +/*private final class ValueBoxLoggerImpl: ValueBoxLogger { func log(_ what: String) { print("ValueBox: \(what)") } -} +}*/ enum SyncProviderImpl { static func isLocked(withRootPath rootPath: String) -> Bool { @@ -43,7 +43,7 @@ enum SyncProviderImpl { } } - static func addIncomingMessage(queue: Queue, withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: @escaping (Int32) -> Void) { + /*static func addIncomingMessage(queue: Queue, withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: @escaping (Int32) -> Void) { queue.async { let _ = registeredTypes @@ -144,5 +144,5 @@ enum SyncProviderImpl { completion(-1) } } - } + }*/ } diff --git a/Telegram/NotificationService/Sources/TelegramChannel.swift b/Telegram/NotificationService/Sources/TelegramChannel.swift index 888afe3cc3..7f43eb20d6 100644 --- a/Telegram/NotificationService/Sources/TelegramChannel.swift +++ b/Telegram/NotificationService/Sources/TelegramChannel.swift @@ -1,4 +1,4 @@ -import PostboxDataTypes +/*import PostboxDataTypes import PostboxCoding public enum TelegramChannelInfo: Int32 { @@ -36,3 +36,4 @@ public final class TelegramChannel: Peer { return true } } +*/ diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index 3012c350c7..d8b6dc86e5 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -793,7 +793,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo var accountResults: [Signal, Error>] = [] for (accountId, accountPeerId, _) in accounts { - accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, transaction: { postbox, transaction -> INObjectSection in + accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, useCopy: true, transaction: { postbox, transaction -> INObjectSection in var accountTitle: String = "" if let peer = transaction.getPeer(accountPeerId) as? TelegramUser { if let username = peer.username, !username.isEmpty { @@ -962,7 +962,7 @@ private final class WidgetIntentHandler { var accountResults: [Signal, Error>] = [] for (accountId, accountPeerId, _) in accounts { - accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, transaction: { postbox, transaction -> INObjectSection in + accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, useCopy: true, transaction: { postbox, transaction -> INObjectSection in var accountTitle: String = "" if let peer = transaction.getPeer(accountPeerId) as? TelegramUser { if let username = peer.username, !username.isEmpty { @@ -1045,7 +1045,7 @@ private final class WidgetIntentHandler { if !isActive { continue } - accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, transaction: { postbox, transaction -> [Friend] in + accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, useCopy: true, transaction: { postbox, transaction -> [Friend] in var peers: [Peer] = [] for id in getRecentPeers(transaction: transaction) { diff --git a/Telegram/WidgetKitWidget/TodayViewController.swift b/Telegram/WidgetKitWidget/TodayViewController.swift index fab57433f9..b459177fcf 100644 --- a/Telegram/WidgetKitWidget/TodayViewController.swift +++ b/Telegram/WidgetKitWidget/TodayViewController.swift @@ -65,6 +65,152 @@ private func rootPathForBasePath(_ appGroupPath: String) -> String { return appGroupPath + "/telegram-data" } +@available(iOS 14.0, *) +private func getCommonTimeline(friends: [Friend]?, in context: TimelineProviderContext, completion: @escaping (Timeline) -> ()) { + if context.isPreview { + completion(Timeline(entries: [SimpleEntry(date: Date(), contents: .preview)], policy: .atEnd)) + return + } + + let currentDate = Date() + let entryDate = Calendar.current.date(byAdding: .hour, value: 0, to: currentDate)! + + guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else { + completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd)) + return + } + + let baseAppBundleId = String(appBundleIdentifier[..] = [] + for (accountId, items) in itemsByAccount { + friendsByAccount.append(accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: accountId), encryptionParameters: encryptionParameters, isReadOnly: true, useCopy: true, transaction: { postbox, transaction -> [ParsedPeer] in + guard let state = transaction.getState() as? AuthorizedAccountState else { + return [] + } + + var result: [ParsedPeer] = [] + + for (peerId, _) in items { + guard let peer = transaction.getPeer(PeerId(peerId)) else { + continue + } + + var name: String = "" + var lastName: String? + + if let user = peer as? TelegramUser { + if let firstName = user.firstName { + name = firstName + lastName = user.lastName + } else if let lastName = user.lastName { + name = lastName + } else if let phone = user.phone, !phone.isEmpty { + name = phone + } + } else { + name = peer.debugDisplayTitle + } + + var badge: WidgetDataPeer.Badge? + + if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 { + var isMuted = false + if let notificationSettings = transaction.getPeerNotificationSettings(peer.id) as? TelegramPeerNotificationSettings { + isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false) + } + badge = WidgetDataPeer.Badge( + count: Int(readState.count), + isMuted: isMuted + ) + } + + var mappedMessage: WidgetDataPeer.Message? + if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) { + if let message = transaction.getMessage(index.id) { + mappedMessage = WidgetDataPeer.Message(accountPeerId: state.peerId, message: message) + } + } + + let widgetPeer = WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in + return postbox.mediaBox.resourcePath(representation.resource) + }, badge: badge, message: mappedMessage) + + result.append(ParsedPeer(accountId: accountId, accountPeerId: state.peerId.toInt64(), peer: widgetPeer)) + } + + return result + }) + |> `catch` { _ -> Signal<[ParsedPeer], NoError> in + return .single([]) + }) + } + + let _ = combineLatest(friendsByAccount).start(next: { allPeers in + var orderedPeers: [ParsedPeer] = [] + + outer: for (accountId, peerId) in itemOrder { + for peerSet in allPeers { + for peer in peerSet { + if peer.accountId == accountId && peer.peer.id == peerId { + orderedPeers.append(peer) + continue outer + } + } + } + } + + let result = ParsedPeers(peers: orderedPeers, updateTimestamp: Int32(Date().timeIntervalSince1970)) + completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .peers(result))], policy: .atEnd)) + }) +} + @available(iOSApplicationExtension 14.0, iOS 14.0, *) struct Provider: IntentTimelineProvider { public typealias Entry = SimpleEntry @@ -79,148 +225,7 @@ struct Provider: IntentTimelineProvider { } func getTimeline(for configuration: SelectFriendsIntent, in context: Context, completion: @escaping (Timeline) -> ()) { - if context.isPreview { - completion(Timeline(entries: [SimpleEntry(date: Date(), contents: .preview)], policy: .atEnd)) - return - } - - let currentDate = Date() - let entryDate = Calendar.current.date(byAdding: .hour, value: 0, to: currentDate)! - - guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else { - completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd)) - return - } - - let baseAppBundleId = String(appBundleIdentifier[..] = [] - for (accountId, items) in itemsByAccount { - friendsByAccount.append(accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: accountId), encryptionParameters: encryptionParameters, isReadOnly: true, transaction: { postbox, transaction -> [ParsedPeer] in - guard let state = transaction.getState() as? AuthorizedAccountState else { - return [] - } - - var result: [ParsedPeer] = [] - - for (peerId, _) in items { - guard let peer = transaction.getPeer(PeerId(peerId)) else { - continue - } - - var name: String = "" - var lastName: String? - - if let user = peer as? TelegramUser { - if let firstName = user.firstName { - name = firstName - lastName = user.lastName - } else if let lastName = user.lastName { - name = lastName - } else if let phone = user.phone, !phone.isEmpty { - name = phone - } - } else { - name = peer.debugDisplayTitle - } - - var badge: WidgetDataPeer.Badge? - - if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 { - var isMuted = false - if let notificationSettings = transaction.getPeerNotificationSettings(peer.id) as? TelegramPeerNotificationSettings { - isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false) - } - badge = WidgetDataPeer.Badge( - count: Int(readState.count), - isMuted: isMuted - ) - } - - var mappedMessage: WidgetDataPeer.Message? - if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) { - if let message = transaction.getMessage(index.id) { - mappedMessage = WidgetDataPeer.Message(accountPeerId: state.peerId, message: message) - } - } - - let widgetPeer = WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in - return postbox.mediaBox.resourcePath(representation.resource) - }, badge: badge, message: mappedMessage) - - result.append(ParsedPeer(accountId: accountId, accountPeerId: state.peerId.toInt64(), peer: widgetPeer)) - } - - return result - }) - |> `catch` { _ -> Signal<[ParsedPeer], NoError> in - return .single([]) - }) - } - - let _ = combineLatest(friendsByAccount).start(next: { allPeers in - var orderedPeers: [ParsedPeer] = [] - - outer: for (accountId, peerId) in itemOrder { - for peerSet in allPeers { - for peer in peerSet { - if peer.accountId == accountId && peer.peer.id == peerId { - orderedPeers.append(peer) - continue outer - } - } - } - } - - let result = ParsedPeers(peers: orderedPeers, updateTimestamp: Int32(Date().timeIntervalSince1970)) - completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .peers(result))], policy: .atEnd)) - }) + getCommonTimeline(friends: configuration.friends, in: context, completion: completion) } } @@ -238,148 +243,7 @@ struct AvatarsProvider: IntentTimelineProvider { } func getTimeline(for configuration: SelectAvatarFriendsIntent, in context: Context, completion: @escaping (Timeline) -> ()) { - if context.isPreview { - completion(Timeline(entries: [SimpleEntry(date: Date(), contents: .preview)], policy: .atEnd)) - return - } - - let currentDate = Date() - let entryDate = Calendar.current.date(byAdding: .hour, value: 0, to: currentDate)! - - guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else { - completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd)) - return - } - - let baseAppBundleId = String(appBundleIdentifier[..] = [] - for (accountId, items) in itemsByAccount { - friendsByAccount.append(accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: accountId), encryptionParameters: encryptionParameters, isReadOnly: true, transaction: { postbox, transaction -> [ParsedPeer] in - guard let state = transaction.getState() as? AuthorizedAccountState else { - return [] - } - - var result: [ParsedPeer] = [] - - for (peerId, _) in items { - guard let peer = transaction.getPeer(PeerId(peerId)) else { - continue - } - - var name: String = "" - var lastName: String? - - if let user = peer as? TelegramUser { - if let firstName = user.firstName { - name = firstName - lastName = user.lastName - } else if let lastName = user.lastName { - name = lastName - } else if let phone = user.phone, !phone.isEmpty { - name = phone - } - } else { - name = peer.debugDisplayTitle - } - - var badge: WidgetDataPeer.Badge? - - if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 { - var isMuted = false - if let notificationSettings = transaction.getPeerNotificationSettings(peer.id) as? TelegramPeerNotificationSettings { - isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false) - } - badge = WidgetDataPeer.Badge( - count: Int(readState.count), - isMuted: isMuted - ) - } - - var mappedMessage: WidgetDataPeer.Message? - if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) { - if let message = transaction.getMessage(index.id) { - mappedMessage = WidgetDataPeer.Message(accountPeerId: state.peerId, message: message) - } - } - - let widgetPeer = WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in - return postbox.mediaBox.resourcePath(representation.resource) - }, badge: badge, message: mappedMessage) - - result.append(ParsedPeer(accountId: accountId, accountPeerId: state.peerId.toInt64(), peer: widgetPeer)) - } - - return result - }) - |> `catch` { _ -> Signal<[ParsedPeer], NoError> in - return .single([]) - }) - } - - let _ = combineLatest(friendsByAccount).start(next: { allPeers in - var orderedPeers: [ParsedPeer] = [] - - outer: for (accountId, peerId) in itemOrder { - for peerSet in allPeers { - for peer in peerSet { - if peer.accountId == accountId && peer.peer.id == peerId { - orderedPeers.append(peer) - continue outer - } - } - } - } - - let result = ParsedPeers(peers: orderedPeers, updateTimestamp: Int32(Date().timeIntervalSince1970)) - completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .peers(result))], policy: .atEnd)) - }) + getCommonTimeline(friends: configuration.friends, in: context, completion: completion) } } diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 23fa9e34ac..5b0f799b5f 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1125,11 +1125,37 @@ func debugRestoreState(basePath:String, name: String) { private let sharedQueue = Queue(name: "org.telegram.postbox.Postbox") -public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32, isTemporary: Bool, isReadOnly: Bool) -> Signal { +public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32, isTemporary: Bool, isReadOnly: Bool, useCopy: Bool) -> Signal { let queue = sharedQueue return Signal { subscriber in queue.async { let _ = try? FileManager.default.createDirectory(atPath: basePath, withIntermediateDirectories: true, attributes: nil) + + var tempDir: TempBoxDirectory? + let dbBasePath: String + if useCopy { + let directory = TempBox.shared.tempDirectory() + tempDir = directory + + let originalBasePath = basePath + "/db" + if let originalFiles = try? FileManager.default.contentsOfDirectory(atPath: originalBasePath) { + do { + for fileName in originalFiles { + try FileManager.default.copyItem(atPath: originalBasePath + "/" + fileName, toPath: directory.path + "/" + fileName) + } + } catch let e { + postboxLog("openPostbox useCopy error: \(e)") + subscriber.putNext(.error) + return + } + } else { + subscriber.putNext(.error) + return + } + dbBasePath = directory.path + } else { + dbBasePath = basePath + "/db" + } #if DEBUG //debugSaveState(basePath: basePath, name: "previous1") @@ -1138,7 +1164,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, let startTime = CFAbsoluteTimeGetCurrent() - guard var valueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in + guard var valueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in subscriber.putNext(.upgrading(progress)) }) else { subscriber.putNext(.error) @@ -1161,7 +1187,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, postboxLog("Version \(userVersion) is newer than supported") assertionFailure("Version \(userVersion) is newer than supported") valueBox.drop() - guard let updatedValueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in + guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in subscriber.putNext(.upgrading(progress)) }) else { subscriber.putNext(.error) @@ -1183,9 +1209,9 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, }) if let updatedPath = updatedPath { valueBox.internalClose() - let _ = try? FileManager.default.removeItem(atPath: basePath + "/db") - let _ = try? FileManager.default.moveItem(atPath: updatedPath, toPath: basePath + "/db") - guard let updatedValueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in + let _ = try? FileManager.default.removeItem(atPath: dbBasePath) + let _ = try? FileManager.default.moveItem(atPath: updatedPath, toPath: dbBasePath) + guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in subscriber.putNext(.upgrading(progress)) }) else { subscriber.putNext(.error) @@ -1199,7 +1225,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, assertionFailure("Couldn't find any upgrade for \(userVersion)") postboxLog("Couldn't find any upgrade for \(userVersion)") valueBox.drop() - guard let updatedValueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in + guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in subscriber.putNext(.upgrading(progress)) }) else { subscriber.putNext(.error) @@ -1217,7 +1243,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, let endTime = CFAbsoluteTimeGetCurrent() print("Postbox load took \((endTime - startTime) * 1000.0) ms") - subscriber.putNext(.postbox(Postbox(queue: queue, basePath: basePath, seedConfiguration: seedConfiguration, valueBox: valueBox, timestampForAbsoluteTimeBasedOperations: timestampForAbsoluteTimeBasedOperations, isTemporary: isTemporary))) + subscriber.putNext(.postbox(Postbox(queue: queue, basePath: basePath, seedConfiguration: seedConfiguration, valueBox: valueBox, timestampForAbsoluteTimeBasedOperations: timestampForAbsoluteTimeBasedOperations, isTemporary: isTemporary, tempDir: tempDir))) subscriber.putCompletion() break } @@ -1232,6 +1258,7 @@ public final class Postbox { public let seedConfiguration: SeedConfiguration private let basePath: String let valueBox: SqliteValueBox + private let tempDir: TempBoxDirectory? private let ipcNotificationsDisposable = MetaDisposable() @@ -1370,7 +1397,7 @@ public final class Postbox { var installedMessageActionsByPeerId: [PeerId: Bag<([StoreMessage], Transaction) -> Void>] = [:] - init(queue: Queue, basePath: String, seedConfiguration: SeedConfiguration, valueBox: SqliteValueBox, timestampForAbsoluteTimeBasedOperations: Int32, isTemporary: Bool) { + init(queue: Queue, basePath: String, seedConfiguration: SeedConfiguration, valueBox: SqliteValueBox, timestampForAbsoluteTimeBasedOperations: Int32, isTemporary: Bool, tempDir: TempBoxDirectory?) { assert(queue.isCurrent()) let startTime = CFAbsoluteTimeGetCurrent() @@ -1378,6 +1405,7 @@ public final class Postbox { self.queue = queue self.basePath = basePath self.seedConfiguration = seedConfiguration + self.tempDir = tempDir print("MediaBox path: \(self.basePath + "/media")") @@ -1585,7 +1613,9 @@ public final class Postbox { } deinit { - assert(true) + if let tempDir = self.tempDir { + TempBox.shared.dispose(tempDir) + } } private func takeNextViewId() -> Int { diff --git a/submodules/Postbox/Sources/TempBox.swift b/submodules/Postbox/Sources/TempBox.swift index d602c01814..2b34dcf95e 100644 --- a/submodules/Postbox/Sources/TempBox.swift +++ b/submodules/Postbox/Sources/TempBox.swift @@ -7,7 +7,11 @@ private final class TempBoxFileContext { var subscribers = Set() var path: String { - return self.directory + "/" + self.fileName + if self.fileName.isEmpty { + return self.directory + } else { + return self.directory + "/" + self.fileName + } } init(directory: String, fileName: String) { @@ -34,6 +38,18 @@ public final class TempBoxFile { } } +public final class TempBoxDirectory { + fileprivate let key: TempBoxKey + fileprivate let id: Int + public let path: String + + fileprivate init(key: TempBoxKey, id: Int, path: String) { + self.key = key + self.id = id + self.path = path + } +} + private final class TempBoxContexts { private var nextId: Int = 0 private var contexts: [TempBoxKey: TempBoxFileContext] = [:] @@ -86,6 +102,23 @@ private final class TempBoxContexts { return TempBoxFile(key: key, id: id, path: context.path) } + func tempDirectory(basePath: String) -> TempBoxDirectory { + let id = self.nextId + self.nextId += 1 + + let key = TempBoxKey(path: nil, fileName: "", uniqueId: id) + let context: TempBoxFileContext + + let dirName = "\(id)" + let dirPath = basePath + "/" + dirName + context = TempBoxFileContext(directory: dirPath, fileName: "") + self.contexts[key] = context + let _ = try? FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) + + context.subscribers.insert(id) + return TempBoxDirectory(key: key, id: id, path: context.path) + } + func dispose(_ file: TempBoxFile) -> [String] { if let context = self.contexts[file.key] { context.subscribers.remove(file.id) @@ -96,6 +129,17 @@ private final class TempBoxContexts { } return [] } + + func dispose(_ directory: TempBoxDirectory) -> [String] { + if let context = self.contexts[directory.key] { + context.subscribers.remove(directory.id) + if context.subscribers.isEmpty { + self.contexts.removeValue(forKey: directory.key) + return [context.directory] + } + } + return [] + } } private var sharedValue: TempBox? @@ -150,6 +194,12 @@ public final class TempBox { } } + public func tempDirectory() -> TempBoxDirectory { + return self.contexts.with { contexts in + return contexts.tempDirectory(basePath: self.currentBasePath) + } + } + public func dispose(_ file: TempBoxFile) { let removePaths = self.contexts.with { contexts in return contexts.dispose(file) @@ -162,4 +212,17 @@ public final class TempBox { } } } + + public func dispose(_ directory: TempBoxDirectory) { + let removePaths = self.contexts.with { contexts in + return contexts.dispose(directory) + } + if !removePaths.isEmpty { + DispatchQueue.global(qos: .background).async { + for path in removePaths { + let _ = try? FileManager.default.removeItem(atPath: path) + } + } + } + } } diff --git a/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift b/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift index 970aacfee2..843c08ddea 100644 --- a/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift +++ b/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift @@ -73,9 +73,9 @@ public enum AccountTransactionError { case couldNotOpen } -public func accountTransaction(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, isReadOnly: Bool, transaction: @escaping (Postbox, Transaction) -> T) -> Signal { +public func accountTransaction(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, isReadOnly: Bool, useCopy: Bool = false, transaction: @escaping (Postbox, Transaction) -> T) -> Signal { let path = "\(rootPath)/\(accountRecordIdPathName(id))" - let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: isReadOnly) + let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: isReadOnly, useCopy: useCopy) return postbox |> castError(AccountTransactionError.self) |> mapToSignal { value -> Signal in diff --git a/submodules/TelegramCore/Sources/Account.swift b/submodules/TelegramCore/Sources/Account.swift index 617b021a97..11fccf62f9 100644 --- a/submodules/TelegramCore/Sources/Account.swift +++ b/submodules/TelegramCore/Sources/Account.swift @@ -160,7 +160,7 @@ public enum AccountPreferenceEntriesResult { public func accountPreferenceEntries(rootPath: String, id: AccountRecordId, keys: Set, encryptionParameters: ValueBoxEncryptionParameters) -> Signal { let path = "\(rootPath)/\(accountRecordIdPathName(id))" - let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true) + let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true, useCopy: false) return postbox |> mapToSignal { value -> Signal in switch value { @@ -189,7 +189,7 @@ public enum AccountNoticeEntriesResult { public func accountNoticeEntries(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal { let path = "\(rootPath)/\(accountRecordIdPathName(id))" - let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true) + let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true, useCopy: false) return postbox |> mapToSignal { value -> Signal in switch value { @@ -212,7 +212,7 @@ public enum LegacyAccessChallengeDataResult { public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal { let path = "\(rootPath)/\(accountRecordIdPathName(id))" - let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true) + let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true, useCopy: false) return postbox |> mapToSignal { value -> Signal in switch value { @@ -231,7 +231,7 @@ public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecord public func accountWithId(accountManager: AccountManager, networkArguments: NetworkInitializationArguments, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, supplementary: Bool, rootPath: String, beginWithTestingEnvironment: Bool, backupData: AccountBackupData?, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal { let path = "\(rootPath)/\(accountRecordIdPathName(id))" - let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: false, isReadOnly: false) + let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: false, isReadOnly: false, useCopy: false) return postbox |> mapToSignal { result -> Signal in diff --git a/submodules/TelegramCore/Sources/AccountManager.swift b/submodules/TelegramCore/Sources/AccountManager.swift index 9db982445a..63bd006448 100644 --- a/submodules/TelegramCore/Sources/AccountManager.swift +++ b/submodules/TelegramCore/Sources/AccountManager.swift @@ -214,7 +214,7 @@ public func temporaryAccount(manager: AccountManager, rootPath: String, encrypti return manager.allocatedTemporaryAccountId() |> mapToSignal { id -> Signal in let path = "\(rootPath)/\(accountRecordIdPathName(id))" - return openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: false, isReadOnly: false) + return openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: false, isReadOnly: false, useCopy: false) |> mapToSignal { result -> Signal in switch result { case .upgrading: