From 51fb4ed21d6f93a23b92f8842f798e7184466b76 Mon Sep 17 00:00:00 2001
From: Niels Andriesse
Date: Sun, 2 Feb 2020 11:33:34 +1100
Subject: [PATCH 1/8] Implement file server proxying
---
Pods | 2 +-
.../src/Loki/API/LokiDotNetAPI.swift | 5 +-
.../src/Loki/API/LokiFileServerProxy.swift | 106 ++++++++++++++++++
.../src/Loki/API/LokiHttpClient.swift | 88 +++++++++------
.../src/Loki/API/LokiSnodeProxy.swift | 40 ++-----
.../API/Public Chat/LokiPublicChatAPI.swift | 30 ++---
.../Public Chat/LokiPublicChatManager.swift | 2 +
.../Public Chat/LokiPublicChatMessage.swift | 2 -
...e+Hashable.swift => Promise+Hashing.swift} | 0
9 files changed, 191 insertions(+), 84 deletions(-)
create mode 100644 SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
rename SignalServiceKit/src/Loki/Utilities/{Promise+Hashable.swift => Promise+Hashing.swift} (100%)
diff --git a/Pods b/Pods
index d7ebdadef..9837a2fa9 160000
--- a/Pods
+++ b/Pods
@@ -1 +1 @@
-Subproject commit d7ebdadef0ed419082789e6a1f65a75ff791996e
+Subproject commit 9837a2fa9828fcd261233cff21dc688dc308c6b8
diff --git a/SignalServiceKit/src/Loki/API/LokiDotNetAPI.swift b/SignalServiceKit/src/Loki/API/LokiDotNetAPI.swift
index 3e8c23337..1b1fc4492 100644
--- a/SignalServiceKit/src/Loki/API/LokiDotNetAPI.swift
+++ b/SignalServiceKit/src/Loki/API/LokiDotNetAPI.swift
@@ -1,5 +1,6 @@
import PromiseKit
+/// Base class for `LokiStorageAPI` and `LokiPublicChatAPI`.
public class LokiDotNetAPI : NSObject {
// MARK: Convenience
@@ -129,7 +130,7 @@ public class LokiDotNetAPI : NSObject {
let queryParameters = "pubKey=\(userHexEncodedPublicKey)"
let url = URL(string: "\(server)/loki/v1/get_challenge?\(queryParameters)")!
let request = TSRequest(url: url)
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { $0.responseObject }.map { rawResponse in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { rawResponse in
guard let json = rawResponse as? JSON, let base64EncodedChallenge = json["cipherText64"] as? String, let base64EncodedServerPublicKey = json["serverPubKey64"] as? String,
let challenge = Data(base64Encoded: base64EncodedChallenge), var serverPublicKey = Data(base64Encoded: base64EncodedServerPublicKey) else {
throw Error.parsingFailed
@@ -153,7 +154,7 @@ public class LokiDotNetAPI : NSObject {
let url = URL(string: "\(server)/loki/v1/submit_challenge")!
let parameters = [ "pubKey" : userHexEncodedPublicKey, "token" : token ]
let request = TSRequest(url: url, method: "POST", parameters: parameters)
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { _ in token }
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { _ in token }
}
// MARK: Attachments (Public Obj-C API)
diff --git a/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift b/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
new file mode 100644
index 000000000..de0d6cfec
--- /dev/null
+++ b/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
@@ -0,0 +1,106 @@
+import PromiseKit
+
+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 proxy response."
+ case .fileServerHTTPError(let httpStatusCode, let message): return "File server returned error \(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 uncheckedSymmetricKey = try? Curve25519.generateSharedSecret(fromPublicKey: LokiFileServerProxy.fileServerPublicKey, privateKey: keyPair.privateKey)
+ guard let symmetricKey = uncheckedSymmetricKey else { return Promise(error: Error.symmetricKeyGenerationFailed) }
+ let headers = getCanonicalHeaders(for: request)
+ return LokiAPI.getRandomSnode().then { [server = self.server, keyPair = self.keyPair, httpSession = self.httpSession] proxy -> Promise in
+ let url = "\(proxy.address):\(proxy.port)/proxy"
+ print("[Loki] Proxying request to \(server) through \(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.. Promise in
+ print("[Loki] Proxy request failed with error: \(error.localizedDescription).")
+ throw HTTPError.from(error: error) ?? error
+ }
+ }
+}
diff --git a/SignalServiceKit/src/Loki/API/LokiHttpClient.swift b/SignalServiceKit/src/Loki/API/LokiHttpClient.swift
index 790408569..2cb9c7eb1 100644
--- a/SignalServiceKit/src/Loki/API/LokiHttpClient.swift
+++ b/SignalServiceKit/src/Loki/API/LokiHttpClient.swift
@@ -1,50 +1,74 @@
import PromiseKit
+/// Base class for `LokiSnodeProxy`, `LokiFileServerProxy` and `LokiRSSFeedProxy`.
internal class LokiHTTPClient {
-
- internal enum HTTPError: LocalizedError {
- case networkError(code: Int, response: Any?, underlyingError: Error?)
-
- public var errorDescription: String? {
- switch self {
- case .networkError(let code, let body, let underlingError): return underlingError?.localizedDescription ?? "Failed HTTP request with status code: \(code), message: \(body ?? "")."
- }
- }
- }
-
+
+ internal lazy var httpSession: AFHTTPSessionManager = {
+ let result = AFHTTPSessionManager(sessionConfiguration: .ephemeral)
+ let securityPolicy = AFSecurityPolicy.default()
+ securityPolicy.allowInvalidCertificates = true
+ securityPolicy.validatesDomainName = false
+ result.securityPolicy = securityPolicy
+ result.responseSerializer = AFHTTPResponseSerializer()
+ return result
+ }()
+
internal func perform(_ request: TSRequest, withCompletionQueue queue: DispatchQueue = DispatchQueue.main) -> LokiAPI.RawResponsePromise {
return TSNetworkManager.shared().perform(request, withCompletionQueue: queue).map { $0.responseObject }.recover { error -> LokiAPI.RawResponsePromise in
throw HTTPError.from(error: error) ?? error
}
}
+
+ internal func getCanonicalHeaders(for request: TSRequest) -> [String: Any] {
+ guard let headers = request.allHTTPHeaderFields else { return [:] }
+ return headers.mapValues { value in
+ switch value.lowercased() {
+ case "true": return true
+ case "false": return false
+ default: return value
+ }
+ }
+ }
}
-internal extension LokiHTTPClient.HTTPError {
-
- internal static func from(error: Error) -> LokiHTTPClient.HTTPError? {
- if let error = error as? NetworkManagerError {
- if case NetworkManagerError.taskError(_, let underlyingError) = error, let nsError = underlyingError as? NSError {
- var response = nsError.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]
- // Deserialize response if needed
- if let data = response as? Data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON {
- response = json
+// MARK: - HTTP Error
+
+internal extension LokiHTTPClient {
+
+ internal enum HTTPError : LocalizedError {
+ case networkError(code: Int, response: Any?, underlyingError: Error?)
+
+ internal static func from(error: Error) -> LokiHTTPClient.HTTPError? {
+ if let error = error as? NetworkManagerError {
+ if case NetworkManagerError.taskError(_, let underlyingError) = error, let nsError = underlyingError as? NSError {
+ var response = nsError.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]
+ // Deserialize response if needed
+ if let data = response as? Data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON {
+ response = json
+ }
+ return LokiHTTPClient.HTTPError.networkError(code: error.statusCode, response: response, underlyingError: underlyingError)
}
- return LokiHTTPClient.HTTPError.networkError(code: error.statusCode, response: response, underlyingError: underlyingError)
+ return LokiHTTPClient.HTTPError.networkError(code: error.statusCode, response: nil, underlyingError: error)
}
- return LokiHTTPClient.HTTPError.networkError(code: error.statusCode, response: nil, underlyingError: error)
+ return nil
}
- return nil
- }
-
- internal var isNetworkError: Bool {
- switch self {
- case .networkError(_, _, let underlyingError): return underlyingError != nil && IsNSErrorNetworkFailure(underlyingError)
+
+ public var errorDescription: String? {
+ switch self {
+ case .networkError(let code, let body, let underlyingError): return underlyingError?.localizedDescription ?? "HTTP request failed with status code: \(code), message: \(body ?? "nil")."
+ }
+ }
+
+ internal var statusCode: Int {
+ switch self {
+ case .networkError(let code, _, _): return code
+ }
}
- }
- internal var statusCode: Int {
- switch self {
- case .networkError(let code, _, _): return code
+ internal var isNetworkError: Bool {
+ switch self {
+ case .networkError(_, _, let underlyingError): return underlyingError != nil && IsNSErrorNetworkFailure(underlyingError)
+ }
}
}
}
diff --git a/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift b/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift
index 84af884ed..8fa9b1179 100644
--- a/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift
+++ b/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift
@@ -1,18 +1,8 @@
import PromiseKit
internal class LokiSnodeProxy : LokiHTTPClient {
- internal let target: LokiAPITarget
- private let keyPair: ECKeyPair
-
- private lazy var httpSession: AFHTTPSessionManager = {
- let result = AFHTTPSessionManager(sessionConfiguration: .ephemeral)
- let securityPolicy = AFSecurityPolicy.default()
- securityPolicy.allowInvalidCertificates = true
- securityPolicy.validatesDomainName = false
- result.securityPolicy = securityPolicy
- result.responseSerializer = AFHTTPResponseSerializer()
- return result
- }()
+ private let target: LokiAPITarget
+ private let keyPair = Curve25519.generateKeyPair()
// MARK: Error
internal enum Error : LocalizedError {
@@ -23,10 +13,10 @@ internal class LokiSnodeProxy : LokiHTTPClient {
internal var errorDescription: String? {
switch self {
- case .targetPublicKeySetMissing: return "Missing target public key set"
- case .symmetricKeyGenerationFailed: return "Couldn't generate symmetric key"
- case .proxyResponseParsingFailed: return "Couldn't parse proxy response"
- case .targetSnodeHTTPError(let httpStatusCode, let message): return "Target snode returned error \(httpStatusCode) with description: \(message ?? "no description provided")."
+ case .targetPublicKeySetMissing: return "Missing target public key set."
+ case .symmetricKeyGenerationFailed: return "Couldn't generate symmetric key."
+ case .proxyResponseParsingFailed: return "Couldn't parse proxy response."
+ case .targetSnodeHTTPError(let httpStatusCode, let message): return "Target snode returned error \(httpStatusCode) with description: \(message ?? "no description provided.")."
}
}
}
@@ -34,7 +24,6 @@ internal class LokiSnodeProxy : LokiHTTPClient {
// MARK: Initialization
internal init(for target: LokiAPITarget) {
self.target = target
- keyPair = Curve25519.generateKeyPair()
super.init()
}
@@ -48,7 +37,7 @@ internal class LokiSnodeProxy : LokiHTTPClient {
let url = "\(proxy.address):\(proxy.port)/proxy"
print("[Loki] Proxying request to \(target) through \(proxy).")
let parametersAsData = try JSONSerialization.data(withJSONObject: request.parameters, options: [])
- let proxyRequestParameters: [String:Any] = [
+ let proxyRequestParameters: JSON = [
"method" : request.httpMethod,
"body" : String(bytes: parametersAsData, encoding: .utf8),
"headers" : headers
@@ -56,7 +45,7 @@ internal class LokiSnodeProxy : LokiHTTPClient {
let proxyRequestParametersAsData = try JSONSerialization.data(withJSONObject: proxyRequestParameters, options: [])
let ivAndCipherText = try DiffieHellman.encrypt(proxyRequestParametersAsData, using: symmetricKey)
let proxyRequestHeaders = [
- "X-Sender-Public-Key" : keyPair.publicKey.map { String(format: "%02hhx", $0) }.joined(),
+ "X-Sender-Public-Key" : keyPair.publicKey.toHexString(),
"X-Target-Snode-Key" : targetHexEncodedPublicKeySet.idKey
]
let (promise, resolver) = LokiAPI.RawResponsePromise.pending()
@@ -72,7 +61,6 @@ internal class LokiSnodeProxy : LokiHTTPClient {
nsError.isRetryable = false
resolver.reject(nsError)
} else {
- OutageDetection.shared.reportConnectionSuccess()
resolver.fulfill(result)
}
}
@@ -101,16 +89,4 @@ internal class LokiSnodeProxy : LokiHTTPClient {
throw HTTPError.from(error: error) ?? error
}
}
-
- // MARK: Convenience
- private func getCanonicalHeaders(for request: TSRequest) -> [String: Any] {
- guard let headers = request.allHTTPHeaderFields else { return [:] }
- return headers.mapValues { value in
- switch value.lowercased() {
- case "true": return true
- case "false": return false
- default: return value
- }
- }
- }
}
diff --git a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift
index 6245103f2..829455a12 100644
--- a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift
+++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift
@@ -26,9 +26,9 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
}
// MARK: Database
- override internal class var authTokenCollection: String { "LokiGroupChatAuthTokenCollection" } // Should ideally be LokiPublicChatAuthTokenCollection
- @objc public static let lastMessageServerIDCollection = "LokiGroupChatLastMessageServerIDCollection" // Should ideally be LokiPublicChatLastMessageServerIDCollection
- @objc public static let lastDeletionServerIDCollection = "LokiGroupChatLastDeletionServerIDCollection" // Should ideally be LokiPublicChatLastDeletionServerIDCollection
+ override internal class var authTokenCollection: String { "LokiGroupChatAuthTokenCollection" }
+ @objc public static let lastMessageServerIDCollection = "LokiGroupChatLastMessageServerIDCollection"
+ @objc public static let lastDeletionServerIDCollection = "LokiGroupChatLastDeletionServerIDCollection"
private static func getLastMessageServerID(for group: UInt64, on server: String) -> UInt? {
var result: UInt? = nil
@@ -80,7 +80,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
}
let url = URL(string: "\(server)/channels/\(channel)/messages?\(queryParameters)")!
let request = TSRequest(url: url)
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { $0.responseObject }.map { rawResponse in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { rawResponse in
guard let json = rawResponse as? JSON, let rawMessages = json["data"] as? [JSON] else {
print("[Loki] Couldn't parse messages for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).")
throw Error.parsingFailed
@@ -156,7 +156,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
let request = TSRequest(url: url, method: "POST", parameters: parameters)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
let displayName = userDisplayName
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { $0.responseObject }.map { rawResponse in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { rawResponse in
// ISO8601DateFormatter doesn't support milliseconds before iOS 11
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
@@ -187,7 +187,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
}
let url = URL(string: "\(server)/loki/v1/channel/\(channel)/deletes?\(queryParameters)")!
let request = TSRequest(url: url)
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { $0.responseObject }.map { rawResponse in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { rawResponse in
guard let json = rawResponse as? JSON, let deletions = json["data"] as? [JSON] else {
print("[Loki] Couldn't parse deleted messages for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).")
throw Error.parsingFailed
@@ -212,7 +212,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
let url = URL(string: urlAsString)!
let request = TSRequest(url: url, method: "DELETE", parameters: [:])
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).done(on: DispatchQueue.global()) { result -> Void in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).done(on: DispatchQueue.global()) { result -> Void in
print("[Loki] Deleted message with ID: \(messageID) on server: \(server).")
}.retryingIfNeeded(maxRetryCount: maxRetryCount)
}
@@ -221,7 +221,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
public static func getModerators(for channel: UInt64, on server: String) -> Promise> {
let url = URL(string: "\(server)/loki/v1/channel/\(channel)/get_moderators")!
let request = TSRequest(url: url)
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { $0.responseObject }.map { rawResponse in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { rawResponse in
guard let json = rawResponse as? JSON, let moderators = json["moderators"] as? [String] else {
print("[Loki] Couldn't parse moderators for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).")
throw Error.parsingFailed
@@ -241,7 +241,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
let url = URL(string: "\(server)/channels/\(channel)/subscribe")!
let request = TSRequest(url: url, method: "POST", parameters: [:])
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).done(on: DispatchQueue.global()) { result -> Void in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).done(on: DispatchQueue.global()) { result -> Void in
print("[Loki] Joined channel with ID: \(channel) on server: \(server).")
}.retryingIfNeeded(maxRetryCount: maxRetryCount)
}
@@ -252,7 +252,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
let url = URL(string: "\(server)/channels/\(channel)/subscribe")!
let request = TSRequest(url: url, method: "DELETE", parameters: [:])
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).done(on: DispatchQueue.global()) { result -> Void in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).done(on: DispatchQueue.global()) { result -> Void in
print("[Loki] Left channel with ID: \(channel) on server: \(server).")
}.retryingIfNeeded(maxRetryCount: maxRetryCount)
}
@@ -264,7 +264,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
let url = URL(string: "\(server)/channels/\(channel)/subscribers?\(queryParameters)")!
let request = TSRequest(url: url)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { $0.responseObject }.map { rawResponse in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { rawResponse in
guard let json = rawResponse as? JSON, let users = json["data"] as? [JSON] else {
print("[Loki] Couldn't parse user count for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).")
throw Error.parsingFailed
@@ -288,7 +288,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
let queryParameters = "ids=\(hexEncodedPublicKeys.map { "@\($0)" }.joined(separator: ","))&include_user_annotations=1"
let url = URL(string: "\(server)/users?\(queryParameters)")!
let request = TSRequest(url: url)
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { $0.responseObject }.map { rawResponse in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { rawResponse in
guard let json = rawResponse as? JSON, let data = json["data"] as? [JSON] else {
print("[Loki] Couldn't parse display names for users: \(hexEncodedPublicKeys) from: \(rawResponse).")
throw Error.parsingFailed
@@ -318,7 +318,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
let url = URL(string: "\(server)/users/me")!
let request = TSRequest(url: url, method: "PATCH", parameters: parameters)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { _ in }.recover(on: DispatchQueue.global()) { error in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { _ in }.recover(on: DispatchQueue.global()) { error in
print("Couldn't update display name due to error: \(error).")
throw error
}
@@ -336,7 +336,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
let url = URL(string: "\(server)/users/me")!
let request = TSRequest(url: url, method: "PATCH", parameters: parameters)
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { _ in }.recover(on: DispatchQueue.global()) { error in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { _ in }.recover(on: DispatchQueue.global()) { error in
print("[Loki] Couldn't update profile picture due to error: \(error).")
throw error
}
@@ -346,7 +346,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
public static func getInfo(for channel: UInt64, on server: String) -> Promise {
let url = URL(string: "\(server)/channels/\(channel)?include_annotations=1")!
let request = TSRequest(url: url)
- return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { $0.responseObject }.map { rawResponse in
+ return LokiFileServerProxy(for: server).perform(request, withCompletionQueue: DispatchQueue.global()).map { rawResponse in
guard let json = rawResponse as? JSON,
let data = json["data"] as? JSON,
let annotations = data["annotations"] as? [JSON],
diff --git a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift
index a55a30453..aec2d96a4 100644
--- a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift
+++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift
@@ -1,5 +1,7 @@
import PromiseKit
+// TODO: Clean
+
@objc(LKPublicChatManager)
public final class LokiPublicChatManager : NSObject {
private let storage = OWSPrimaryStorage.shared()
diff --git a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatMessage.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatMessage.swift
index dd8f59d1c..d458eb5ad 100644
--- a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatMessage.swift
+++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatMessage.swift
@@ -135,11 +135,9 @@ public final class LokiPublicChatMessage : NSObject {
value["sig"] = signature.data.toHexString()
value["sigver"] = signature.version
}
-
if let avatar = avatar {
value["avatar"] = avatar;
}
-
let annotation: JSON = [ "type" : type, "value" : value ]
let attachmentAnnotations: [JSON] = attachments.map { attachment in
let type: String
diff --git a/SignalServiceKit/src/Loki/Utilities/Promise+Hashable.swift b/SignalServiceKit/src/Loki/Utilities/Promise+Hashing.swift
similarity index 100%
rename from SignalServiceKit/src/Loki/Utilities/Promise+Hashable.swift
rename to SignalServiceKit/src/Loki/Utilities/Promise+Hashing.swift
From 5eca0234222b01c703cb9a8c4a07e46e4acfdcec Mon Sep 17 00:00:00 2001
From: Niels Andriesse
Date: Sun, 2 Feb 2020 11:47:38 +1100
Subject: [PATCH 2/8] Implement RSS feed proxying
---
.../src/Loki/API/LokiHttpClient.swift | 2 +-
.../src/Loki/API/LokiRSSFeedProxy.swift | 26 +++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
create mode 100644 SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift
diff --git a/SignalServiceKit/src/Loki/API/LokiHttpClient.swift b/SignalServiceKit/src/Loki/API/LokiHttpClient.swift
index 2cb9c7eb1..ad9cae850 100644
--- a/SignalServiceKit/src/Loki/API/LokiHttpClient.swift
+++ b/SignalServiceKit/src/Loki/API/LokiHttpClient.swift
@@ -1,6 +1,6 @@
import PromiseKit
-/// Base class for `LokiSnodeProxy`, `LokiFileServerProxy` and `LokiRSSFeedProxy`.
+/// Base class for `LokiSnodeProxy` and `LokiFileServerProxy`.
internal class LokiHTTPClient {
internal lazy var httpSession: AFHTTPSessionManager = {
diff --git a/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift b/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift
new file mode 100644
index 000000000..3cf43e27c
--- /dev/null
+++ b/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift
@@ -0,0 +1,26 @@
+import PromiseKit
+
+internal enum LokiRSSFeedProxy {
+
+ internal enum Error : LocalizedError {
+ case proxyResponseParsingFailed
+
+ internal var errorDescription: String? {
+ switch self {
+ case .proxyResponseParsingFailed: return "Couldn't parse proxy response."
+ }
+ }
+ }
+
+ internal static func fetchContent(for url: String) -> Promise {
+ let server = LokiStorageAPI.server
+ let endpoints = [ "messenger-updates/feed" : "loki/v1/rss/messenger", "loki.network/feed" : "loki/v1/rss/loki" ]
+ let endpoint = endpoints.first { url.lowercased().contains($0.key) }!.value
+ let url = URL(string: server + "/" + endpoint)!
+ let request = TSRequest(url: url)
+ return LokiFileServerProxy(for: server).perform(request).map { response -> String in
+ guard let json = response as? JSON, let data = json["data"] as? String else { throw Error.proxyResponseParsingFailed }
+ return data
+ }
+ }
+}
From 740f84c466392786c5e4024b250499860964ac9d Mon Sep 17 00:00:00 2001
From: Niels Andriesse
Date: Mon, 3 Feb 2020 12:59:04 +1100
Subject: [PATCH 3/8] Debug file server proxying
---
.../src/Loki/API/LokiFileServerProxy.swift | 20 ++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift b/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
index de0d6cfec..127847955 100644
--- a/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
+++ b/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
@@ -37,35 +37,41 @@ internal class LokiFileServerProxy : LokiHTTPClient {
// MARK: Proxying
override internal func perform(_ request: TSRequest, withCompletionQueue queue: DispatchQueue = DispatchQueue.main) -> LokiAPI.RawResponsePromise {
+ let isLokiFileServer = server.contains("file.lokinet.org") || server.contains("file-dev.lokinet.org")
+ guard isLokiFileServer else { return super.perform(request, withCompletionQueue: queue) } // Don't proxy open group requests for now
let uncheckedSymmetricKey = try? Curve25519.generateSharedSecret(fromPublicKey: LokiFileServerProxy.fileServerPublicKey, privateKey: keyPair.privateKey)
guard let symmetricKey = uncheckedSymmetricKey else { return Promise(error: Error.symmetricKeyGenerationFailed) }
- let headers = getCanonicalHeaders(for: request)
+ var headers = getCanonicalHeaders(for: request)
+ headers["Content-Type"] = "application/json"
return LokiAPI.getRandomSnode().then { [server = self.server, keyPair = self.keyPair, httpSession = self.httpSession] proxy -> Promise in
- let url = "\(proxy.address):\(proxy.port)/proxy"
+ let url = "\(proxy.address):\(proxy.port)/file_proxy"
print("[Loki] Proxying request to \(server) through \(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.. Promise in
- print("[Loki] Proxy request failed with error: \(error.localizedDescription).")
+ print("[Loki] File server proxy request failed with error: \(error.localizedDescription).")
throw HTTPError.from(error: error) ?? error
}
}
From c1c04b7ef273a30976ce71c3124d1d046e6c3304 Mon Sep 17 00:00:00 2001
From: Niels Andriesse
Date: Mon, 3 Feb 2020 12:59:20 +1100
Subject: [PATCH 4/8] Remove Signal request
---
SignalServiceKit/src/Messages/UD/OWSUDManager.swift | 1 +
1 file changed, 1 insertion(+)
diff --git a/SignalServiceKit/src/Messages/UD/OWSUDManager.swift b/SignalServiceKit/src/Messages/UD/OWSUDManager.swift
index 3c1de038f..2a20ab1a3 100644
--- a/SignalServiceKit/src/Messages/UD/OWSUDManager.swift
+++ b/SignalServiceKit/src/Messages/UD/OWSUDManager.swift
@@ -408,6 +408,7 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
}
public func ensureSenderCertificate(certificateExpirationPolicy: OWSUDCertificateExpirationPolicy) -> Promise {
+ return Promise(error: "Disabled.")
// If there is a valid cached sender certificate, use that.
//
// NOTE: We use a "strict" expiration policy.
From e95f91558b02cdc7440109d56095b06089313454 Mon Sep 17 00:00:00 2001
From: Niels Andriesse
Date: Mon, 3 Feb 2020 14:15:35 +1100
Subject: [PATCH 5/8] Integrate RSS feed proxy
---
.../Loki/Messaging/LokiRSSFeedPoller.swift | 73 ++++++++++---------
.../src/Loki/API/LokiFileServerProxy.swift | 33 +++++++--
.../src/Loki/API/LokiRSSFeedProxy.swift | 12 +--
.../src/Loki/API/LokiSnodeProxy.swift | 2 +-
4 files changed, 71 insertions(+), 49 deletions(-)
diff --git a/Signal/src/Loki/Messaging/LokiRSSFeedPoller.swift b/Signal/src/Loki/Messaging/LokiRSSFeedPoller.swift
index c630a4a7d..62a0d0341 100644
--- a/Signal/src/Loki/Messaging/LokiRSSFeedPoller.swift
+++ b/Signal/src/Loki/Messaging/LokiRSSFeedPoller.swift
@@ -28,41 +28,44 @@ public final class LokiRSSFeedPoller : NSObject {
private func poll() {
let feed = self.feed
- let url = URL(string: feed.server)!
- FeedParser(URL: url).parseAsync { wrapper in
- guard case .rss(let x) = wrapper, let items = x.items else { return print("[Loki] Failed to parse RSS feed for: \(feed.server).") }
- items.reversed().forEach { item in
- guard let title = item.title, let description = item.description, let date = item.pubDate else { return }
- let timestamp = UInt64(date.timeIntervalSince1970 * 1000)
- let urlRegex = try! NSRegularExpression(pattern: "]*?\\s+)?href=\"([^\"]*)\".*?>(.*?)<.*?\\/a>")
- var bodyAsHTML = "\(title)
\(description)".replacingOccurrences(of: "
", with: "
")
- while true {
- guard let match = urlRegex.firstMatch(in: bodyAsHTML, options: [], range: NSRange(location: 0, length: bodyAsHTML.utf16.count)) else { break }
- let matchRange = match.range(at: 0)
- let urlRange = match.range(at: 1)
- let descriptionRange = match.range(at: 2)
- let url = (bodyAsHTML as NSString).substring(with: urlRange)
- let description = (bodyAsHTML as NSString).substring(with: descriptionRange)
- bodyAsHTML = (bodyAsHTML as NSString).replacingCharacters(in: matchRange, with: "\(description) (\(url))") as String
- }
- guard let bodyAsData = bodyAsHTML.data(using: String.Encoding.unicode) else { return }
- let options = [ NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html ]
- guard let body = try? NSAttributedString(data: bodyAsData, options: options, documentAttributes: nil).string else { return }
- let id = LKGroupUtilities.getEncodedRSSFeedIDAsData(feed.id)
- let groupContext = SSKProtoGroupContext.builder(id: id, type: .deliver)
- groupContext.setName(feed.displayName)
- let dataMessage = SSKProtoDataMessage.builder()
- dataMessage.setTimestamp(timestamp)
- dataMessage.setGroup(try! groupContext.build())
- dataMessage.setBody(body)
- let content = SSKProtoContent.builder()
- content.setDataMessage(try! dataMessage.build())
- let envelope = SSKProtoEnvelope.builder(type: .ciphertext, timestamp: timestamp)
- envelope.setSource(NSLocalizedString("Loki", comment: ""))
- envelope.setSourceDevice(OWSDevicePrimaryDeviceId)
- envelope.setContent(try! content.build().serializedData())
- OWSPrimaryStorage.shared().dbReadWriteConnection.readWrite { transaction in
- SSKEnvironment.shared.messageManager.throws_processEnvelope(try! envelope.build(), plaintextData: try! content.build().serializedData(), wasReceivedByUD: false, transaction: transaction, serverID: 0)
+ let url = feed.server
+ let _ = LokiRSSFeedProxy.fetchContent(for: url).done { xml in
+ guard let data = xml.data(using: String.Encoding.utf8) else { return print("[Loki] Failed to parse RSS feed for: \(feed.server).") }
+ FeedParser(data: data).parseAsync { wrapper in
+ guard case .rss(let x) = wrapper, let items = x.items else { return print("[Loki] Failed to parse RSS feed for: \(feed.server).") }
+ items.reversed().forEach { item in
+ guard let title = item.title, let description = item.description, let date = item.pubDate else { return }
+ let timestamp = UInt64(date.timeIntervalSince1970 * 1000)
+ let urlRegex = try! NSRegularExpression(pattern: "]*?\\s+)?href=\"([^\"]*)\".*?>(.*?)<.*?\\/a>")
+ var bodyAsHTML = "\(title)
\(description)".replacingOccurrences(of: "", with: "
")
+ while true {
+ guard let match = urlRegex.firstMatch(in: bodyAsHTML, options: [], range: NSRange(location: 0, length: bodyAsHTML.utf16.count)) else { break }
+ let matchRange = match.range(at: 0)
+ let urlRange = match.range(at: 1)
+ let descriptionRange = match.range(at: 2)
+ let url = (bodyAsHTML as NSString).substring(with: urlRange)
+ let description = (bodyAsHTML as NSString).substring(with: descriptionRange)
+ bodyAsHTML = (bodyAsHTML as NSString).replacingCharacters(in: matchRange, with: "\(description) (\(url))") as String
+ }
+ guard let bodyAsData = bodyAsHTML.data(using: String.Encoding.unicode) else { return }
+ let options = [ NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html ]
+ guard let body = try? NSAttributedString(data: bodyAsData, options: options, documentAttributes: nil).string else { return }
+ let id = LKGroupUtilities.getEncodedRSSFeedIDAsData(feed.id)
+ let groupContext = SSKProtoGroupContext.builder(id: id, type: .deliver)
+ groupContext.setName(feed.displayName)
+ let dataMessage = SSKProtoDataMessage.builder()
+ dataMessage.setTimestamp(timestamp)
+ dataMessage.setGroup(try! groupContext.build())
+ dataMessage.setBody(body)
+ let content = SSKProtoContent.builder()
+ content.setDataMessage(try! dataMessage.build())
+ let envelope = SSKProtoEnvelope.builder(type: .ciphertext, timestamp: timestamp)
+ envelope.setSource(NSLocalizedString("Loki", comment: ""))
+ envelope.setSourceDevice(OWSDevicePrimaryDeviceId)
+ envelope.setContent(try! content.build().serializedData())
+ OWSPrimaryStorage.shared().dbReadWriteConnection.readWrite { transaction in
+ SSKEnvironment.shared.messageManager.throws_processEnvelope(try! envelope.build(), plaintextData: try! content.build().serializedData(), wasReceivedByUD: false, transaction: transaction, serverID: 0)
+ }
}
}
}
diff --git a/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift b/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
index 127847955..986c65e89 100644
--- a/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
+++ b/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
@@ -87,23 +87,42 @@ internal class LokiFileServerProxy : LokiHTTPClient {
task.resume()
return promise
}.map { rawResponse in
- guard let data = rawResponse as? Data, !data.isEmpty, let cipherText = Data(base64Encoded: data) else {
- print("[Loki] Received a non-string encoded response.")
+ guard let data = rawResponse as? Data, !data.isEmpty else {
+ print("[Loki] Received an empty response.")
return rawResponse
}
+ var uncheckedStatusCode: Int? = nil
+ let uncheckedCipherText: Data?
+ if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON, let base64EncodedData = json["data"] as? String {
+ if let meta = json["meta"] as? JSON { uncheckedStatusCode = meta["code"] as? Int }
+ uncheckedCipherText = Data(base64Encoded: base64EncodedData)
+ } else {
+ uncheckedCipherText = data
+ }
+ guard let cipherText = uncheckedCipherText else {
+ print("[Loki] Received an invalid response.")
+ throw Error.proxyResponseParsingFailed
+ }
let response = try DiffieHellman.decrypt(cipherText, using: symmetricKey)
let uncheckedJSON = try? JSONSerialization.jsonObject(with: response, options: .allowFragments) as? JSON
- guard let json = uncheckedJSON, let httpStatusCode = json["status"] as? Int else { throw HTTPError.networkError(code: -1, response: nil, underlyingError: Error.proxyResponseParsingFailed) }
- let isSuccess = (200..<300).contains(httpStatusCode)
+ guard let json = uncheckedJSON else { throw HTTPError.networkError(code: -1, response: nil, underlyingError: Error.proxyResponseParsingFailed) }
+ if uncheckedStatusCode == nil {
+ uncheckedStatusCode = json["status"] as? Int
+ }
+ guard let statusCode = uncheckedStatusCode else {
+ print("[Loki] Received an invalid response.")
+ throw Error.proxyResponseParsingFailed
+ }
+ let isSuccess = (200..<300).contains(statusCode)
var body: Any? = nil
if let bodyAsString = json["body"] as? String {
body = bodyAsString
- if let bodyAsJSON = try? JSONSerialization.jsonObject(with: bodyAsString.data(using: .utf8)!, options: .allowFragments) as? [String: Any] {
+ if let bodyAsJSON = try? JSONSerialization.jsonObject(with: bodyAsString.data(using: .utf8)!, options: .allowFragments) as? JSON {
body = bodyAsJSON
}
}
- guard isSuccess else { throw HTTPError.networkError(code: httpStatusCode, response: body, underlyingError: Error.fileServerHTTPError(code: httpStatusCode, message: body)) }
- return body
+ guard isSuccess else { throw HTTPError.networkError(code: statusCode, response: body, underlyingError: Error.fileServerHTTPError(code: statusCode, message: body)) }
+ return body ?? response
}.recover { error -> Promise in
print("[Loki] File server proxy request failed with error: \(error.localizedDescription).")
throw HTTPError.from(error: error) ?? error
diff --git a/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift b/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift
index 3cf43e27c..9c28f352f 100644
--- a/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift
+++ b/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift
@@ -1,26 +1,26 @@
import PromiseKit
-internal enum LokiRSSFeedProxy {
+public enum LokiRSSFeedProxy {
- internal enum Error : LocalizedError {
+ public enum Error : LocalizedError {
case proxyResponseParsingFailed
- internal var errorDescription: String? {
+ public var errorDescription: String? {
switch self {
case .proxyResponseParsingFailed: return "Couldn't parse proxy response."
}
}
}
- internal static func fetchContent(for url: String) -> Promise {
+ public static func fetchContent(for url: String) -> Promise {
let server = LokiStorageAPI.server
let endpoints = [ "messenger-updates/feed" : "loki/v1/rss/messenger", "loki.network/feed" : "loki/v1/rss/loki" ]
let endpoint = endpoints.first { url.lowercased().contains($0.key) }!.value
let url = URL(string: server + "/" + endpoint)!
let request = TSRequest(url: url)
return LokiFileServerProxy(for: server).perform(request).map { response -> String in
- guard let json = response as? JSON, let data = json["data"] as? String else { throw Error.proxyResponseParsingFailed }
- return data
+ guard let data = response as? Data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON, let xml = json["data"] as? String else { throw Error.proxyResponseParsingFailed }
+ return xml
}
}
}
diff --git a/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift b/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift
index 8fa9b1179..c32747566 100644
--- a/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift
+++ b/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift
@@ -78,7 +78,7 @@ internal class LokiSnodeProxy : LokiHTTPClient {
var body: Any? = nil
if let bodyAsString = json["body"] as? String {
body = bodyAsString
- if let bodyAsJSON = try? JSONSerialization.jsonObject(with: bodyAsString.data(using: .utf8)!, options: .allowFragments) as? [String: Any] {
+ if let bodyAsJSON = try? JSONSerialization.jsonObject(with: bodyAsString.data(using: .utf8)!, options: .allowFragments) as? JSON {
body = bodyAsJSON
}
}
From 340c30e2b5786cd112d78dca5946591dd4523adc Mon Sep 17 00:00:00 2001
From: Niels Andriesse
Date: Mon, 3 Feb 2020 14:16:00 +1100
Subject: [PATCH 6/8] Update Pods
---
Pods | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Pods b/Pods
index 9837a2fa9..0518320cb 160000
--- a/Pods
+++ b/Pods
@@ -1 +1 @@
-Subproject commit 9837a2fa9828fcd261233cff21dc688dc308c6b8
+Subproject commit 0518320cbc8571118b8a10decf72497134e96185
From 2dc1a44087ca44db3af3f9c5139bdb0520829ff9 Mon Sep 17 00:00:00 2001
From: Niels Andriesse
Date: Mon, 3 Feb 2020 14:33:47 +1100
Subject: [PATCH 7/8] Clean
---
.../src/Loki/API/LokiFileServerProxy.swift | 41 ++++---------------
.../src/Loki/API/LokiSnodeProxy.swift | 8 ++--
2 files changed, 12 insertions(+), 37 deletions(-)
diff --git a/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift b/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
index 986c65e89..cd2f6f69e 100644
--- a/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
+++ b/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift
@@ -24,7 +24,7 @@ internal class LokiFileServerProxy : LokiHTTPClient {
case .symmetricKeyGenerationFailed: return "Couldn't generate symmetric key."
case .endpointParsingFailed: return "Couldn't parse endpoint."
case .proxyResponseParsingFailed: return "Couldn't parse proxy response."
- case .fileServerHTTPError(let httpStatusCode, let message): return "File server returned error \(httpStatusCode) with description: \(message ?? "no description provided.")."
+ case .fileServerHTTPError(let httpStatusCode, let message): return "File server returned \(httpStatusCode) with description: \(message ?? "no description provided.")."
}
}
}
@@ -87,42 +87,17 @@ internal class LokiFileServerProxy : LokiHTTPClient {
task.resume()
return promise
}.map { rawResponse in
- guard let data = rawResponse as? Data, !data.isEmpty else {
- print("[Loki] Received an empty response.")
- return rawResponse
- }
- var uncheckedStatusCode: Int? = nil
- let uncheckedCipherText: Data?
- if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON, let base64EncodedData = json["data"] as? String {
- if let meta = json["meta"] as? JSON { uncheckedStatusCode = meta["code"] as? Int }
- uncheckedCipherText = Data(base64Encoded: base64EncodedData)
- } else {
- uncheckedCipherText = data
- }
- guard let cipherText = uncheckedCipherText else {
- print("[Loki] Received an invalid response.")
- throw Error.proxyResponseParsingFailed
- }
- let response = try DiffieHellman.decrypt(cipherText, using: symmetricKey)
- let uncheckedJSON = try? JSONSerialization.jsonObject(with: response, options: .allowFragments) as? JSON
- guard let json = uncheckedJSON else { throw HTTPError.networkError(code: -1, response: nil, underlyingError: Error.proxyResponseParsingFailed) }
- if uncheckedStatusCode == nil {
- uncheckedStatusCode = json["status"] as? Int
- }
- guard let statusCode = uncheckedStatusCode else {
+ guard let responseAsData = rawResponse as? Data, let responseAsJSON = try? JSONSerialization.jsonObject(with: responseAsData, options: .allowFragments) as? JSON, let base64EncodedCipherText = responseAsJSON["data"] as? String,
+ let meta = responseAsJSON["meta"] as? JSON, let statusCode = meta["code"] as? Int, let cipherText = Data(base64Encoded: base64EncodedCipherText) else {
print("[Loki] Received an invalid response.")
throw Error.proxyResponseParsingFailed
}
let isSuccess = (200..<300).contains(statusCode)
- var body: Any? = nil
- if let bodyAsString = json["body"] as? String {
- body = bodyAsString
- if let bodyAsJSON = try? JSONSerialization.jsonObject(with: bodyAsString.data(using: .utf8)!, options: .allowFragments) as? JSON {
- body = bodyAsJSON
- }
- }
- guard isSuccess else { throw HTTPError.networkError(code: statusCode, response: body, underlyingError: Error.fileServerHTTPError(code: statusCode, message: body)) }
- return body ?? response
+ guard isSuccess else { throw HTTPError.networkError(code: statusCode, response: nil, underlyingError: Error.fileServerHTTPError(code: statusCode, message: nil)) }
+ let uncheckedJSONAsData = try DiffieHellman.decrypt(cipherText, using: symmetricKey)
+ let uncheckedJSON = try? JSONSerialization.jsonObject(with: uncheckedJSONAsData, options: .allowFragments) as? JSON
+ guard let json = uncheckedJSON else { throw HTTPError.networkError(code: -1, response: nil, underlyingError: Error.proxyResponseParsingFailed) }
+ return json
}.recover { error -> Promise in
print("[Loki] File server proxy request failed with error: \(error.localizedDescription).")
throw HTTPError.from(error: error) ?? error
diff --git a/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift b/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift
index c32747566..d23328ced 100644
--- a/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift
+++ b/SignalServiceKit/src/Loki/API/LokiSnodeProxy.swift
@@ -67,14 +67,14 @@ internal class LokiSnodeProxy : LokiHTTPClient {
task.resume()
return promise
}.map { rawResponse in
- guard let data = rawResponse as? Data, !data.isEmpty, let cipherText = Data(base64Encoded: data) else {
+ guard let responseAsData = rawResponse as? Data, let cipherText = Data(base64Encoded: responseAsData) else {
print("[Loki] Received a non-string encoded response.")
return rawResponse
}
let response = try DiffieHellman.decrypt(cipherText, using: symmetricKey)
let uncheckedJSON = try? JSONSerialization.jsonObject(with: response, options: .allowFragments) as? JSON
- guard let json = uncheckedJSON, let httpStatusCode = json["status"] as? Int else { throw HTTPError.networkError(code: -1, response: nil, underlyingError: Error.proxyResponseParsingFailed) }
- let isSuccess = (200..<300).contains(httpStatusCode)
+ guard let json = uncheckedJSON, let statusCode = json["status"] as? Int else { throw HTTPError.networkError(code: -1, response: nil, underlyingError: Error.proxyResponseParsingFailed) }
+ let isSuccess = (200..<300).contains(statusCode)
var body: Any? = nil
if let bodyAsString = json["body"] as? String {
body = bodyAsString
@@ -82,7 +82,7 @@ internal class LokiSnodeProxy : LokiHTTPClient {
body = bodyAsJSON
}
}
- guard isSuccess else { throw HTTPError.networkError(code: httpStatusCode, response: body, underlyingError: Error.targetSnodeHTTPError(code: httpStatusCode, message: body)) }
+ guard isSuccess else { throw HTTPError.networkError(code: statusCode, response: body, underlyingError: Error.targetSnodeHTTPError(code: statusCode, message: body)) }
return body
}.recover { error -> Promise in
print("[Loki] Proxy request failed with error: \(error.localizedDescription).")
From efeefc4d0eb2bc5e2116952222f00e7ab8f80b64 Mon Sep 17 00:00:00 2001
From: Niels Andriesse
Date: Mon, 3 Feb 2020 14:52:15 +1100
Subject: [PATCH 8/8] Fix RSS feed parsing
---
SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift b/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift
index 9c28f352f..12a9732b0 100644
--- a/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift
+++ b/SignalServiceKit/src/Loki/API/LokiRSSFeedProxy.swift
@@ -19,7 +19,7 @@ public enum LokiRSSFeedProxy {
let url = URL(string: server + "/" + endpoint)!
let request = TSRequest(url: url)
return LokiFileServerProxy(for: server).perform(request).map { response -> String in
- guard let data = response as? Data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON, let xml = json["data"] as? String else { throw Error.proxyResponseParsingFailed }
+ guard let json = response as? JSON, let xml = json["data"] as? String else { throw Error.proxyResponseParsingFailed }
return xml
}
}