Refactor GroupMessageProcessor

pull/192/head
nielsandriesse 5 years ago
parent 439bdac7f4
commit 04e14dd5dd

@ -8,7 +8,6 @@ import android.support.annotation.Nullable;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
@ -18,26 +17,23 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob; import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage; import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type; import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
import org.whispersystems.signalservice.loki.protocol.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -45,8 +41,6 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import kotlin.Unit;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer; import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
@ -104,13 +98,12 @@ public class GroupMessageProcessor {
} }
} }
// We should only create the group if we are part of the member list // Loki - Ignore message if needed
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, TextSecurePreferences.getLocalNumber(context)); if (ClosedGroupsProtocol.shouldIgnoreMessage(context, group)) {
if (members == null || !members.contains(Address.fromSerialized(hexEncodedPublicKey))) {
Log.d("Loki - Group Message", "Received a group create message which doesn't include us in the member list. Ignoring.");
return null; return null;
} }
// Loki - Parse admins
if (group.getAdmins().isPresent()) { if (group.getAdmins().isPresent()) {
for (String admin : group.getAdmins().get()) { for (String admin : group.getAdmins().get()) {
admins.add(Address.fromExternal(context, admin)); admins.add(Address.fromExternal(context, admin));
@ -121,7 +114,7 @@ public class GroupMessageProcessor {
avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins); avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins);
if (group.getMembers().isPresent()) { if (group.getMembers().isPresent()) {
establishSessionsWithMembersIfNeeded(context, group.getMembers().get()); ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
} }
return storeMessage(context, content, group, builder.build(), outgoing); return storeMessage(context, content, group, builder.build(), outgoing);
@ -137,21 +130,21 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group); String id = GroupUtil.getEncodedId(group);
String ourHexEncodedPublicKey = getMasterHexEncodedPublicKey(context, TextSecurePreferences.getLocalNumber(context)); String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) { if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) {
// Only update group if the group admin sent the message // Loki - Only update the group if the group admin sent the message
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender()); String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
if (!groupRecord.getAdmins().contains(Address.fromSerialized(hexEncodedPublicKey))) { if (!groupRecord.getAdmins().contains(Address.fromSerialized(masterDevice))) {
Log.d("Loki - Group Message", "Received a group update message from a non-admin user for " + id +". Ignoring."); Log.d("Loki", "Received a group update message from a non-admin user for: " + id +"; ignoring.");
return null; return null;
} }
// We should only process update messages if we're in the group // Loki - Only process update messages if we're part of the group
Address ourAddress = Address.fromSerialized(ourHexEncodedPublicKey); Address userMasterDeviceAddress = Address.fromSerialized(userMasterDevice);
if (!groupRecord.getMembers().contains(ourAddress) && if (!groupRecord.getMembers().contains(userMasterDeviceAddress) &&
!group.getMembers().or(Collections.emptyList()).contains(ourHexEncodedPublicKey)) { !group.getMembers().or(Collections.emptyList()).contains(userMasterDevice)) {
Log.d("Loki - Group Message", "Received a group update message from a group we are not a member of: " + id + "; ignoring."); Log.d("Loki", "Received a group update message from a group we're not a member of: " + id + "; ignoring.");
database.setActive(id, false); database.setActive(id, false);
return null; return null;
} }
@ -180,8 +173,8 @@ public class GroupMessageProcessor {
database.updateMembers(id, new LinkedList<>(newMembers)); database.updateMembers(id, new LinkedList<>(newMembers));
} }
// We add any new or removed members to the group context // Add any new or removed members to the group context.
// This will allow us later to iterate over them to check if they left or were added for UI purposes // This will allow us later to iterate over them to check if they left or were added for UI purposes.
for (Address addedMember : addedMembers) { for (Address addedMember : addedMembers) {
builder.addNewMembers(addedMember.serialize()); builder.addNewMembers(addedMember.serialize());
} }
@ -200,13 +193,13 @@ public class GroupMessageProcessor {
} }
// If we were removed then we need to disable the chat // If we were removed then we need to disable the chat
if (removedMembers.contains(Address.fromSerialized(ourHexEncodedPublicKey))) { if (removedMembers.contains(Address.fromSerialized(userMasterDevice))) {
database.setActive(id, false); database.setActive(id, false);
} else { } else {
if (!groupRecord.isActive()) database.setActive(id, true); if (!groupRecord.isActive()) database.setActive(id, true);
if (group.getMembers().isPresent()) { if (group.getMembers().isPresent()) {
establishSessionsWithMembersIfNeeded(context, group.getMembers().get()); ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
} }
} }
@ -218,8 +211,8 @@ public class GroupMessageProcessor {
@NonNull SignalServiceGroup group, @NonNull SignalServiceGroup group,
@NonNull GroupRecord record) @NonNull GroupRecord record)
{ {
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender()); String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
if (record.getMembers().contains(Address.fromSerialized(hexEncodedPublicKey))) { if (record.getMembers().contains(Address.fromSerialized(masterDevice))) {
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
.getJobManager() .getJobManager()
.add(new PushGroupUpdateJob(content.getSender(), group.getGroupId())); .add(new PushGroupUpdateJob(content.getSender(), group.getGroupId()));
@ -240,9 +233,9 @@ public class GroupMessageProcessor {
GroupContext.Builder builder = createGroupContext(group); GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.QUIT); builder.setType(GroupContext.Type.QUIT);
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender()); String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
if (members.contains(Address.fromExternal(context, hexEncodedPublicKey))) { if (members.contains(Address.fromExternal(context, masterDevice))) {
database.remove(id, Address.fromExternal(context, hexEncodedPublicKey)); database.remove(id, Address.fromExternal(context, masterDevice));
if (outgoing) database.setActive(id, false); if (outgoing) database.setActive(id, false);
return storeMessage(context, content, group, builder.build(), outgoing); return storeMessage(context, content, group, builder.build(), outgoing);
@ -322,32 +315,4 @@ public class GroupMessageProcessor {
return builder; return builder;
} }
private static String getMasterHexEncodedPublicKey(Context context, String hexEncodedPublicKey) {
String ourPublicKey = TextSecurePreferences.getLocalNumber(context);
try {
String masterHexEncodedPublicKey = hexEncodedPublicKey.equalsIgnoreCase(ourPublicKey)
? TextSecurePreferences.getMasterHexEncodedPublicKey(context)
: PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(hexEncodedPublicKey), 5000).get();
return masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : hexEncodedPublicKey;
} catch (Exception e) {
return hexEncodedPublicKey;
}
}
private static void establishSessionsWithMembersIfNeeded(Context context, List<String> members) {
String ourNumber = TextSecurePreferences.getLocalNumber(context);
for (String member : members) {
// Make sure we have session with all of the members secondary devices
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(member).success(devices -> {
if (devices.contains(ourNumber)) { return Unit.INSTANCE; }
for (String device : devices) {
SignalProtocolAddress protocolAddress = new SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID);
boolean haveSession = new TextSecureSessionStore(context).containsSession(protocolAddress);
if (!haveSession) { MessageSender.sendBackgroundSessionRequest(context, device); }
}
return Unit.INSTANCE;
});
}
}
} }

@ -42,7 +42,7 @@ public abstract class PushReceivedJob extends BaseJob {
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType()); Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());
} }
} catch (Exception e) { } catch (Exception e) {
Log.d("Loki", "Failed to process envelope: " + e); Log.d("Loki", "Failed to process envelope due to error: " + e);
} }
} }
} }

@ -1,16 +1,27 @@
package org.thoughtcrime.securesms.loki.protocol package org.thoughtcrime.securesms.loki.protocol
import android.content.Context import android.content.Context
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore
import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.sms.MessageSender import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.GroupUtil import org.thoughtcrime.securesms.util.GroupUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.libsignal.SignalProtocolAddress
import org.whispersystems.signalservice.api.messages.SignalServiceGroup
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
object ClosedGroupsProtocol { object ClosedGroupsProtocol {
@JvmStatic
fun shouldIgnoreMessage(context: Context, group: SignalServiceGroup): Boolean {
val members = group.members
val masterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
return !members.isPresent || !members.get().contains(masterDevice)
}
@JvmStatic @JvmStatic
fun getDestinations(groupID: String, context: Context): List<Address> { fun getDestinations(groupID: String, context: Context): List<Address> {
if (GroupUtil.isRSSFeed(groupID)) { return listOf() } if (GroupUtil.isRSSFeed(groupID)) { return listOf() }
@ -51,4 +62,26 @@ object ClosedGroupsProtocol {
groupDatabase.remove(groupID, Address.fromSerialized(publicKeyToUse)) groupDatabase.remove(groupID, Address.fromSerialized(publicKeyToUse))
return true return true
} }
@JvmStatic
fun establishSessionsWithMembersIfNeeded(context: Context, members: List<String>) {
val allDevices = members.flatMap { member ->
MultiDeviceProtocol.shared.getAllLinkedDevices(member)
}.toMutableSet()
val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
if (masterPublicKey != null && allDevices.contains(masterPublicKey)) {
allDevices.remove(masterPublicKey)
}
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
if (userPublicKey != null && allDevices.contains(userPublicKey)) {
allDevices.remove(userPublicKey)
}
for (device in allDevices) {
val address = SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID)
val hasSession = TextSecureSessionStore(context).containsSession(address)
if (!hasSession) {
MessageSender.sendBackgroundSessionRequest(context, device)
}
}
}
} }
Loading…
Cancel
Save