Skip to content

Commit

Permalink
GV2 message contexts.
Browse files Browse the repository at this point in the history
  • Loading branch information
alan-signal authored and alex-signal committed May 14, 2020
1 parent f099c35 commit b8df905
Show file tree
Hide file tree
Showing 14 changed files with 287 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private void bind(@NonNull MessageRecord messageRecord, @NonNull Locale locale)
}

if (this.messageRecord != null && messageRecord.isGroupAction()) {
GroupUtil.getDescription(getContext(), messageRecord.getBody()).removeObserver(this);
GroupUtil.getDescription(getContext(), messageRecord.getBody(), messageRecord.isGroupV2()).removeObserver(this);
}

this.messageRecord = messageRecord;
Expand All @@ -113,7 +113,7 @@ private void bind(@NonNull MessageRecord messageRecord, @NonNull Locale locale)
this.sender.observeForever(this);

if (this.messageRecord != null && messageRecord.isGroupAction()) {
GroupUtil.getDescription(getContext(), messageRecord.getBody()).addObserver(this);
GroupUtil.getDescription(getContext(), messageRecord.getBody(), messageRecord.isGroupV2()).addObserver(this);
}

present(messageRecord);
Expand Down Expand Up @@ -236,7 +236,7 @@ public void unbind() {
sender.removeForeverObserver(this);
}
if (this.messageRecord != null && messageRecord.isGroupAction()) {
GroupUtil.getDescription(getContext(), messageRecord.getBody()).removeObserver(this);
GroupUtil.getDescription(getContext(), messageRecord.getBody(), messageRecord.isGroupV2()).removeObserver(this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,11 @@ public long insertMessageOutbox(@NonNull OutgoingMediaMessage message,
MessageGroupContext.GroupV2Properties groupV2Properties = outgoingGroupUpdateMessage.requireGroupV2Properties();
members.addAll(Stream.of(groupV2Properties.getActiveMembers()).map(recipientDatabase::getOrInsertFromUuid).toList());
if (groupV2Properties.isUpdate()) {
members.addAll(Stream.of(groupV2Properties.getPendingMembers()).map(recipientDatabase::getOrInsertFromUuid).toList());
members.addAll(Stream.concat(Stream.of(groupV2Properties.getPendingMembers()),
Stream.of(groupV2Properties.getRemovedMembers()))
.distinct()
.map(recipientDatabase::getOrInsertFromUuid)
.toList());
}
members.remove(Recipient.self().getId());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,9 +616,14 @@ protected Optional<InsertResult> insertMessageInbox(IncomingTextMessage message,
} else if (message.isSecureMessage()) {
type |= Types.SECURE_MESSAGE_BIT;
} else if (message.isGroup()) {
IncomingGroupUpdateMessage incomingGroupUpdateMessage = (IncomingGroupUpdateMessage) message;

type |= Types.SECURE_MESSAGE_BIT;
if (((IncomingGroupUpdateMessage)message).isUpdate()) type |= Types.GROUP_UPDATE_BIT;
else if (((IncomingGroupUpdateMessage)message).isQuit()) type |= Types.GROUP_QUIT_BIT;

if (incomingGroupUpdateMessage.isGroupV2()) type |= Types.GROUP_V2_BIT | Types.GROUP_UPDATE_BIT;
else if (incomingGroupUpdateMessage.isUpdate()) type |= Types.GROUP_UPDATE_BIT;
else if (incomingGroupUpdateMessage.isQuit()) type |= Types.GROUP_QUIT_BIT;

} else if (message.isEndSession()) {
type |= Types.SECURE_MESSAGE_BIT;
type |= Types.END_SESSION_BIT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
package org.thoughtcrime.securesms.database.model;

import android.content.Context;
import androidx.annotation.NonNull;
import android.text.SpannableString;

import androidx.annotation.NonNull;

import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
Expand Down Expand Up @@ -111,6 +112,10 @@ public boolean isGroupUpdate() {
return SmsDatabase.Types.isGroupUpdate(type);
}

public boolean isGroupV2() {
return SmsDatabase.Types.isGroupV2(type);
}

public boolean isGroupQuit() {
return SmsDatabase.Types.isGroupQuit(type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public SpannableString getDisplayBody(@NonNull Context context) {
if (isGroupUpdate() && isOutgoing()) {
return new SpannableString(context.getString(R.string.MessageRecord_you_updated_group));
} else if (isGroupUpdate()) {
return new SpannableString(GroupUtil.getDescription(context, getBody()).toString(getIndividualRecipient()));
return new SpannableString(GroupUtil.getDescription(context, getBody(), false).toString(getIndividualRecipient()));
} else if (isGroupQuit() && isOutgoing()) {
return new SpannableString(context.getString(R.string.MessageRecord_left_group));
} else if (isGroupQuit()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.groups;

import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;

import androidx.annotation.NonNull;
Expand Down Expand Up @@ -30,8 +29,6 @@
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
Expand Down Expand Up @@ -154,7 +151,7 @@ private static GroupActionResult sendGroupUpdate(@NonNull Context cont
static boolean leaveGroup(@NonNull Context context, @NonNull GroupId.V1 groupId) {
Recipient groupRecipient = Recipient.externalGroup(context, groupId);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
Optional<OutgoingGroupUpdateMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient);
Optional<OutgoingGroupUpdateMessage> leaveMessage = createGroupLeaveMessage(context, groupId, groupRecipient);

if (threadId != -1 && leaveMessage.isPresent()) {
try {
Expand All @@ -180,7 +177,7 @@ static boolean silentLeaveGroup(@NonNull Context context, @NonNull GroupId.V1 gr
if (DatabaseFactory.getGroupDatabase(context).isActive(groupId)) {
Recipient groupRecipient = Recipient.externalGroup(context, groupId);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
Optional<OutgoingGroupUpdateMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient);
Optional<OutgoingGroupUpdateMessage> leaveMessage = createGroupLeaveMessage(context, groupId, groupRecipient);

if (threadId != -1 && leaveMessage.isPresent()) {
ApplicationDependencies.getJobManager().add(LeaveGroupJob.create(groupRecipient));
Expand Down Expand Up @@ -210,4 +207,32 @@ static void updateGroupTimer(@NonNull Context context, @NonNull GroupId.V1 group
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(recipient, System.currentTimeMillis(), expirationTime * 1000L);
MessageSender.send(context, outgoingMessage, threadId, false, null);
}

@WorkerThread
private static Optional<OutgoingGroupUpdateMessage> createGroupLeaveMessage(@NonNull Context context,
@NonNull GroupId.V1 groupId,
@NonNull Recipient groupRecipient)
{
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);

if (!groupDatabase.isActive(groupId)) {
Log.w(TAG, "Group has already been left.");
return Optional.absent();
}

GroupContext groupContext = GroupContext.newBuilder()
.setId(ByteString.copyFrom(groupId.getDecodedId()))
.setType(GroupContext.Type.QUIT)
.build();

return Optional.of(new OutgoingGroupUpdateMessage(groupRecipient,
groupContext,
null,
System.currentTimeMillis(),
0,
false,
null,
Collections.emptyList(),
Collections.emptyList()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
import androidx.annotation.WorkerThread;

import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
import org.signal.zkgroup.VerificationFailedException;
import org.signal.zkgroup.groups.GroupMasterKey;
import org.signal.zkgroup.groups.GroupSecretParams;
import org.signal.zkgroup.util.UUIDUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
Expand All @@ -29,10 +32,14 @@
import org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.IncomingGroupUpdateMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupHistoryEntry;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api;
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.exceptions.NotInGroupException;

import java.io.IOException;
Expand Down Expand Up @@ -132,7 +139,14 @@ public GroupUpdateResult updateLocalGroupToRevision(final int revision,
return new GroupUpdateResult(GroupState.GROUP_CONSISTENT_OR_AHEAD, null);
}

GlobalGroupState inputGroupState = queryServer();
GlobalGroupState inputGroupState;
try {
inputGroupState = queryServer();
} catch (GroupNotAMemberException e) {
Log.w(TAG, "Unable to query server for group " + groupId + " server says we're not in group, inserting leave message");
insertGroupLeave();
throw e;
}
AdvanceGroupStateResult advanceGroupStateResult = GroupStateMapper.partiallyAdvanceGroupState(inputGroupState, revision);
DecryptedGroup newLocalState = advanceGroupStateResult.getNewGlobalGroupState().getLocalState();

Expand All @@ -152,6 +166,49 @@ public GroupUpdateResult updateLocalGroupToRevision(final int revision,
return new GroupUpdateResult(GroupState.GROUP_UPDATED, newLocalState);
}

private void insertGroupLeave() {
if (!groupDatabase.isActive(groupId)) {
Log.w(TAG, "Group has already been left.");
return;
}

Recipient groupRecipient = Recipient.externalGroup(context, groupId);
UUID selfUuid = Recipient.self().getUuid().get();
DecryptedGroup decryptedGroup = groupDatabase.requireGroup(groupId)
.requireV2GroupProperties()
.getDecryptedGroup();

DecryptedGroup simulatedGroupState = DecryptedGroupUtil.removeMember(decryptedGroup, selfUuid, decryptedGroup.getVersion() + 1);
DecryptedGroupChange simulatedGroupChange = DecryptedGroupChange.newBuilder()
.setEditor(UuidUtil.toByteString(UuidUtil.UNKNOWN_UUID))
.setVersion(simulatedGroupState.getVersion())
.addDeleteMembers(UuidUtil.toByteString(selfUuid))
.build();

DecryptedGroupV2Context decryptedGroupV2Context = GroupProtoUtil.createDecryptedGroupV2Context(masterKey, simulatedGroupState, simulatedGroupChange);
OutgoingGroupUpdateMessage leaveMessage = new OutgoingGroupUpdateMessage(groupRecipient,
decryptedGroupV2Context,
null,
System.currentTimeMillis(),
0,
false,
null,
Collections.emptyList(),
Collections.emptyList());

try {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
long id = mmsDatabase.insertMessageOutbox(leaveMessage, threadId, false, null);
mmsDatabase.markAsSent(id, true);
} catch (MmsException e) {
Log.w(TAG, "Failed to insert leave message.", e);
}

groupDatabase.setActive(groupId, false);
groupDatabase.remove(groupId, Recipient.self().getId());
}

/**
* @return true iff group exists locally and is at least the specified revision.
*/
Expand Down Expand Up @@ -252,17 +309,29 @@ private List<GroupLogEntry> getFullMemberHistory(@NonNull UUID selfUuid, int log
}

private void storeMessage(@NonNull DecryptedGroupV2Context decryptedGroupV2Context, long timestamp) {
try {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
RecipientId recipientId = recipientDatabase.getOrInsertFromGroupId(groupId);
Recipient recipient = Recipient.resolved(recipientId);
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(recipient, decryptedGroupV2Context, null, timestamp, 0, false, null, Collections.emptyList(), Collections.emptyList());
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);

mmsDatabase.markAsSent(messageId, true);
} catch (MmsException e) {
Log.w(TAG, e);
UUID editor = DecryptedGroupUtil.editorUuid(decryptedGroupV2Context.getChange());
boolean outgoing = Recipient.self().getUuid().get().equals(editor);

if (outgoing) {
try {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
RecipientId recipientId = recipientDatabase.getOrInsertFromGroupId(groupId);
Recipient recipient = Recipient.resolved(recipientId);
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(recipient, decryptedGroupV2Context, null, timestamp, 0, false, null, Collections.emptyList(), Collections.emptyList());
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);

mmsDatabase.markAsSent(messageId, true);
} catch (MmsException e) {
Log.w(TAG, e);
}
} else {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
RecipientId sender = Recipient.externalPush(context, editor, null).getId();
IncomingTextMessage incoming = new IncomingTextMessage(sender, -1, timestamp, timestamp, "", Optional.of(groupId), 0, false);
IncomingGroupUpdateMessage groupMessage = new IncomingGroupUpdateMessage(incoming, decryptedGroupV2Context);

smsDatabase.insertMessageInbox(groupMessage);
}
}
}
Expand Down

0 comments on commit b8df905

Please sign in to comment.