mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
438 lines
13 KiB
Swift
438 lines
13 KiB
Swift
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 += "<null>"
|
|
}
|
|
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<R>(_ 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(proxyConnection: AccountProxyConnection?, account: StoredAccountInfo, 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")
|
|
|
|
if let proxy = proxyConnection {
|
|
apiEnvironment = apiEnvironment.withUpdatedSocksProxySettings(MTSocksProxySettings(ip: proxy.host, port: UInt16(clamping: proxy.port), username: proxy.username, password: proxy.password, secret: proxy.secret))
|
|
}
|
|
|
|
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
|
|
|
|
context.performBatchUpdates({
|
|
for (id, info) in account.datacenters {
|
|
if !info.addressList.isEmpty {
|
|
var addressList: [MTDatacenterAddress] = []
|
|
for address in info.addressList {
|
|
addressList.append(MTDatacenterAddress(ip: address.host, port: UInt16(clamping: address.port), preferForMedia: address.isMedia, restrictToTcp: false, cdn: false, preferForProxy: false, secret: address.secret))
|
|
}
|
|
context.updateAddressSetForDatacenter(withId: Int(id), addressSet: MTDatacenterAddressSet(addressList: addressList), forceUpdateSchemes: true)
|
|
}
|
|
}
|
|
})
|
|
|
|
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()
|
|
}
|
|
}
|