From cc369f5c52fba21ec34c1d7896959534fc754c20 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 4 Feb 2020 10:09:32 +1100 Subject: [PATCH] Fix message syncing in group chats --- .../securesms/jobs/PushGroupSendJob.java | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index cbb583f793..2bc5241f58 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; @@ -35,6 +36,7 @@ import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceMessageSender; @@ -56,6 +58,7 @@ import org.whispersystems.signalservice.loki.utilities.PromiseUtil; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -163,8 +166,13 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList(); else target = getGroupMessageRecipients(message.getRecipient().getAddress().toGroupString(), messageId); + String localNumber = TextSecurePreferences.getLocalNumber(context); + // Only send messages to the contacts we have sessions with List
validTargets = Stream.of(target).filter(member -> { + // Our device is always valid + if (member.serialize().equalsIgnoreCase(localNumber)) { return true; } + SignalProtocolAddress protocolAddress = new SignalProtocolAddress(member.toPhoneString(), SignalServiceAddress.DEFAULT_DEVICE_ID); boolean hasSession = new TextSecureSessionStore(context).containsSession(protocolAddress); if (hasSession) { return true; } @@ -330,18 +338,37 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { } return result; } else { + /* + Our biggest assumption here is that group members will only consist of primary devices. + No secondary device should be able to be added to a group. + */ List destinations = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId); - if (!destinations.isEmpty()) return Stream.of(destinations).map(GroupReceiptInfo::getAddress).toList(); - List members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false); + Set
memberSet = new HashSet<>(); + if (destinations.isEmpty()) { + List groupMembers = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false); + memberSet.addAll(Stream.of(groupMembers).map(Recipient::getAddress).toList()); + } else { + memberSet.addAll(Stream.of(destinations).map(GroupReceiptInfo::getAddress).toList()); + } - // Add secondary devices to the list - Set
memberSet = Stream.of(members).map(Recipient::getAddress).collect(Collectors.toSet()); - for (Recipient member : members) { - if (!member.getAddress().isPhone()) { continue; } + // Replace primary device public key with ours so message syncing works correctly + String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context); + String localNumber = TextSecurePreferences.getLocalNumber(context); + if (masterHexEncodedPublicKey != null && memberSet.contains(Address.fromSerialized(masterHexEncodedPublicKey))) { + memberSet.remove(Address.fromSerialized(masterHexEncodedPublicKey)); + memberSet.add(Address.fromSerialized(localNumber)); + } + + // Add secondary devices to the list. We shouldn't add our secondary devices + for (Address member : memberSet) { + if (!member.isPhone() || member.serialize().equalsIgnoreCase(localNumber)) { continue; } try { - List secondaryDevices = PromiseUtil.timeout(LokiStorageAPI.shared.getSecondaryDevicePublicKeys(member.getAddress().serialize()), 5000).get(); - memberSet.addAll(Stream.of(secondaryDevices).map(Address::fromSerialized).toList()); + List secondaryDevices = PromiseUtil.timeout(LokiStorageAPI.shared.getSecondaryDevicePublicKeys(member.serialize()), 5000).get(); + memberSet.addAll(Stream.of(secondaryDevices).map(string -> { + // Loki - Calling .map(Address::fromSerialized) is causing errors, thus we use the long method :( + return Address.fromSerialized(string); + }).toList()); } catch (Exception e) { // Timed out, go to the next member }