|  |  |  | @ -3,18 +3,19 @@ import PromiseKit | 
		
	
		
			
				|  |  |  |  | @objc public final class LokiAPI : NSObject { | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     private static let version = "v1" | 
		
	
		
			
				|  |  |  |  |     private static let targetSnodeCount = 2 | 
		
	
		
			
				|  |  |  |  |     public static let defaultMessageTTL: UInt64 = 4 * 24 * 60 * 60 | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     // MARK: Types | 
		
	
		
			
				|  |  |  |  |     private enum Method : String { | 
		
	
		
			
				|  |  |  |  |     private struct Target : Hashable { | 
		
	
		
			
				|  |  |  |  |         let address: String | 
		
	
		
			
				|  |  |  |  |         let port: UInt16 | 
		
	
		
			
				|  |  |  |  |          | 
		
	
		
			
				|  |  |  |  |         enum Method : String { | 
		
	
		
			
				|  |  |  |  |             case getSwarm = "get_snodes_for_pubkey" | 
		
	
		
			
				|  |  |  |  |             case getMessages = "retrieve" | 
		
	
		
			
				|  |  |  |  |             case sendMessage = "store" | 
		
	
		
			
				|  |  |  |  |         case getSwarm = "get_snodes_for_pubkey" | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     public struct Target : Hashable { | 
		
	
		
			
				|  |  |  |  |         let address: String | 
		
	
		
			
				|  |  |  |  |         let port: UInt16 | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     public typealias RawResponse = Any | 
		
	
	
		
			
				
					|  |  |  | @ -32,37 +33,36 @@ import PromiseKit | 
		
	
		
			
				|  |  |  |  |     // MARK: Lifecycle | 
		
	
		
			
				|  |  |  |  |     override private init() { } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     // MARK: API | 
		
	
		
			
				|  |  |  |  |     private static func invoke(_ method: Method, on target: Target, with parameters: [String:Any] = [:]) -> Promise<RawResponse> { | 
		
	
		
			
				|  |  |  |  |     // MARK: Internal API | 
		
	
		
			
				|  |  |  |  |     private static func invoke(_ method: Target.Method, on target: Target, with parameters: [String:Any] = [:]) -> Promise<RawResponse> { | 
		
	
		
			
				|  |  |  |  |         let url = URL(string: "\(target.address):\(target.port)/\(version)/storage_rpc")! | 
		
	
		
			
				|  |  |  |  |         let request = TSRequest(url: url, method: "POST", parameters: [ "method" : method.rawValue, "params" : parameters ]) | 
		
	
		
			
				|  |  |  |  |         return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     public static func getRandomSnode() -> Promise<Target> { | 
		
	
		
			
				|  |  |  |  |     private static func getRandomSnode() -> Promise<Target> { | 
		
	
		
			
				|  |  |  |  |         return Promise<Target> { seal in | 
		
	
		
			
				|  |  |  |  |             seal.fulfill(Target(address: "http://13.238.53.205", port: 8080)) // TODO: Temporary | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     public static func getMessages() -> Promise<[SSKProtoEnvelope]> { | 
		
	
		
			
				|  |  |  |  |         let parameters = [ | 
		
	
		
			
				|  |  |  |  |             "pubKey" : OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey, | 
		
	
		
			
				|  |  |  |  |             "lastHash" : "" // TODO: Implement | 
		
	
		
			
				|  |  |  |  |         ] | 
		
	
		
			
				|  |  |  |  |         return getRandomSnode().then { invoke(.getMessages, on: $0, with: parameters) }.map { rawResponse in // TODO: Use getSwarm() | 
		
	
		
			
				|  |  |  |  |             guard let json = rawResponse as? JSON, let messages = json["messages"] as? [JSON] else { return [] } | 
		
	
		
			
				|  |  |  |  |             return messages.compactMap { message in | 
		
	
		
			
				|  |  |  |  |                 guard let base64EncodedData = message["data"] as? String, let data = Data(base64Encoded: base64EncodedData) else { | 
		
	
		
			
				|  |  |  |  |                     Logger.warn("[Loki] Failed to decode data for message: \(message).") | 
		
	
		
			
				|  |  |  |  |                     return nil | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |                 guard let envelope = try? unwrap(data: data) else { | 
		
	
		
			
				|  |  |  |  |                     Logger.warn("[Loki] Failed to unwrap data for message: \(message).") | 
		
	
		
			
				|  |  |  |  |                     return nil | 
		
	
		
			
				|  |  |  |  |     private static func getSwarm(for hexEncodedPublicKey: String) -> Promise<Set<Target>> { | 
		
	
		
			
				|  |  |  |  |         return getRandomSnode().then { invoke(.getSwarm, on: $0, with: [ "pubKey" : hexEncodedPublicKey ]) }.map { rawResponse in return [] } // TODO: Parse targets from raw response | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |                 return envelope | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     private static func getTargetSnodes(for hexEncodedPublicKey: String) -> Promise<Set<Target>> { | 
		
	
		
			
				|  |  |  |  |         return getSwarm(for: hexEncodedPublicKey).map { Set(Array($0).shuffled().prefix(targetSnodeCount)) } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     // MARK: Public API | 
		
	
		
			
				|  |  |  |  |     public typealias MessagesPromise = Promise<[SSKProtoEnvelope]> | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     public static func getMessages() -> Promise<[MessagesPromise]> { | 
		
	
		
			
				|  |  |  |  |         let hexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey | 
		
	
		
			
				|  |  |  |  |         let lastHash = "" // TODO: Implement | 
		
	
		
			
				|  |  |  |  |         let parameters: [String:Any] = [ "pubKey" : hexEncodedPublicKey, "lastHash" : "" ] | 
		
	
		
			
				|  |  |  |  |         return getTargetSnodes(for: hexEncodedPublicKey).mapValues { targetSnode in | 
		
	
		
			
				|  |  |  |  |             return invoke(.getMessages, on: targetSnode, with: parameters).map { parseProtoEnvelopes(from: $0) } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
	
		
			
				
					|  |  |  | @ -74,19 +74,29 @@ import PromiseKit | 
		
	
		
			
				|  |  |  |  |         return getRandomSnode().then { invoke(.sendMessage, on: $0, with: [ "pubKey" : hexEncodedPublicKey ]) } // TODO: Use getSwarm() and figure out correct parameters | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     public static func getSwarm(for hexEncodedPublicKey: String) -> Promise<Set<Target>> { | 
		
	
		
			
				|  |  |  |  |         return getRandomSnode().then { invoke(.getSwarm, on: $0, with: [ "pubKey" : hexEncodedPublicKey ]) }.map { rawResponse in return [] } // TODO: Parse targets from raw response | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     // MARK: Obj-C API | 
		
	
		
			
				|  |  |  |  |     // MARK: Public API (Obj-C) | 
		
	
		
			
				|  |  |  |  |     @objc public static func objc_sendSignalMessage(_ signalMessage: SignalMessage, to destination: String, timestamp: UInt64, requiringPoW isPoWRequired: Bool) -> AnyPromise { | 
		
	
		
			
				|  |  |  |  |         let promise = Message.from(signalMessage: signalMessage, timestamp: timestamp, requiringPoW: isPoWRequired) | 
		
	
		
			
				|  |  |  |  |             .then(sendMessage) | 
		
	
		
			
				|  |  |  |  |             .recoverNetworkErrorIfNeeded(on: DispatchQueue.global()) | 
		
	
		
			
				|  |  |  |  |         let promise = Message.from(signalMessage: signalMessage, timestamp: timestamp, requiringPoW: isPoWRequired).then(sendMessage).recoverNetworkErrorIfNeeded(on: DispatchQueue.global()) | 
		
	
		
			
				|  |  |  |  |         let anyPromise = AnyPromise(promise) | 
		
	
		
			
				|  |  |  |  |         anyPromise.retainUntilComplete() | 
		
	
		
			
				|  |  |  |  |         return anyPromise | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     // MARK: Convenience | 
		
	
		
			
				|  |  |  |  |     private static func parseProtoEnvelopes(from rawResponse: Any) -> [SSKProtoEnvelope] { | 
		
	
		
			
				|  |  |  |  |         guard let json = rawResponse as? JSON, let messages = json["messages"] as? [JSON] else { return [] } | 
		
	
		
			
				|  |  |  |  |         return messages.compactMap { message in | 
		
	
		
			
				|  |  |  |  |             guard let base64EncodedData = message["data"] as? String, let data = Data(base64Encoded: base64EncodedData) else { | 
		
	
		
			
				|  |  |  |  |                 Logger.warn("[Loki] Failed to decode data for message: \(message).") | 
		
	
		
			
				|  |  |  |  |                 return nil | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             guard let envelope = try? unwrap(data: data) else { | 
		
	
		
			
				|  |  |  |  |                 Logger.warn("[Loki] Failed to unwrap data for message: \(message).") | 
		
	
		
			
				|  |  |  |  |                 return nil | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             return envelope | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | // MARK: - Convenience | 
		
	
	
		
			
				
					|  |  |  | 
 |