import Foundation import MtProtoKit import TelegramApi private let apiPrefix: String = { let type = _typeName(Api.User.self) let userType = "User" if type.hasSuffix(userType) { return String(type[type.startIndex ..< type.index(type.endIndex, offsetBy: -userType.count)]) } else { return "TelegramApi.Api." } }() private let apiPrefixLength = apiPrefix.count private let redactChildrenOfType: [String: Set] = [ "Message.message": Set(["message"]), "Updates.updateShortMessage": Set(["message"]), "Updates.updateShortChatMessage": Set(["message"]), "BotInlineMessage.botInlineMessageText": Set(["message"]), "DraftMessage.draftMessage": Set(["message"]), "InputSingleMedia.inputSingleMedia": Set(["message"]), "InputContact.inputPhoneContact": Set(["phone"]), "User.user": Set(["phone"]), "Update.updateUserPhone": Set(["phone"]) ] private let redactFunctionParameters: [String: Set] = [ "messages.sendMessage": Set(["message"]), "messages.sendMedia": Set(["message"]), "messages.saveDraft": Set(["message"]), "messages.getWebPagePreview": Set(["message"]) ] func apiFunctionDescription(of desc: FunctionDescription) -> String { var result = desc.name if !desc.parameters.isEmpty { result.append("(") var first = true for param in desc.parameters { if first { first = false } else { result.append(", ") } result.append(param.0) result.append(": ") var redactParam = false if let redactParams = redactFunctionParameters[desc.name] { redactParam = redactParams.contains(param.0) } if redactParam, Logger.shared.redactSensitiveData { result.append("[[redacted]]") } else { result.append(recursiveDescription(redact: Logger.shared.redactSensitiveData, of: param.1)) } } result.append(")") } return result } func apiShortFunctionDescription(of desc: FunctionDescription) -> String { return desc.name } private func recursiveDescription(redact: Bool, of value: Any) -> String { let mirror = Mirror(reflecting: value) var result = "" if let displayStyle = mirror.displayStyle { switch displayStyle { case .enum: result.append(_typeName(mirror.subjectType)) if result.hasPrefix(apiPrefix) { result.removeSubrange(result.startIndex ..< result.index(result.startIndex, offsetBy: apiPrefixLength)) } if let value = value as? TypeConstructorDescription { let (consName, fields) = value.descriptionFields() result.append(".") result.append(consName) let redactChildren: Set? if redact { redactChildren = redactChildrenOfType[result] } else { redactChildren = nil } if !fields.isEmpty { result.append("(") var first = true for (fieldName, fieldValue) in fields { if first { first = false } else { result.append(", ") } var redactValue: Bool = false if let redactChildren = redactChildren, redactChildren.contains("*") { redactValue = true } result.append(fieldName) result.append(": ") if let redactChildren = redactChildren, redactChildren.contains(fieldName) { redactValue = true } if redactValue { result.append("[[redacted]]") } else { result.append(recursiveDescription(redact: redact, of: fieldValue)) } } result.append(")") } } else { inner: for child in mirror.children { if let label = child.label { result.append(".") result.append(label) } let redactChildren: Set? if redact { redactChildren = redactChildrenOfType[result] } else { redactChildren = nil } let valueMirror = Mirror(reflecting: child.value) if let displayStyle = valueMirror.displayStyle { switch displayStyle { case .tuple: var hadChildren = false for child in valueMirror.children { if !hadChildren { hadChildren = true result.append("(") } else { result.append(", ") } var redactValue: Bool = false if let redactChildren = redactChildren, redactChildren.contains("*") { redactValue = true } if let label = child.label { result.append(label) result.append(": ") if let redactChildren = redactChildren, redactChildren.contains(label) { redactValue = true } } if redactValue { result.append("[[redacted]]") } else { result.append(recursiveDescription(redact: redact, of: child.value)) } } if hadChildren { result.append(")") } default: break } } else { result.append("(") result.append(String(describing: child.value)) result.append(")") } break } } case .collection: result.append("[") var isFirst = true for child in mirror.children { if isFirst { isFirst = false } else { result.append(", ") } result.append(recursiveDescription(redact: redact, of: child.value)) } result.append("]") default: result.append("\(value)") } } else { result.append("\(value)") } return result } public class BoxedMessage: NSObject { public let body: Any public init(_ body: Any) { self.body = body } override public var description: String { get { return recursiveDescription(redact: Logger.shared.redactSensitiveData, of: self.body) } } } public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { return 125 } public func parseMessage(_ data: Data!) -> Any! { if let body = Api.parse(Buffer(data: data)) { return BoxedMessage(body) } return nil } public func exportAuthorization(_ datacenterId: Int32, data: AutoreleasingUnsafeMutablePointer) -> MTExportAuthorizationResponseParser! { let functionContext = Api.functions.auth.exportAuthorization(dcId: datacenterId) data.pointee = functionContext.1.makeData() as NSData return { data -> MTExportedAuthorizationData? in if let exported = functionContext.2.parse(Buffer(data: data)) { switch exported { case let .exportedAuthorization(id, bytes): return MTExportedAuthorizationData(authorizationBytes: bytes.makeData(), authorizationId: id) } } else { return nil } } } public func importAuthorization(_ authId: Int32, bytes: Data!) -> Data! { return Api.functions.auth.importAuthorization(id: authId, bytes: Buffer(data: bytes)).1.makeData() } public func requestDatacenterAddress(with data: AutoreleasingUnsafeMutablePointer) -> MTRequestDatacenterAddressListParser! { let (_, buffer, parser) = Api.functions.help.getConfig() data.pointee = buffer.makeData() as NSData return { response -> MTDatacenterAddressListData? in if let config = parser.parse(Buffer(data: response)) { switch config { case let .config(config): var addressDict: [NSNumber: [Any]] = [:] for option in config.dcOptions { switch option { case let .dcOption(flags, id, ipAddress, port, secret): if addressDict[id as NSNumber] == nil { addressDict[id as NSNumber] = [] } let preferForMedia = (flags & (1 << 1)) != 0 let restrictToTcp = (flags & (1 << 2)) != 0 let isCdn = (flags & (1 << 3)) != 0 let preferForProxy = (flags & (1 << 4)) != 0 addressDict[id as NSNumber]!.append(MTDatacenterAddress(ip: ipAddress, port: UInt16(port), preferForMedia: preferForMedia, restrictToTcp: restrictToTcp, cdn: isCdn, preferForProxy: preferForProxy, secret: secret?.makeData())!) break } } return MTDatacenterAddressListData(addressList: addressDict) } } return nil } } public func requestNoop(_ data: AutoreleasingUnsafeMutablePointer!) -> MTRequestNoopParser! { let (_, buffer, parser) = Api.functions.help.test() data.pointee = buffer.makeData() as NSData return { response -> AnyObject? in if let _ = parser.parse(Buffer(data: response)) { return true as NSNumber } else { return nil } } } }