diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index 29ad6db9ad..59041f3566 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -174,6 +174,8 @@ D0448CA31E291B14005A61A7 /* FetchSecretFileResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0448CA11E291B14005A61A7 /* FetchSecretFileResource.swift */; }; D0448CA51E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0448CA41E29215A005A61A7 /* MediaResourceApiUtils.swift */; }; D0448CA61E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0448CA41E29215A005A61A7 /* MediaResourceApiUtils.swift */; }; + D0458C881E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0458C871E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift */; }; + D0458C891E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0458C871E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift */; }; D049EAD51E43D98500A2CD3A /* RecentMediaItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D049EAD41E43D98500A2CD3A /* RecentMediaItem.swift */; }; D049EAD61E43D98500A2CD3A /* RecentMediaItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D049EAD41E43D98500A2CD3A /* RecentMediaItem.swift */; }; D049EAD81E43DAD200A2CD3A /* ManagedRecentStickers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D049EAD71E43DAD200A2CD3A /* ManagedRecentStickers.swift */; }; @@ -546,6 +548,7 @@ D0448C9E1E27F5EB005A61A7 /* Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = ""; }; D0448CA11E291B14005A61A7 /* FetchSecretFileResource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchSecretFileResource.swift; sourceTree = ""; }; D0448CA41E29215A005A61A7 /* MediaResourceApiUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaResourceApiUtils.swift; sourceTree = ""; }; + D0458C871E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingContentInfoMessageAttribute.swift; sourceTree = ""; }; D049EAD41E43D98500A2CD3A /* RecentMediaItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentMediaItem.swift; sourceTree = ""; }; D049EAD71E43DAD200A2CD3A /* ManagedRecentStickers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedRecentStickers.swift; sourceTree = ""; }; D049EAE71E44B67100A2CD3A /* RecentPeerItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentPeerItem.swift; sourceTree = ""; }; @@ -858,6 +861,7 @@ D0F7AB2B1DCE889D009AD9A1 /* EditedMessageAttribute.swift */, D0F7AB2E1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift */, D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */, + D0458C871E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift */, ); name = Attributes; sourceTree = ""; @@ -1351,6 +1355,7 @@ D033FEB01E61EB0200644997 /* PeerReportStatus.swift in Sources */, D050F2511E4A59C200988324 /* JoinLink.swift in Sources */, D07827C91E02F59C00071108 /* InstantPage.swift in Sources */, + D0458C881E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift in Sources */, D07827CB1E02F5B200071108 /* RichText.swift in Sources */, D0613FD71E606B3B00202CDB /* ConvertGroupToSupergroup.swift in Sources */, D03B0CE01D62249100955575 /* StoreMessage_Telegram.swift in Sources */, @@ -1555,6 +1560,7 @@ D050F26A1E4A5B6D00988324 /* ManagedGlobalNotificationSettings.swift in Sources */, D050F26B1E4A5B6D00988324 /* ApplyMaxReadIndexInteractively.swift in Sources */, D033FEB11E61EB0200644997 /* PeerReportStatus.swift in Sources */, + D0458C891E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift in Sources */, D050F26C1E4A5B6D00988324 /* UpdatePeers.swift in Sources */, D050F26D1E4A5B6D00988324 /* CreateGroup.swift in Sources */, D050F26E1E4A5B6D00988324 /* RemovePeerChat.swift in Sources */, diff --git a/TelegramCore/Account.swift b/TelegramCore/Account.swift index d5b768634d..28fc2ee60f 100644 --- a/TelegramCore/Account.swift +++ b/TelegramCore/Account.swift @@ -210,6 +210,8 @@ private var declaredEncodables: Void = { declareEncodable(RecentMediaItem.self, f: { RecentMediaItem(decoder: $0) }) declareEncodable(RecentPeerItem.self, f: { RecentPeerItem(decoder: $0) }) declareEncodable(LoggedOutAccountAttribute.self, f: { LoggedOutAccountAttribute(decoder: $0) }) + declareEncodable(CloudChatClearHistoryOperation.self, f: { CloudChatClearHistoryOperation(decoder: $0) }) + declareEncodable(OutgoingContentInfoMessageAttribute.self, f: { OutgoingContentInfoMessageAttribute(decoder: $0) }) return }() diff --git a/TelegramCore/Log.swift b/TelegramCore/Log.swift index 0cd708ba9d..57e7398bdc 100644 --- a/TelegramCore/Log.swift +++ b/TelegramCore/Log.swift @@ -20,7 +20,7 @@ public func trace2(_ what: @autoclosure() -> String) { let milliseconds = curTime.tv_usec / 1000 //queue.async { - let result = String(format: "%d-%d-%d %02d:%02d:%03d %@", arguments: [Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_yday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(milliseconds), string]) + let result = String(format: "%d-%d-%d %02d:%02d:%03d %@", arguments: [Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_mday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(milliseconds), string]) print(result) //} } @@ -38,7 +38,7 @@ public func trace1(_ domain: String, what: @autoclosure() -> String) { let milliseconds = curTime.tv_usec / 1000 queue.async { - let result = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [domain, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_yday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(seconds), Int(milliseconds), string]) + let result = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [domain, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_mday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(seconds), Int(milliseconds), string]) print(result) } @@ -67,6 +67,9 @@ public final class Logger { private let basePath: String private var file: (Int32, Int)? + public var logToFile: Bool = true + public var logToConsole: Bool = true + public static func setSharedLogger(_ logger: Logger) { sharedLogger = logger } @@ -99,6 +102,10 @@ public final class Logger { } public func log(_ tag: String, _ what: @autoclosure () -> String) { + if !self.logToFile && !self.logToConsole { + return + } + let string = what() var rawTime = time_t() @@ -110,93 +117,99 @@ public final class Logger { gettimeofday(&curTime, nil) let milliseconds = curTime.tv_usec / 1000 - #if TARGET_IPHONE_SIMULATOR || DEBUG - let content = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [tag, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_yday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds), string]) + var consoleContent: String? + if self.logToConsole { + let content = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [tag, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_mday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds), string]) + consoleContent = content + print(content) + } - print(content) - #endif - - self.queue.async { - #if !(TARGET_IPHONE_SIMULATOR || DEBUG) - let content = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [tag, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_yday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds), string]) - #endif - - var fd: Int32? - var openNew = false - if let (file, length) = self.file { - if length >= self.maxLength { - close(file) - openNew = true + if self.logToFile { + self.queue.async { + let content: String + if let consoleContent = consoleContent { + content = consoleContent } else { - fd = file + content = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [tag, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_mday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds), string]) } - } else { - openNew = true - } - if openNew { - let _ = try? FileManager.default.createDirectory(atPath: self.basePath, withIntermediateDirectories: true, attributes: nil) - var createNew = false - if let files = try? FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: self.basePath), includingPropertiesForKeys: [URLResourceKey.creationDateKey], options: []) { - var minCreationDate: (Date, URL)? - var maxCreationDate: (Date, URL)? - var count = 0 - for url in files { - if url.lastPathComponent.hasPrefix("log-") { - if let values = try? url.resourceValues(forKeys: Set([URLResourceKey.creationDateKey])), let creationDate = values.creationDate { - count += 1 - if minCreationDate == nil || minCreationDate!.0 > creationDate { - minCreationDate = (creationDate, url) - } - if maxCreationDate == nil || maxCreationDate!.0 < creationDate { - maxCreationDate = (creationDate, url) + var fd: Int32? + var openNew = false + if let (file, length) = self.file { + if length >= self.maxLength { + close(file) + openNew = true + } else { + fd = file + } + } else { + openNew = true + } + if openNew { + let _ = try? FileManager.default.createDirectory(atPath: self.basePath, withIntermediateDirectories: true, attributes: nil) + + var createNew = false + if let files = try? FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: self.basePath), includingPropertiesForKeys: [URLResourceKey.creationDateKey], options: []) { + var minCreationDate: (Date, URL)? + var maxCreationDate: (Date, URL)? + var count = 0 + for url in files { + if url.lastPathComponent.hasPrefix("log-") { + if let values = try? url.resourceValues(forKeys: Set([URLResourceKey.creationDateKey])), let creationDate = values.creationDate { + count += 1 + if minCreationDate == nil || minCreationDate!.0 > creationDate { + minCreationDate = (creationDate, url) + } + if maxCreationDate == nil || maxCreationDate!.0 < creationDate { + maxCreationDate = (creationDate, url) + } } } } - } - if let (_, url) = minCreationDate, count >= self.maxFiles { - let _ = try? FileManager.default.removeItem(at: url) - } - if let (_, url) = maxCreationDate { - var value = stat() - if stat(url.path, &value) == 0 && Int(value.st_size) < self.maxLength { - let handle = open(url.path, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR) - if handle >= 0 { - fd = handle - self.file = (handle, Int(value.st_size)) + if let (_, url) = minCreationDate, count >= self.maxFiles { + let _ = try? FileManager.default.removeItem(at: url) + } + if let (_, url) = maxCreationDate { + var value = stat() + if stat(url.path, &value) == 0 && Int(value.st_size) < self.maxLength { + let handle = open(url.path, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR) + if handle >= 0 { + fd = handle + self.file = (handle, Int(value.st_size)) + } + } else { + createNew = true } } else { createNew = true } - } else { - createNew = true + } + + if createNew { + let fileName = String(format: "log-%d-%d-%d_%02d-%02d-%02d.%03d.txt", arguments: [Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_mday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds)]) + + let path = self.basePath + "/" + fileName + + let handle = open(path, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR) + if handle >= 0 { + fd = handle + self.file = (handle, 0) + } } } - if createNew { - let fileName = String(format: "log-%d-%d-%d_%02d-%02d-%02d.%03d.txt", arguments: [Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_yday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds)]) - - let path = self.basePath + "/" + fileName - - let handle = open(path, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR) - if handle >= 0 { - fd = handle - self.file = (handle, 0) - } - } - } - - if let fd = fd { - if let data = content.data(using: .utf8) { - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - write(fd, bytes, data.count) - } - var newline: UInt8 = 0x0a - write(fd, &newline, 1) - if let file = self.file { - self.file = (file.0, file.1 + data.count) - } else { - assertionFailure() + if let fd = fd { + if let data = content.data(using: .utf8) { + data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + write(fd, bytes, data.count) + } + var newline: UInt8 = 0x0a + write(fd, &newline, 1) + if let file = self.file { + self.file = (file.0, file.1 + data.count) + } else { + assertionFailure() + } } } } diff --git a/TelegramCore/ManagedCloudChatRemoveMessagesOperations.swift b/TelegramCore/ManagedCloudChatRemoveMessagesOperations.swift index 572cb0ea3b..b9b33a338d 100644 --- a/TelegramCore/ManagedCloudChatRemoveMessagesOperations.swift +++ b/TelegramCore/ManagedCloudChatRemoveMessagesOperations.swift @@ -57,7 +57,7 @@ private func withTakenOperation(postbox: Postbox, peerId: PeerId, tagLocalIndex: return postbox.modify { modifier -> Signal in var result: PeerMergedOperationLogEntry? modifier.operationLogUpdateEntry(peerId: peerId, tag: OperationLogTags.CloudChatRemoveMessages, tagLocalIndex: tagLocalIndex, { entry in - if let entry = entry, let _ = entry.mergedIndex, (entry.contents is CloudChatRemoveMessagesOperation || entry.contents is CloudChatRemoveChatOperation) { + if let entry = entry, let _ = entry.mergedIndex, (entry.contents is CloudChatRemoveMessagesOperation || entry.contents is CloudChatRemoveChatOperation || entry.contents is CloudChatClearHistoryOperation) { result = entry.mergedEntry! return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none) } else { @@ -213,44 +213,18 @@ private func removeChat(modifier: Modifier, postbox: Postbox, network: Network, } let deleteMessages: Signal if let inputPeer = apiInputPeer(peer), let topMessageId = modifier.getTopPeerMessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud) { - deleteMessages = network.request(Api.functions.messages.deleteHistory(flags: 0, peer: inputPeer, maxId: topMessageId.id)) - |> map { result -> Api.messages.AffectedHistory? in - return result - } - |> `catch` { _ in - return .single(nil) - } - |> mapToSignal { result in - if let result = result { - switch result { - case let .affectedHistory(pts, ptsCount, _): - stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) - } - } - return .complete() - } + deleteMessages = requestClearHistory(postbox: postbox, network: network, stateManager: stateManager, inputPeer: inputPeer, maxId: topMessageId.id, justClear: false) } else { deleteMessages = .complete() } - return deleteMessages |> then(deleteUser) + return deleteMessages |> then(deleteUser) |> then(postbox.modify { modifier -> Void in + modifier.clearHistory(peer.id) + }) } else if peer.id.namespace == Namespaces.Peer.CloudUser { if let inputPeer = apiInputPeer(peer), let topMessageId = modifier.getTopPeerMessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud) { - return network.request(Api.functions.messages.deleteHistory(flags: 0, peer: inputPeer, maxId: topMessageId.id)) - |> map { result -> Api.messages.AffectedHistory? in - return result - } - |> `catch` { _ in - return .single(nil) - } - |> mapToSignal { result in - if let result = result { - switch result { - case let .affectedHistory(pts, ptsCount, _): - stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) - } - } - return .complete() - } + return requestClearHistory(postbox: postbox, network: network, stateManager: stateManager, inputPeer: inputPeer, maxId: topMessageId.id, justClear: false) |> then(postbox.modify { modifier -> Void in + modifier.clearHistory(peer.id) + }) } else { return .complete() } @@ -259,25 +233,39 @@ private func removeChat(modifier: Modifier, postbox: Postbox, network: Network, } } +private func requestClearHistory(postbox: Postbox, network: Network, stateManager: AccountStateManager, inputPeer: Api.InputPeer, maxId: Int32, justClear: Bool) -> Signal { + let signal = network.request(Api.functions.messages.deleteHistory(flags: justClear ? 1 : 0, peer: inputPeer, maxId: maxId)) + |> map { result -> Api.messages.AffectedHistory? in + return result + } + |> `catch` { _ -> Signal in + return .fail(true) + } + |> mapToSignal { result -> Signal in + if let result = result { + switch result { + case let .affectedHistory(pts, ptsCount, offset): + stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) + if offset == 0 { + return .fail(true) + } else { + return .complete() + } + } + } else { + return .fail(true) + } + } + return (signal |> restart) + |> `catch` { _ -> Signal in + return .complete() + } +} + private func clearHistory(modifier: Modifier, postbox: Postbox, network: Network, stateManager: AccountStateManager, peer: Peer, operation: CloudChatClearHistoryOperation) -> Signal { if peer.id.namespace == Namespaces.Peer.CloudGroup || peer.id.namespace == Namespaces.Peer.CloudUser { if let inputPeer = apiInputPeer(peer) { - return network.request(Api.functions.messages.deleteHistory(flags: 0, peer: inputPeer, maxId: operation.topMessageId.id)) - |> map { result -> Api.messages.AffectedHistory? in - return result - } - |> `catch` { _ in - return .single(nil) - } - |> mapToSignal { result in - if let result = result { - switch result { - case let .affectedHistory(pts, ptsCount, _): - stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) - } - } - return .complete() - } + return requestClearHistory(postbox: postbox, network: network, stateManager: stateManager, inputPeer: inputPeer, maxId: operation.topMessageId.id, justClear: true) } else { return .complete() } diff --git a/TelegramCore/OutgoingContentInfoMessageAttribute.swift b/TelegramCore/OutgoingContentInfoMessageAttribute.swift new file mode 100644 index 0000000000..460a884f77 --- /dev/null +++ b/TelegramCore/OutgoingContentInfoMessageAttribute.swift @@ -0,0 +1,40 @@ +import Foundation +#if os(macOS) + import PostboxMac +#else + import Postbox +#endif + +public struct OutgoingContentInfoFlags: OptionSet { + public var rawValue: Int32 + + public init() { + self.rawValue = 0 + } + + public init(rawValue: Int32) { + self.rawValue = rawValue + } + + public static let disableLinkPreviews = OutgoingContentInfoFlags(rawValue: 1 << 0) +} + +public class OutgoingContentInfoMessageAttribute: MessageAttribute { + public let flags: OutgoingContentInfoFlags + + init(flags: OutgoingContentInfoFlags) { + self.flags = flags + } + + required public init(decoder: Decoder) { + self.flags = OutgoingContentInfoFlags(rawValue: decoder.decodeInt32ForKey("f")) + } + + public func encode(_ encoder: Encoder) { + encoder.encodeInt32(self.flags.rawValue, forKey: "f") + } + + public func withUpdatedFlags(_ flags: OutgoingContentInfoFlags) -> OutgoingContentInfoMessageAttribute { + return OutgoingContentInfoMessageAttribute(flags: flags) + } +} diff --git a/TelegramCore/PendingMessageManager.swift b/TelegramCore/PendingMessageManager.swift index a351dcd399..5980d169e7 100644 --- a/TelegramCore/PendingMessageManager.swift +++ b/TelegramCore/PendingMessageManager.swift @@ -310,6 +310,8 @@ public final class PendingMessageManager { var messageEntities: [Api.MessageEntity]? var replyMessageId: Int32? + var flags: Int32 = 0 + for attribute in message.attributes { if let replyAttribute = attribute as? ReplyMessageAttribute { replyMessageId = replyAttribute.messageId.id @@ -319,10 +321,13 @@ public final class PendingMessageManager { forwardSourceInfoAttribute = attribute } else if let attribute = attribute as? TextEntitiesMessageAttribute { messageEntities = apiTextAttributeEntities(attribute, associatedPeers: message.peers) + } else if let attribute = attribute as? OutgoingContentInfoMessageAttribute { + if attribute.flags.contains(.disableLinkPreviews) { + flags |= Int32(1 << 1) + } } } - var flags: Int32 = 0 if let _ = replyMessageId { flags |= Int32(1 << 0) }