import PromiseKit import SignalMetadataKit internal class LokiFileServerProxy : LokiHTTPClient { private let server: String private let keyPair = Curve25519.generateKeyPair() private static let fileServerPublicKey: Data = { let base64EncodedPublicKey = "BWJQnVm97sQE3Q1InB4Vuo+U/T1hmwHBv0ipkiv8tzEc" let publicKeyWithPrefix = Data(base64Encoded: base64EncodedPublicKey)! let hexEncodedPublicKeyWithPrefix = publicKeyWithPrefix.toHexString() let hexEncodedPublicKey = hexEncodedPublicKeyWithPrefix.removing05PrefixIfNeeded() return Data(hex: hexEncodedPublicKey) }() // MARK: Error internal enum Error : LocalizedError { case symmetricKeyGenerationFailed case endpointParsingFailed case proxyResponseParsingFailed case fileServerHTTPError(code: Int, message: Any?) internal var errorDescription: String? { switch self { case .symmetricKeyGenerationFailed: return "Couldn't generate symmetric key." case .endpointParsingFailed: return "Couldn't parse endpoint." case .proxyResponseParsingFailed: return "Couldn't parse file server proxy response." case .fileServerHTTPError(let httpStatusCode, let message): return "File server returned \(httpStatusCode) with description: \(message ?? "no description provided.")." } } } // MARK: Initialization internal init(for server: String) { self.server = server super.init() } // MARK: Proxying override internal func perform(_ request: TSRequest, withCompletionQueue queue: DispatchQueue = DispatchQueue.main) -> LokiAPI.RawResponsePromise { let isLokiFileServer = (server == LokiFileServerAPI.server) guard isLokiFileServer else { return super.perform(request, withCompletionQueue: queue) } // Don't proxy open group requests for now return performLokiFileServerNSURLRequest(request, withCompletionQueue: queue) } internal func performLokiFileServerNSURLRequest(_ request: NSURLRequest, withCompletionQueue queue: DispatchQueue = DispatchQueue.main) -> LokiAPI.RawResponsePromise { var headers = getCanonicalHeaders(for: request) return Promise { [server = self.server, keyPair = self.keyPair, httpSession = self.httpSession] seal in DispatchQueue.global(qos: .userInitiated).async { let uncheckedSymmetricKey = try? Curve25519.generateSharedSecret(fromPublicKey: LokiFileServerProxy.fileServerPublicKey, privateKey: keyPair.privateKey) guard let symmetricKey = uncheckedSymmetricKey else { return seal.reject(Error.symmetricKeyGenerationFailed) } LokiAPI.getFileServerProxy().then(on: DispatchQueue.global()) { proxy -> Promise in let url = "\(proxy.address):\(proxy.port)/file_proxy" guard let urlAsString = request.url?.absoluteString, let serverURLEndIndex = urlAsString.range(of: server)?.upperBound, serverURLEndIndex < urlAsString.endIndex else { throw Error.endpointParsingFailed } let endpointStartIndex = urlAsString.index(after: serverURLEndIndex) let endpoint = String(urlAsString[endpointStartIndex..