Add support for resending badly-encrypted stories.

This commit is contained in:
Greyson Parrelli 2022-08-11 13:37:37 -04:00 committed by Alex Hart
parent 7873ec2b67
commit c6be427883
11 changed files with 288 additions and 71 deletions

View file

@ -320,7 +320,19 @@ class DistributionListDatabase constructor(context: Context?, databaseHelper: Si
} }
fun getList(listId: DistributionListId): DistributionListRecord? { fun getList(listId: DistributionListId): DistributionListRecord? {
readableDatabase.query(ListTable.TABLE_NAME, null, "${ListTable.ID} = ? AND ${ListTable.IS_NOT_DELETED}", SqlUtil.buildArgs(listId), null, null, null).use { cursor -> return getListByQuery("${ListTable.ID} = ? AND ${ListTable.IS_NOT_DELETED}", SqlUtil.buildArgs(listId))
}
fun getList(recipientId: RecipientId): DistributionListRecord? {
return getListByQuery("${ListTable.RECIPIENT_ID} = ? AND ${ListTable.IS_NOT_DELETED}", SqlUtil.buildArgs(recipientId))
}
fun getListByDistributionId(distributionId: DistributionId): DistributionListRecord? {
return getListByQuery("${ListTable.DISTRIBUTION_ID} = ? AND ${ListTable.IS_NOT_DELETED}", SqlUtil.buildArgs(distributionId))
}
private fun getListByQuery(query: String, args: Array<String>): DistributionListRecord? {
readableDatabase.query(ListTable.TABLE_NAME, null, query, args, null, null, null).use { cursor ->
return if (cursor.moveToFirst()) { return if (cursor.moveToFirst()) {
val id: DistributionListId = DistributionListId.from(cursor.requireLong(ListTable.ID)) val id: DistributionListId = DistributionListId.from(cursor.requireLong(ListTable.ID))
val privacyMode: DistributionListPrivacyMode = cursor.requireObject(ListTable.PRIVACY_MODE, DistributionListPrivacyMode.Serializer) val privacyMode: DistributionListPrivacyMode = cursor.requireObject(ListTable.PRIVACY_MODE, DistributionListPrivacyMode.Serializer)
@ -393,6 +405,16 @@ class DistributionListDatabase constructor(context: Context?, databaseHelper: Si
} }
} }
fun getDistributionId(recipientId: RecipientId): DistributionId? {
readableDatabase.query(ListTable.TABLE_NAME, arrayOf(ListTable.DISTRIBUTION_ID), "${ListTable.RECIPIENT_ID} = ? AND ${ListTable.IS_NOT_DELETED}", SqlUtil.buildArgs(recipientId), null, null, null).use { cursor ->
return if (cursor.moveToFirst()) {
DistributionId.from(cursor.requireString(ListTable.DISTRIBUTION_ID))
} else {
null
}
}
}
fun getMembers(listId: DistributionListId): List<RecipientId> { fun getMembers(listId: DistributionListId): List<RecipientId> {
lateinit var privacyMode: DistributionListPrivacyMode lateinit var privacyMode: DistributionListPrivacyMode
lateinit var rawMembers: List<RecipientId> lateinit var rawMembers: List<RecipientId>

View file

@ -44,7 +44,7 @@ public class JobManager implements ConstraintObserver.Notifier {
private static final String TAG = Log.tag(JobManager.class); private static final String TAG = Log.tag(JobManager.class);
public static final int CURRENT_VERSION = 8; public static final int CURRENT_VERSION = 9;
private final Application application; private final Application application;
private final Configuration configuration; private final Configuration configuration;

View file

@ -0,0 +1,67 @@
package org.thoughtcrime.securesms.jobmanager.migrations;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.JobMigration;
import org.thoughtcrime.securesms.jobs.FailingJob;
import java.util.Optional;
/**
* We removed the messageId property from the job data and replaced it with a serialized envelope,
* so we need to take jobs that referenced an ID and replace it with the envelope instead.
*/
public class SenderKeyDistributionSendJobRecipientMigration extends JobMigration {
private static final String TAG = Log.tag(SenderKeyDistributionSendJobRecipientMigration.class);
private final GroupDatabase groupDatabase;
public SenderKeyDistributionSendJobRecipientMigration() {
this(SignalDatabase.groups());
}
@VisibleForTesting
SenderKeyDistributionSendJobRecipientMigration(GroupDatabase groupDatabase) {
super(9);
this.groupDatabase = groupDatabase;
}
@Override
protected @NonNull JobData migrate(@NonNull JobData jobData) {
if ("SenderKeyDistributionSendJob".equals(jobData.getFactoryKey())) {
return migrateJob(jobData, groupDatabase);
} else {
return jobData;
}
}
private static @NonNull JobData migrateJob(@NonNull JobData jobData, @NonNull GroupDatabase groupDatabase) {
Data data = jobData.getData();
if (data.hasString("group_id")) {
GroupId groupId = GroupId.pushOrThrow(data.getStringAsBlob("group_id"));
Optional<GroupRecord> group = groupDatabase.getGroup(groupId);
if (group.isPresent()) {
return jobData.withData(data.buildUpon()
.putString("thread_recipient_id", group.get().getRecipientId().serialize())
.build());
} else {
return jobData.withFactoryKey(FailingJob.KEY);
}
} else if (!data.hasString("thread_recipient_id")) {
return jobData.withFactoryKey(FailingJob.KEY);
} else {
return jobData;
}
}
}

View file

@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdFollowUpJobMi
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration; import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration;
import org.thoughtcrime.securesms.jobmanager.migrations.RetrieveProfileJobMigration; import org.thoughtcrime.securesms.jobmanager.migrations.RetrieveProfileJobMigration;
import org.thoughtcrime.securesms.jobmanager.migrations.SendReadReceiptsJobMigration; import org.thoughtcrime.securesms.jobmanager.migrations.SendReadReceiptsJobMigration;
import org.thoughtcrime.securesms.jobmanager.migrations.SenderKeyDistributionSendJobRecipientMigration;
import org.thoughtcrime.securesms.migrations.AccountRecordMigrationJob; import org.thoughtcrime.securesms.migrations.AccountRecordMigrationJob;
import org.thoughtcrime.securesms.migrations.ApplyUnknownFieldsToSelfMigrationJob; import org.thoughtcrime.securesms.migrations.ApplyUnknownFieldsToSelfMigrationJob;
import org.thoughtcrime.securesms.migrations.AttachmentCleanupMigrationJob; import org.thoughtcrime.securesms.migrations.AttachmentCleanupMigrationJob;
@ -272,6 +273,7 @@ public final class JobManagerFactories {
new SendReadReceiptsJobMigration(SignalDatabase.mmsSms()), new SendReadReceiptsJobMigration(SignalDatabase.mmsSms()),
new PushProcessMessageQueueJobMigration(application), new PushProcessMessageQueueJobMigration(application),
new RetrieveProfileJobMigration(), new RetrieveProfileJobMigration(),
new PushDecryptMessageJobEnvelopeMigration(application)); new PushDecryptMessageJobEnvelopeMigration(application),
new SenderKeyDistributionSendJobRecipientMigration());
} }
} }

View file

@ -13,6 +13,7 @@ import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.DistributionListRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
@ -143,14 +144,28 @@ public class ResendMessageJob extends BaseJob {
Content contentToSend = content; Content contentToSend = content;
if (distributionId != null) { if (distributionId != null) {
Optional<GroupRecord> groupRecord = SignalDatabase.groups().getGroupByDistributionId(distributionId); if (groupId != null) {
Log.d(TAG, "GroupId is present. Assuming this is a group message.");
Optional<GroupRecord> groupRecord = SignalDatabase.groups().getGroupByDistributionId(distributionId);
if (!groupRecord.isPresent()) { if (!groupRecord.isPresent()) {
Log.w(TAG, "Could not find a matching group for the distributionId! Skipping message send."); Log.w(TAG, "Could not find a matching group for the distributionId! Skipping message send.");
return; return;
} else if (!groupRecord.get().getMembers().contains(recipientId)) { } else if (!groupRecord.get().getMembers().contains(recipientId)) {
Log.w(TAG, "The target user is no longer in the group! Skipping message send."); Log.w(TAG, "The target user is no longer in the group! Skipping message send.");
return; return;
}
} else {
Log.d(TAG, "GroupId is not present. Assuming this is a message for a distribution list.");
DistributionListRecord listRecord = SignalDatabase.distributionLists().getListByDistributionId(distributionId);
if (listRecord == null) {
Log.w(TAG, "Could not find a matching distribution list for the distributionId! Skipping message send.");
return;
} else if (!listRecord.getMembers().contains(recipientId)) {
Log.w(TAG, "The target user is no longer in the distribution list! Skipping message send.");
return;
}
} }
SenderKeyDistributionMessage senderKeyDistributionMessage = messageSender.getOrCreateNewGroupSession(distributionId); SenderKeyDistributionMessage senderKeyDistributionMessage = messageSender.getOrCreateNewGroupSession(distributionId);

View file

@ -6,8 +6,8 @@ import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.SignalProtocolAddress; import org.signal.libsignal.protocol.SignalProtocolAddress;
import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage; import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.DistributionListRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
@ -39,33 +39,33 @@ public final class SenderKeyDistributionSendJob extends BaseJob {
public static final String KEY = "SenderKeyDistributionSendJob"; public static final String KEY = "SenderKeyDistributionSendJob";
private static final String KEY_RECIPIENT_ID = "recipient_id"; private static final String KEY_TARGET_RECIPIENT_ID = "recipient_id";
private static final String KEY_GROUP_ID = "group_id"; private static final String KEY_THREAD_RECIPIENT_ID = "thread_recipient_id";
private final RecipientId recipientId; private final RecipientId targetRecipientId;
private final GroupId.V2 groupId; private final RecipientId threadRecipientId;
public SenderKeyDistributionSendJob(@NonNull RecipientId recipientId, @NonNull GroupId.V2 groupId) { public SenderKeyDistributionSendJob(@NonNull RecipientId targetRecipientId, RecipientId threadRecipientId) {
this(recipientId, groupId, new Parameters.Builder() this(targetRecipientId, threadRecipientId, new Parameters.Builder()
.setQueue(recipientId.toQueueKey()) .setQueue(targetRecipientId.toQueueKey())
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1)) .setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(Parameters.UNLIMITED) .setMaxAttempts(Parameters.UNLIMITED)
.setMaxInstancesForQueue(1) .setMaxInstancesForQueue(1)
.build()); .build());
} }
private SenderKeyDistributionSendJob(@NonNull RecipientId recipientId, @NonNull GroupId.V2 groupId, @NonNull Parameters parameters) { private SenderKeyDistributionSendJob(@NonNull RecipientId targetRecipientId, @NonNull RecipientId threadRecipientId, @NonNull Parameters parameters) {
super(parameters); super(parameters);
this.recipientId = recipientId; this.targetRecipientId = targetRecipientId;
this.groupId = groupId; this.threadRecipientId = threadRecipientId;
} }
@Override @Override
public @NonNull Data serialize() { public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_RECIPIENT_ID, recipientId.serialize()) return new Data.Builder().putString(KEY_TARGET_RECIPIENT_ID, targetRecipientId.serialize())
.putBlobAsString(KEY_GROUP_ID, groupId.getDecodedId()) .putString(KEY_THREAD_RECIPIENT_ID, threadRecipientId.serialize())
.build(); .build();
} }
@ -76,38 +76,62 @@ public final class SenderKeyDistributionSendJob extends BaseJob {
@Override @Override
protected void onRun() throws Exception { protected void onRun() throws Exception {
GroupDatabase groupDatabase = SignalDatabase.groups(); Recipient targetRecipient = Recipient.resolved(targetRecipientId);
Recipient threadRecipient = Recipient.resolved(threadRecipientId);
if (!groupDatabase.isCurrentMember(groupId, recipientId)) { if (targetRecipient.getSenderKeyCapability() != Recipient.Capability.SUPPORTED) {
Log.w(TAG, recipientId + " is no longer a member of " + groupId + "! Not sending."); Log.w(TAG, targetRecipientId + " does not support sender key! Not sending.");
return; return;
} }
Recipient recipient = Recipient.resolved(recipientId); if (targetRecipient.isUnregistered()) {
Log.w(TAG, threadRecipient.getId() + " not registered!");
if (recipient.getSenderKeyCapability() != Recipient.Capability.SUPPORTED) {
Log.w(TAG, recipientId + " does not support sender key! Not sending.");
return; return;
} }
if (recipient.isUnregistered()) { GroupId.V2 groupId;
Log.w(TAG, recipient.getId() + " not registered!"); DistributionId distributionId;
if (threadRecipient.isPushV2Group()) {
groupId = threadRecipient.requireGroupId().requireV2();
distributionId = SignalDatabase.groups().getOrCreateDistributionId(groupId);
} else if (threadRecipient.isDistributionList()) {
groupId = null;
distributionId = SignalDatabase.distributionLists().getDistributionId(threadRecipientId);
} else {
warn(TAG, "Recipient is not a group or distribution list! Skipping.");
return; return;
} }
if (distributionId == null) {
warn(TAG, "Failed to find a distributionId! Skipping.");
return;
}
if (groupId != null && !SignalDatabase.groups().isCurrentMember(groupId, targetRecipientId)) {
Log.w(TAG, targetRecipientId + " is no longer a member of " + groupId + "! Not sending.");
return;
} else if (groupId == null) {
DistributionListRecord listRecord = SignalDatabase.distributionLists().getList(threadRecipientId);
if (listRecord == null || !listRecord.getMembers().contains(targetRecipientId)) {
Log.w(TAG, targetRecipientId + " is no longer a member of the distribution list! Not sending.");
return;
}
}
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender(); SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
List<SignalServiceAddress> address = Collections.singletonList(RecipientUtil.toSignalServiceAddress(context, recipient)); List<SignalServiceAddress> address = Collections.singletonList(RecipientUtil.toSignalServiceAddress(context, targetRecipient));
DistributionId distributionId = groupDatabase.getOrCreateDistributionId(groupId); SenderKeyDistributionMessage message = messageSender.getOrCreateNewGroupSession(distributionId);
SenderKeyDistributionMessage message = messageSender.getOrCreateNewGroupSession(distributionId); List<Optional<UnidentifiedAccessPair>> access = UnidentifiedAccessUtil.getAccessFor(context, Collections.singletonList(targetRecipient));
List<Optional<UnidentifiedAccessPair>> access = UnidentifiedAccessUtil.getAccessFor(context, Collections.singletonList(recipient));
SendMessageResult result = messageSender.sendSenderKeyDistributionMessage(distributionId, address, access, message, Optional.of(groupId.getDecodedId()), false).get(0); SendMessageResult result = messageSender.sendSenderKeyDistributionMessage(distributionId, address, access, message, Optional.ofNullable(groupId).map(GroupId::getDecodedId), false).get(0);
if (result.isSuccess()) { if (result.isSuccess()) {
List<SignalProtocolAddress> addresses = result.getSuccess() List<SignalProtocolAddress> addresses = result.getSuccess()
.getDevices() .getDevices()
.stream() .stream()
.map(device -> recipient.requireServiceId().toProtocolAddress(device)) .map(device -> targetRecipient.requireServiceId().toProtocolAddress(device))
.collect(Collectors.toList()); .collect(Collectors.toList());
ApplicationDependencies.getProtocolStore().aci().markSenderKeySharedWith(distributionId, addresses); ApplicationDependencies.getProtocolStore().aci().markSenderKeySharedWith(distributionId, addresses);
@ -128,8 +152,8 @@ public final class SenderKeyDistributionSendJob extends BaseJob {
@Override @Override
public @NonNull SenderKeyDistributionSendJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull SenderKeyDistributionSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new SenderKeyDistributionSendJob(RecipientId.from(data.getString(KEY_RECIPIENT_ID)), return new SenderKeyDistributionSendJob(RecipientId.from(data.getString(KEY_TARGET_RECIPIENT_ID)),
GroupId.pushOrThrow(data.getStringAsBlob(KEY_GROUP_ID)).requireV2(), RecipientId.from(data.getString(KEY_THREAD_RECIPIENT_ID)),
parameters); parameters);
} }
} }

View file

@ -673,7 +673,7 @@ public final class GroupSendUtil {
@Override @Override
public @NonNull ContentHint getContentHint() { public @NonNull ContentHint getContentHint() {
return ContentHint.RESENDABLE; return ContentHint.IMPLICIT;
} }
@Override @Override

View file

@ -49,6 +49,7 @@ import org.thoughtcrime.securesms.database.SentStorySyncManifest;
import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase; import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.DistributionListRecord;
import org.thoughtcrime.securesms.database.model.Mention; import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.database.model.MessageLogEntry; import org.thoughtcrime.securesms.database.model.MessageLogEntry;
@ -2683,13 +2684,27 @@ public final class MessageContentProcessor {
return; return;
} }
if (!threadRecipient.isPushV2Group()) { if (!threadRecipient.isPushV2Group() && !threadRecipient.isDistributionList()) {
warn(content.getTimestamp(), "[RetryReceipt-SK] Thread recipient is not a v2 group! Skipping."); warn(content.getTimestamp(), "[RetryReceipt-SK] Thread recipient is not a V2 group or distribution list! Skipping.");
return;
}
DistributionId distributionId;
GroupId.V2 groupId;
if (threadRecipient.isGroup()) {
groupId = threadRecipient.requireGroupId().requireV2();
distributionId = SignalDatabase.groups().getOrCreateDistributionId(groupId);
} else {
groupId = null;
distributionId = SignalDatabase.distributionLists().getDistributionId(threadRecipient.getId());
}
if (distributionId == null) {
Log.w(TAG, "[RetryReceipt-SK] Failed to find a distributionId! Skipping.");
return; return;
} }
GroupId.V2 groupId = threadRecipient.requireGroupId().requireV2();
DistributionId distributionId = SignalDatabase.groups().getOrCreateDistributionId(groupId);
SignalProtocolAddress requesterAddress = new SignalProtocolAddress(requester.requireServiceId().toString(), content.getSenderDevice()); SignalProtocolAddress requesterAddress = new SignalProtocolAddress(requester.requireServiceId().toString(), content.getSenderDevice());
SignalDatabase.senderKeyShared().delete(distributionId, Collections.singleton(requesterAddress)); SignalDatabase.senderKeyShared().delete(distributionId, Collections.singleton(requesterAddress));
@ -2705,22 +2720,8 @@ public final class MessageContentProcessor {
groupId, groupId,
distributionId)); distributionId));
} else { } else {
warn(content.getTimestamp(), "[RetryReceipt-SK] Unable to find MSL entry for " + requester.getId() + " (" + requesterAddress + ") with timestamp " + sentTimestamp + "."); warn(content.getTimestamp(), "[RetryReceipt-SK] Unable to find MSL entry for " + requester.getId() + " (" + requesterAddress + ") with timestamp " + sentTimestamp + " for " + (groupId != null ? "group " + groupId : "distribution list") + ". Scheduling a job to send them the SenderKeyDistributionMessage. Membership will be checked there.");
ApplicationDependencies.getJobManager().add(new SenderKeyDistributionSendJob(requester.getId(), threadRecipient.getId()));
Optional<GroupRecord> groupRecord = SignalDatabase.groups().getGroup(groupId);
if (!groupRecord.isPresent()) {
warn(content.getTimestamp(), "[RetryReceipt-SK] Could not find a record for the group!");
return;
}
if (!groupRecord.get().getMembers().contains(requester.getId())) {
warn(content.getTimestamp(), "[RetryReceipt-SK] The requester is not in the group, so we cannot send them a SenderKeyDistributionMessage.");
return;
}
warn(content.getTimestamp(), "[RetryReceipt-SK] The requester is in the group, so we'll send them a SenderKeyDistributionMessage.");
ApplicationDependencies.getJobManager().add(new SenderKeyDistributionSendJob(requester.getId(), groupRecord.get().getId().requireV2()));
} }
} }

View file

@ -0,0 +1,85 @@
package org.thoughtcrime.securesms.jobmanager.migrations;
import org.junit.Test;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.JobMigration;
import org.thoughtcrime.securesms.jobs.FailingJob;
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
import org.thoughtcrime.securesms.jobs.SenderKeyDistributionSendJob;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Util;
import java.util.ArrayList;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class SenderKeyDistributionSendJobRecipientMigrationTest {
private final GroupDatabase mockDatabase = mock(GroupDatabase.class);
private final SenderKeyDistributionSendJobRecipientMigration testSubject = new SenderKeyDistributionSendJobRecipientMigration(mockDatabase);
private static final GroupId GROUP_ID = GroupId.pushOrThrow(Util.getSecretBytes(32));
@Test
public void normalMigration() {
// GIVEN
JobMigration.JobData jobData = new JobMigration.JobData(SenderKeyDistributionSendJob.KEY,
"asdf",
new Data.Builder()
.putString("recipient_id", RecipientId.from(1).serialize())
.putBlobAsString("group_id", GROUP_ID.getDecodedId())
.build());
GroupDatabase.GroupRecord mockGroup = mock(GroupDatabase.GroupRecord.class);
when(mockGroup.getRecipientId()).thenReturn(RecipientId.from(2));
when(mockDatabase.getGroup(GROUP_ID)).thenReturn(Optional.of(mockGroup));
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
// THEN
assertEquals(RecipientId.from(1).serialize(), result.getData().getString("recipient_id"));
assertEquals(RecipientId.from(2).serialize(), result.getData().getString("thread_recipient_id"));
}
@Test
public void cannotFindGroup() {
// GIVEN
JobMigration.JobData jobData = new JobMigration.JobData(SenderKeyDistributionSendJob.KEY,
"asdf",
new Data.Builder()
.putString("recipient_id", RecipientId.from(1).serialize())
.putBlobAsString("group_id", GROUP_ID.getDecodedId())
.build());
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
// THEN
assertEquals(FailingJob.KEY, result.getFactoryKey());
}
@Test
public void missingGroupId() {
// GIVEN
JobMigration.JobData jobData = new JobMigration.JobData(SenderKeyDistributionSendJob.KEY,
"asdf",
new Data.Builder()
.putString("recipient_id", RecipientId.from(1).serialize())
.build());
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
// THEN
assertEquals(FailingJob.KEY, result.getFactoryKey());
}
}

View file

@ -1745,7 +1745,7 @@ public class SignalServiceMessageSender {
if (content.getContent().isPresent() && content.getContent().get().getSyncMessage() != null && content.getContent().get().getSyncMessage().hasSent()) { if (content.getContent().isPresent() && content.getContent().get().getSyncMessage() != null && content.getContent().get().getSyncMessage().hasSent()) {
Log.d(TAG, "[sendMessage][" + timestamp + "] Sending a sent sync message to devices: " + messages.getDevices()); Log.d(TAG, "[sendMessage][" + timestamp + "] Sending a sent sync message to devices: " + messages.getDevices());
} else if (content.getContent().isPresent() && content.getContent().get().hasSenderKeyDistributionMessage()) { } else if (content.getContent().isPresent() && content.getContent().get().hasSenderKeyDistributionMessage()) {
Log.d(TAG, "[sendMessage][" + timestamp + "] Sending a SKDM to " + messages.getDestination() + " for devices: " + messages.getDevices()); Log.d(TAG, "[sendMessage][" + timestamp + "] Sending a SKDM to " + messages.getDestination() + " for devices: " + messages.getDevices() + (content.getContent().get().getDataMessage() != null ? " (it's piggy-backing on a DataMessage)" : ""));
} }
if (cancelationSignal != null && cancelationSignal.isCanceled()) { if (cancelationSignal != null && cancelationSignal.isCanceled()) {

View file

@ -560,8 +560,8 @@ public final class SignalServiceContent {
metadata.getGroupId(), metadata.getGroupId(),
metadata.getDestinationUuid(), metadata.getDestinationUuid(),
serviceContentProto); serviceContentProto);
} else if (senderKeyDistributionMessage.isPresent()) { } else if (message.hasStoryMessage()) {
return new SignalServiceContent(senderKeyDistributionMessage.get(), return new SignalServiceContent(createStoryMessage(message.getStoryMessage()),
metadata.getSender(), metadata.getSender(),
metadata.getSenderDevice(), metadata.getSenderDevice(),
metadata.getTimestamp(), metadata.getTimestamp(),
@ -572,8 +572,9 @@ public final class SignalServiceContent {
metadata.getGroupId(), metadata.getGroupId(),
metadata.getDestinationUuid(), metadata.getDestinationUuid(),
serviceContentProto); serviceContentProto);
} else if (message.hasStoryMessage()) { } else if (senderKeyDistributionMessage.isPresent()) {
return new SignalServiceContent(createStoryMessage(message.getStoryMessage()), // IMPORTANT: This block should always be last, since you can pair SKDM's with other content
return new SignalServiceContent(senderKeyDistributionMessage.get(),
metadata.getSender(), metadata.getSender(),
metadata.getSenderDevice(), metadata.getSenderDevice(),
metadata.getTimestamp(), metadata.getTimestamp(),