From e8f0d0d1243d1f1d4cecd1e5605d24b6d3265aea Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Mon, 2 Aug 2021 17:07:29 +1000 Subject: [PATCH] WIP: delete from storage server --- Session/Conversations/ConversationViewItem.m | 13 ++++-- SessionSnodeKit/Snode.swift | 1 + SessionSnodeKit/SnodeAPI.swift | 48 +++++++++++++++++++- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/Session/Conversations/ConversationViewItem.m b/Session/Conversations/ConversationViewItem.m index 49a9926ae..a569caf6e 100644 --- a/Session/Conversations/ConversationViewItem.m +++ b/Session/Conversations/ConversationViewItem.m @@ -980,6 +980,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (void)deleteRemotelyAction { // TODO: closed group and one-on-one chat + TSMessage *message = (TSMessage *)self.interaction; if (self.isGroupThread) { TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; @@ -988,13 +989,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) OWSInteractionType interationType = self.interaction.interactionType; if (interationType != OWSInteractionType_IncomingMessage && interationType != OWSInteractionType_OutgoingMessage) return; - if (groupThread.isClosedGroup) { - - } - if (groupThread.isOpenGroup) { // Make sure it's an open group message - TSMessage *message = (TSMessage *)self.interaction; if (!message.isOpenGroupMessage) return; // Get the open group @@ -1012,8 +1008,15 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) // Roll back [self.interaction save]; }) retainUntilComplete]; + } else { + NSString *groupPublicKey = [LKGroupUtilities getDecodedGroupID:groupThread.groupModel.groupId]; + [SNSnodeAPI deleteMessageForPublickKey:groupPublicKey serverHash:message.serverHash]; } + } else { + TSContactThread *contactThread = (TSContactThread *)self.interaction.thread; + [SNSnodeAPI deleteMessageForPublickKey:contactThread.contactSessionID serverHash:message.serverHash]; } + } - (BOOL)hasBodyTextActionContent diff --git a/SessionSnodeKit/Snode.swift b/SessionSnodeKit/Snode.swift index 7df663405..bb36f2586 100644 --- a/SessionSnodeKit/Snode.swift +++ b/SessionSnodeKit/Snode.swift @@ -14,6 +14,7 @@ public final class Snode : NSObject, NSCoding { // NSObject/NSCoding conformance case getSwarm = "get_snodes_for_pubkey" case getMessages = "retrieve" case sendMessage = "store" + case deleteMessage = "delete" case oxenDaemonRPCCall = "oxend_request" case getInfo = "info" case clearAllData = "delete_all" diff --git a/SessionSnodeKit/SnodeAPI.swift b/SessionSnodeKit/SnodeAPI.swift index 76ab28d67..10cadd7ab 100644 --- a/SessionSnodeKit/SnodeAPI.swift +++ b/SessionSnodeKit/SnodeAPI.swift @@ -454,7 +454,53 @@ public final class SnodeAPI : NSObject { return promise } -// public static func deleteMessage() -> Promise + @objc(deleteMessageForPublickKey:serverHash:) + public static func objc_deleteMessage(publicKey: String, serverHash: String) -> AnyPromise { + AnyPromise.from(deleteMessage(publicKey: publicKey, serverHash: serverHash)) + } + + public static func deleteMessage(publicKey: String, serverHash: String) -> Promise { + let storage = SNSnodeKitConfiguration.shared.storage + guard let userX25519PublicKey = storage.getUserPublicKey(), + let userED25519KeyPair = storage.getUserED25519KeyPair() else { return Promise(error: Error.noKeyPair) } + let publicKey = Features.useTestnet ? publicKey.removing05PrefixIfNeeded() : publicKey + let (promise, seal) = Promise.pending() + Threading.workQueue.async { + getTargetSnodes(for: publicKey).map2 { targetSnodes in + let snode = targetSnodes.first! + let verificationData = (Snode.Method.deleteMessage.rawValue + serverHash).data(using: String.Encoding.utf8)! + guard let signature = sodium.sign.signature(message: Bytes(verificationData), secretKey: userED25519KeyPair.secretKey) else { throw Error.signingFailed } + let parameters: JSON = [ + "pubkey" : userX25519PublicKey, + "pubkey_ed25519" : userED25519KeyPair.publicKey.toHexString(), + "messages": [serverHash], + "signature": signature.toBase64()! + ] + return attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) { + invoke(.deleteMessage, on: snode, associatedWith: publicKey, parameters: parameters).map2{ rawResponse -> RawResponse in + guard let json = rawResponse as? JSON else { throw HTTP.Error.invalidJSON } + var result = false + let isFailed = json["failed"] as? Bool ?? false + if !isFailed { + guard let hashes = json["deleted"] as? [String], let signature = json["signature"] as? String else { throw HTTP.Error.invalidJSON } + // The signature format is ( PUBKEY_HEX || RMSG[0] || ... || RMSG[N] || DMSG[0] || ... || DMSG[M] ) + let verificationData = (publicKey + serverHash + hashes.joined(separator: "")).data(using: String.Encoding.utf8)! + let isValid = sodium.sign.verify(message: Bytes(verificationData), publicKey: Bytes(Data(hex: snode.publicKeySet.ed25519Key)), signature: Bytes(Data(base64Encoded: signature)!)) + result = isValid + } else { + if let reason = json["reason"] as? String, let statusCode = json["code"] as? String { + SNLog("Couldn't delete message with hash: \(serverHash) due to error: \(reason) (\(statusCode)).") + } else { + SNLog("Couldn't delete message with hash: \(serverHash).") + } + } + return result + } + } + }.done2 { seal.fulfill($0) }.catch2 { seal.reject($0) } + } + return promise + } /// Clears all the user's data from their swarm. Returns a dictionary of snode public key to deletion confirmation. public static func clearAllData() -> Promise<[String:Bool]> {