From b4472833b8fb48401be1c493b5f579d437f13516 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 14 Nov 2024 11:31:30 -0500 Subject: [PATCH] Include unregistered users in blocked sync message. --- .../blocked/BlockedUsersRepository.java | 20 ++-- .../app/privacy/PrivacySettingsRepository.kt | 2 +- .../securesms/database/RecipientTable.kt | 9 +- .../jobs/MultiDeviceBlockedUpdateJob.java | 113 ------------------ .../jobs/MultiDeviceBlockedUpdateJob.kt | 75 ++++++++++++ .../api/SignalServiceMessageSender.java | 9 +- .../multidevice/BlockedListMessage.java | 24 ---- .../multidevice/BlockedListMessage.kt | 17 +++ 8 files changed, 110 insertions(+), 159 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.kt delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.java create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/blocked/BlockedUsersRepository.java b/app/src/main/java/org/thoughtcrime/securesms/blocked/BlockedUsersRepository.java index 7ae791b1a1..0b7d53d623 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/blocked/BlockedUsersRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/blocked/BlockedUsersRepository.java @@ -9,6 +9,7 @@ import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.RecipientTable; import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.database.model.RecipientRecord; import org.thoughtcrime.securesms.groups.GroupChangeBusyException; import org.thoughtcrime.securesms.groups.GroupChangeFailedException; import org.thoughtcrime.securesms.recipients.Recipient; @@ -19,6 +20,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; class BlockedUsersRepository { @@ -32,19 +34,11 @@ class BlockedUsersRepository { void getBlocked(@NonNull Consumer> blockedUsers) { SignalExecutors.BOUNDED.execute(() -> { - RecipientTable db = SignalDatabase.recipients(); - try (RecipientTable.RecipientReader reader = db.readerForBlocked(db.getBlocked())) { - int count = reader.getCount(); - if (count == 0) { - blockedUsers.accept(Collections.emptyList()); - } else { - List recipients = new ArrayList<>(); - while (reader.getNext() != null) { - recipients.add(reader.getCurrent()); - } - blockedUsers.accept(recipients); - } - } + List records = SignalDatabase.recipients().getBlocked(); + List recipients = records.stream() + .map((record) -> Recipient.resolved(record.getId())) + .collect(Collectors.toList()); + blockedUsers.accept(recipients); }); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/PrivacySettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/PrivacySettingsRepository.kt index 0b9fdd4751..75e777a0a6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/PrivacySettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/PrivacySettingsRepository.kt @@ -18,7 +18,7 @@ class PrivacySettingsRepository { SignalExecutors.BOUNDED.execute { val recipientDatabase = SignalDatabase.recipients - consumer(recipientDatabase.getBlocked().count) + consumer(recipientDatabase.getBlocked().size) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index d51f0df760..bb031135ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -709,8 +709,13 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da } } - fun getBlocked(): Cursor { - return readableDatabase.query(TABLE_NAME, ID_PROJECTION, "$BLOCKED = 1", null, null, null, null) + fun getBlocked(): List { + return readableDatabase + .select() + .from(TABLE_NAME) + .where("$BLOCKED = 1") + .run() + .readToList { RecipientTableCursorUtil.getRecord(context, it) } } fun readerForBlocked(cursor: Cursor): RecipientReader { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java deleted file mode 100644 index 93e6d585bd..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.database.RecipientTable; -import org.thoughtcrime.securesms.database.RecipientTable.RecipientReader; -import org.thoughtcrime.securesms.database.SignalDatabase; -import org.thoughtcrime.securesms.dependencies.AppDependencies; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.thoughtcrime.securesms.net.NotPushRegisteredException; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; -import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class MultiDeviceBlockedUpdateJob extends BaseJob { - - public static final String KEY = "MultiDeviceBlockedUpdateJob"; - - @SuppressWarnings("unused") - private static final String TAG = Log.tag(MultiDeviceBlockedUpdateJob.class); - - public MultiDeviceBlockedUpdateJob() { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue("MultiDeviceBlockedUpdateJob") - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build()); - } - - private MultiDeviceBlockedUpdateJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @Nullable byte[] serialize() { - return null; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() - throws IOException, UntrustedIdentityException - { - if (!Recipient.self().isRegistered()) { - throw new NotPushRegisteredException(); - } - - if (!SignalStore.account().hasLinkedDevices()) { - Log.i(TAG, "Not multi device, aborting..."); - return; - } - - RecipientTable database = SignalDatabase.recipients(); - - try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) { - List blockedIndividuals = new LinkedList<>(); - List blockedGroups = new LinkedList<>(); - - Recipient recipient; - - while ((recipient = reader.getNext()) != null) { - if (recipient.isPushGroup()) { - blockedGroups.add(recipient.requireGroupId().getDecodedId()); - } else if (recipient.isMaybeRegistered() && (recipient.getHasServiceId() || recipient.getHasE164())) { - blockedIndividuals.add(RecipientUtil.toSignalServiceAddress(context, recipient)); - } - } - - SignalServiceMessageSender messageSender = AppDependencies.getSignalServiceMessageSender(); - messageSender.sendSyncMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)) - ); - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - if (exception instanceof ServerRejectedException) return false; - if (exception instanceof PushNetworkException) return true; - return false; - } - - @Override - public void onFailure() { - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull MultiDeviceBlockedUpdateJob create(@NonNull Parameters parameters, @Nullable byte[] serializedData) { - return new MultiDeviceBlockedUpdateJob(parameters); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.kt new file mode 100644 index 0000000000..37d677c867 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.kt @@ -0,0 +1,75 @@ +package org.thoughtcrime.securesms.jobs + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.RecipientRecord +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.net.NotPushRegisteredException +import org.thoughtcrime.securesms.recipients.Recipient +import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException +import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage +import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage +import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException +import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException +import java.io.IOException +import java.util.concurrent.TimeUnit + +class MultiDeviceBlockedUpdateJob private constructor(parameters: Parameters) : BaseJob(parameters) { + companion object { + const val KEY: String = "MultiDeviceBlockedUpdateJob" + + private val TAG = Log.tag(MultiDeviceBlockedUpdateJob::class.java) + } + + constructor() : this( + Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("MultiDeviceBlockedUpdateJob") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build() + ) + + override fun serialize(): ByteArray? = null + + override fun getFactoryKey(): String = KEY + + @Throws(IOException::class, UntrustedIdentityException::class) + public override fun onRun() { + if (!Recipient.self().isRegistered) { + throw NotPushRegisteredException() + } + + if (!SignalStore.account.hasLinkedDevices) { + Log.i(TAG, "Not multi device, aborting...") + return + } + + val blocked: List = SignalDatabase.recipients.getBlocked() + val blockedGroups = blocked.mapNotNull { it.groupId?.decodedId } + val blockedIndividuals = blocked + .filter { it.aci != null || it.e164 != null } + .map { BlockedListMessage.Individual(it.aci, it.e164) } + + AppDependencies.signalServiceMessageSender.sendSyncMessage( + SignalServiceSyncMessage.forBlocked(BlockedListMessage(blockedIndividuals, blockedGroups)) + ) + } + + public override fun onShouldRetry(exception: Exception): Boolean { + if (exception is ServerRejectedException) return false + if (exception is PushNetworkException) return true + return false + } + + override fun onFailure() = Unit + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): MultiDeviceBlockedUpdateJob { + return MultiDeviceBlockedUpdateJob(parameters) + } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 3ef443387b..b31ac74f77 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -22,8 +22,6 @@ import org.signal.libsignal.protocol.state.PreKeyBundle; import org.signal.libsignal.protocol.state.SessionRecord; import org.signal.libsignal.protocol.util.Pair; import org.signal.libsignal.zkgroup.groupsend.GroupSendFullToken; -import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations; -import org.whispersystems.signalservice.api.attachment.AttachmentApi; import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil; import org.whispersystems.signalservice.api.crypto.ContentHint; import org.whispersystems.signalservice.api.crypto.EnvelopeContent; @@ -89,7 +87,6 @@ import org.whispersystems.signalservice.api.util.Uint64Util; import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException; import org.whispersystems.signalservice.internal.ServiceResponse; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.crypto.AttachmentDigest; import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; import org.whispersystems.signalservice.internal.push.AttachmentPointer; @@ -1486,9 +1483,9 @@ public class SignalServiceMessageSender { SyncMessage.Builder syncMessage = createSyncMessageBuilder(); SyncMessage.Blocked.Builder blockedMessage = new SyncMessage.Blocked.Builder(); - blockedMessage.acis(blocked.getAddresses().stream().map(a -> a.getServiceId().toString()).collect(Collectors.toList())); - blockedMessage.numbers(blocked.getAddresses().stream().filter(a -> a.getNumber().isPresent()).map(a -> a.getNumber().get()).collect(Collectors.toList())); - blockedMessage.groupIds(blocked.getGroupIds().stream().map(ByteString::of).collect(Collectors.toList())); + blockedMessage.acis(blocked.individuals.stream().filter(a -> a.getAci() != null).map(a -> a.getAci().toString()).collect(Collectors.toList())); + blockedMessage.numbers(blocked.individuals.stream().filter(a -> a.getE164() != null).map(a -> a.getE164()).collect(Collectors.toList())); + blockedMessage.groupIds(blocked.groupIds.stream().map(ByteString::of).collect(Collectors.toList())); return container.syncMessage(syncMessage.blocked(blockedMessage.build()).build()).build(); } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.java deleted file mode 100644 index 31652c0c52..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.whispersystems.signalservice.api.messages.multidevice; - -import org.whispersystems.signalservice.api.push.SignalServiceAddress; - -import java.util.List; - -public class BlockedListMessage { - - private final List addresses; - private final List groupIds; - - public BlockedListMessage(List addresses, List groupIds) { - this.addresses = addresses; - this.groupIds = groupIds; - } - - public List getAddresses() { - return addresses; - } - - public List getGroupIds() { - return groupIds; - } -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.kt new file mode 100644 index 0000000000..954b232f04 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.kt @@ -0,0 +1,17 @@ +package org.whispersystems.signalservice.api.messages.multidevice + +import org.whispersystems.signalservice.api.push.ServiceId + +class BlockedListMessage( + @JvmField val individuals: List, + @JvmField val groupIds: List +) { + data class Individual( + val aci: ServiceId.ACI?, + val e164: String? + ) { + init { + check(aci != null || e164 != null) + } + } +}