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