From d2006853c7fe24efee21fa8100ab9a53baf2c918 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 12 Mar 2025 12:52:04 -0400 Subject: [PATCH] Start new style messsage api and convert report spam to WebSocket. --- .../securesms/dependencies/AppDependencies.kt | 5 +++ .../ApplicationDependencyProvider.java | 6 ++++ .../dependencies/NetworkDependenciesModule.kt | 5 +++ .../securesms/jobs/ReportSpamJob.java | 13 ++++---- .../securesms/net/SignalNetwork.kt | 6 ++++ .../MockApplicationDependencyProvider.kt | 5 +++ .../api/SignalServiceAccountManager.java | 4 --- .../signalservice/api/message/MessageApi.kt | 32 +++++++++++++++++++ .../api/message/SpamTokenMessage.kt | 10 ++++++ .../internal/push/PushServiceSocket.java | 8 ----- .../internal/push/SpamTokenMessage.kt | 5 --- 11 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/MessageApi.kt create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/SpamTokenMessage.kt delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SpamTokenMessage.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt index 1180911e78..5d3c64a679 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt @@ -50,6 +50,7 @@ import org.whispersystems.signalservice.api.cds.CdsApi import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations import org.whispersystems.signalservice.api.keys.KeysApi import org.whispersystems.signalservice.api.link.LinkDeviceApi +import org.whispersystems.signalservice.api.message.MessageApi import org.whispersystems.signalservice.api.payments.PaymentsApi import org.whispersystems.signalservice.api.ratelimit.RateLimitChallengeApi import org.whispersystems.signalservice.api.registration.RegistrationApi @@ -330,6 +331,9 @@ object AppDependencies { val rateLimitChallengeApi: RateLimitChallengeApi get() = networkModule.rateLimitChallengeApi + val messageApi: MessageApi + get() = networkModule.messageApi + @JvmStatic val okHttpClient: OkHttpClient get() = networkModule.okHttpClient @@ -406,5 +410,6 @@ object AppDependencies { fun providePaymentsApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): PaymentsApi fun provideCdsApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): CdsApi fun provideRateLimitChallengeApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): RateLimitChallengeApi + fun provideMessageApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket): MessageApi } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index d00f9486d9..baef32fe93 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -89,6 +89,7 @@ import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.keys.KeysApi; import org.whispersystems.signalservice.api.link.LinkDeviceApi; +import org.whispersystems.signalservice.api.message.MessageApi; import org.whispersystems.signalservice.api.payments.PaymentsApi; import org.whispersystems.signalservice.api.push.ServiceId.ACI; import org.whispersystems.signalservice.api.push.ServiceId.PNI; @@ -522,6 +523,11 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider { return new RateLimitChallengeApi(authWebSocket); } + @Override + public @NonNull MessageApi provideMessageApi(@NonNull SignalWebSocket.AuthenticatedWebSocket authWebSocket, @NonNull SignalWebSocket.UnauthenticatedWebSocket unauthWebSocket) { + return new MessageApi(authWebSocket, unauthWebSocket); + } + @VisibleForTesting static class DynamicCredentialsProvider implements CredentialsProvider { diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt index 079fad7aa3..0c80cd6577 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt @@ -34,6 +34,7 @@ import org.whispersystems.signalservice.api.cds.CdsApi import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations import org.whispersystems.signalservice.api.keys.KeysApi import org.whispersystems.signalservice.api.link.LinkDeviceApi +import org.whispersystems.signalservice.api.message.MessageApi import org.whispersystems.signalservice.api.payments.PaymentsApi import org.whispersystems.signalservice.api.push.TrustStore import org.whispersystems.signalservice.api.ratelimit.RateLimitChallengeApi @@ -182,6 +183,10 @@ class NetworkDependenciesModule( provider.provideRateLimitChallengeApi(authWebSocket) } + val messageApi: MessageApi by lazy { + provider.provideMessageApi(authWebSocket, unauthWebSocket) + } + val okHttpClient: OkHttpClient by lazy { OkHttpClient.Builder() .addInterceptor(StandardUserAgentInterceptor()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/ReportSpamJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/ReportSpamJob.java index d183342f20..e9332c91a6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/ReportSpamJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/ReportSpamJob.java @@ -3,18 +3,18 @@ package org.thoughtcrime.securesms.jobs; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.signal.core.util.Base64; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.MessageTable.ReportSpamData; import org.thoughtcrime.securesms.database.SignalDatabase; -import org.thoughtcrime.securesms.dependencies.AppDependencies; -import org.thoughtcrime.securesms.jobmanager.JsonJobData; import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.JsonJobData; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.net.SignalNetwork; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.signal.core.util.Base64; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; +import org.whispersystems.signalservice.api.NetworkResultUtil; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException; @@ -92,8 +92,7 @@ public class ReportSpamJob extends BaseJob { reportSpamData = SignalDatabase.messages().getReportSpamMessageServerData(threadId, timestamp, MAX_MESSAGE_COUNT); } - int count = 0; - SignalServiceAccountManager signalServiceAccountManager = AppDependencies.getSignalServiceAccountManager(); + int count = 0; for (ReportSpamData data : reportSpamData) { RecipientId recipientId = data.getRecipientId(); @@ -108,7 +107,7 @@ public class ReportSpamJob extends BaseJob { reportingTokenEncoded = Base64.encodeWithPadding(reportingTokenBytes); } - signalServiceAccountManager.reportSpam(serviceId.get(), data.getServerGuid(), reportingTokenEncoded); + NetworkResultUtil.toBasicLegacy(SignalNetwork.message().reportSpam(serviceId.get(), data.getServerGuid(), reportingTokenEncoded)); count++; } else { Log.w(TAG, "Unable to report spam without an ACI for " + recipientId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/net/SignalNetwork.kt b/app/src/main/java/org/thoughtcrime/securesms/net/SignalNetwork.kt index a2ad78c266..c04f29d17d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/net/SignalNetwork.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/net/SignalNetwork.kt @@ -13,6 +13,7 @@ import org.whispersystems.signalservice.api.calling.CallingApi import org.whispersystems.signalservice.api.cds.CdsApi import org.whispersystems.signalservice.api.keys.KeysApi import org.whispersystems.signalservice.api.link.LinkDeviceApi +import org.whispersystems.signalservice.api.message.MessageApi import org.whispersystems.signalservice.api.payments.PaymentsApi import org.whispersystems.signalservice.api.ratelimit.RateLimitChallengeApi import org.whispersystems.signalservice.api.storage.StorageServiceApi @@ -47,6 +48,11 @@ object SignalNetwork { val linkDevice: LinkDeviceApi get() = AppDependencies.linkDeviceApi + @JvmStatic + @get:JvmName("message") + val message: MessageApi + get() = AppDependencies.messageApi + @JvmStatic @get:JvmName("payments") val payments: PaymentsApi diff --git a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt index ab070679f0..a8c1c4134c 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt @@ -44,6 +44,7 @@ import org.whispersystems.signalservice.api.cds.CdsApi import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations import org.whispersystems.signalservice.api.keys.KeysApi import org.whispersystems.signalservice.api.link.LinkDeviceApi +import org.whispersystems.signalservice.api.message.MessageApi import org.whispersystems.signalservice.api.payments.PaymentsApi import org.whispersystems.signalservice.api.ratelimit.RateLimitChallengeApi import org.whispersystems.signalservice.api.registration.RegistrationApi @@ -270,4 +271,8 @@ class MockApplicationDependencyProvider : AppDependencies.Provider { override fun provideRateLimitChallengeApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): RateLimitChallengeApi { return mockk(relaxed = true) } + + override fun provideMessageApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket): MessageApi { + return mockk(relaxed = true) + } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java index f9f20b7c0a..977a132237 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java @@ -171,10 +171,6 @@ public class SignalServiceAccountManager { this.pushServiceSocket.pingStorageService(); } - public void reportSpam(ServiceId serviceId, String serverGuid, String reportingToken) throws IOException { - this.pushServiceSocket.reportSpam(serviceId, serverGuid, reportingToken); - } - /** * @return The avatar URL path, if one was written. */ diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/MessageApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/MessageApi.kt new file mode 100644 index 0000000000..9fcaa4ec0f --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/MessageApi.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.message + +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.push.ServiceId +import org.whispersystems.signalservice.api.websocket.SignalWebSocket +import org.whispersystems.signalservice.internal.post +import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage + +/** + * Collection of endpoints for operating on messages. + */ +class MessageApi( + private val authWebSocket: SignalWebSocket.AuthenticatedWebSocket, + private val unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket +) { + + /** + * Report a message sender and message id as spam. + * + * POST /v1/messages/report/[serviceId]/[serverGuid] + * - 200: Success + */ + fun reportSpam(serviceId: ServiceId, serverGuid: String, reportingToken: String?): NetworkResult { + val request = WebSocketRequestMessage.post("/v1/messages/report/$serviceId/$serverGuid", SpamTokenMessage(reportingToken)) + return NetworkResult.fromWebSocketRequest(authWebSocket, request) + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/SpamTokenMessage.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/SpamTokenMessage.kt new file mode 100644 index 0000000000..941b206773 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/SpamTokenMessage.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.message + +import com.fasterxml.jackson.annotation.JsonProperty + +data class SpamTokenMessage(@JsonProperty val token: String?) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index 9c6bca2644..f601a4d7e7 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -253,8 +253,6 @@ public class PushServiceSocket { private static final String SVR2_AUTH = "/v2/backup/auth"; private static final String SVR3_AUTH = "/v3/backup/auth"; - private static final String REPORT_SPAM = "/v1/messages/report/%s/%s"; - private static final String BACKUP_AUTH_CHECK_V2 = "/v2/backup/auth/check"; private static final String BACKUP_AUTH_CHECK_V3 = "/v3/backup/auth/check"; @@ -2521,12 +2519,6 @@ public class PushServiceSocket { } } - public void reportSpam(ServiceId serviceId, String serverGuid, String reportingToken) - throws NonSuccessfulResponseCodeException, MalformedResponseException, PushNetworkException - { - makeServiceRequest(String.format(REPORT_SPAM, serviceId.toString(), serverGuid), "POST", JsonUtil.toJson(new SpamTokenMessage(reportingToken))); - } - /** * Handler for Google Play Billing purchase token linking */ diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SpamTokenMessage.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SpamTokenMessage.kt deleted file mode 100644 index fbf8eed5b2..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SpamTokenMessage.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.whispersystems.signalservice.internal.push - -import com.fasterxml.jackson.annotation.JsonProperty - -data class SpamTokenMessage(@JsonProperty val token: String?)