diff --git a/SessionMessagingKit/Open Groups/V2/OpenGroupAPIV2.swift b/SessionMessagingKit/Open Groups/V2/OpenGroupAPIV2.swift index 176a4911c..92175f7de 100644 --- a/SessionMessagingKit/Open Groups/V2/OpenGroupAPIV2.swift +++ b/SessionMessagingKit/Open Groups/V2/OpenGroupAPIV2.swift @@ -119,6 +119,36 @@ public final class OpenGroupAPIV2 : NSObject { } } + public static func compactPoll(_ server: String) -> Promise<[(room: String, messages: [OpenGroupMessageV2], deletions: [Int64], moderators: [String])]> { + let storage = SNMessagingKitConfiguration.shared.storage + let rooms = storage.getAllV2OpenGroups().values.filter { $0.server == server }.map { $0.room } + var body: [JSON] = [] + for room in rooms { + let authToken = try! getAuthToken(for: room, on: server).wait() // TODO: This should be async + var json: JSON = [ "room_id" : room, "auth_token" : authToken ] + if let lastMessageServerID = storage.getLastMessageServerID(for: room, on: server) { + json["from_message_server_id"] = String(lastMessageServerID) + } + if let lastDeletionServerID = storage.getLastDeletionServerID(for: room, on: server) { + json["from_deletion_server_id"] = String(lastDeletionServerID) + } + body.append(json) + } + let request = Request(verb: .post, room: nil, server: server, endpoint: "compact_poll", parameters: [ "requests" : body ], isAuthRequired: false) + return send(request).map(on: DispatchQueue.global(qos: .userInitiated)) { json in + guard let results = json["results"] as? [JSON] else { throw Error.parsingFailed } + var x: [(room: String, messages: [OpenGroupMessageV2], deletions: [Int64], moderators: [String])] = [] + for result in results { + guard let room = result["room_id"] as? String else { continue } + let messages = try! parseMessages(from: result, for: room, on: server).wait() // TODO: This should be async + let deletions = result["deletions"] as? [Int64] ?? [] + let moderators = result["moderators"] as? [String] ?? [] + x.append((room: room, messages: messages, deletions: deletions, moderators: moderators)) + } + return x + } + } + // MARK: Authorization private static func getAuthToken(for room: String, on server: String) -> Promise { let storage = SNMessagingKitConfiguration.shared.storage @@ -224,35 +254,40 @@ public final class OpenGroupAPIV2 : NSObject { } let request = Request(verb: .get, room: room, server: server, endpoint: "messages", queryParameters: queryParameters) return send(request).then(on: DispatchQueue.global(qos: .userInitiated)) { json -> Promise<[OpenGroupMessageV2]> in - guard let rawMessages = json["messages"] as? [[String:Any]] else { throw Error.parsingFailed } - let messages: [OpenGroupMessageV2] = rawMessages.compactMap { json in - guard let message = OpenGroupMessageV2.fromJSON(json), message.serverID != nil, let sender = message.sender, let data = Data(base64Encoded: message.base64EncodedData), - let base64EncodedSignature = message.base64EncodedSignature, let signature = Data(base64Encoded: base64EncodedSignature) else { - SNLog("Couldn't parse open group message from JSON: \(json).") - return nil - } - // Validate the message signature - let publicKey = Data(hex: sender.removing05PrefixIfNeeded()) - let isValid = (try? Ed25519.verifySignature(signature, publicKey: publicKey, data: data)) ?? false - guard isValid else { - SNLog("Ignoring message with invalid signature.") - return nil - } - return message + try parseMessages(from: json, for: room, on: server) + } + } + + private static func parseMessages(from json: JSON, for room: String, on server: String) throws -> Promise<[OpenGroupMessageV2]> { + let storage = SNMessagingKitConfiguration.shared.storage + guard let rawMessages = json["messages"] as? [JSON] else { throw Error.parsingFailed } + let messages: [OpenGroupMessageV2] = rawMessages.compactMap { json in + guard let message = OpenGroupMessageV2.fromJSON(json), message.serverID != nil, let sender = message.sender, let data = Data(base64Encoded: message.base64EncodedData), + let base64EncodedSignature = message.base64EncodedSignature, let signature = Data(base64Encoded: base64EncodedSignature) else { + SNLog("Couldn't parse open group message from JSON: \(json).") + return nil } - let serverID = messages.map { $0.serverID! }.max() ?? 0 // Safe because messages with a nil serverID are filtered out - let lastMessageServerID = storage.getLastMessageServerID(for: room, on: server) ?? 0 - if serverID > lastMessageServerID { - let (promise, seal) = Promise<[OpenGroupMessageV2]>.pending() - storage.write(with: { transaction in - storage.setLastMessageServerID(for: room, on: server, to: serverID, using: transaction) - }, completion: { - seal.fulfill(messages) - }) - return promise - } else { - return Promise.value(messages) + // Validate the message signature + let publicKey = Data(hex: sender.removing05PrefixIfNeeded()) + let isValid = (try? Ed25519.verifySignature(signature, publicKey: publicKey, data: data)) ?? false + guard isValid else { + SNLog("Ignoring message with invalid signature.") + return nil } + return message + } + let serverID = messages.map { $0.serverID! }.max() ?? 0 // Safe because messages with a nil serverID are filtered out + let lastMessageServerID = storage.getLastMessageServerID(for: room, on: server) ?? 0 + if serverID > lastMessageServerID { + let (promise, seal) = Promise<[OpenGroupMessageV2]>.pending() + storage.write(with: { transaction in + storage.setLastMessageServerID(for: room, on: server, to: serverID, using: transaction) + }, completion: { + seal.fulfill(messages) + }) + return promise + } else { + return Promise.value(messages) } }