Ensure group membership for typing indicators.
This commit is contained in:
parent
68d29d9a0f
commit
c6dd25a119
7 changed files with 149 additions and 13 deletions
|
@ -27,10 +27,10 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
@ -200,9 +200,9 @@ public final class GroupDatabase extends Database {
|
||||||
|
|
||||||
try (Cursor cursor = database.query(table, null, query, args, null, null, orderBy)) {
|
try (Cursor cursor = database.query(table, null, query, args, null, null, orderBy)) {
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
List<String> members = Util.split(cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)), ",");
|
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS));
|
||||||
|
|
||||||
if (members.contains(recipientId.serialize())) {
|
if (RecipientId.serializedListContains(serializedMembers, recipientId)) {
|
||||||
groups.add(new Reader(cursor).getCurrent());
|
groups.add(new Reader(cursor).getCurrent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,6 +452,23 @@ public final class GroupDatabase extends Database {
|
||||||
database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId.toString()});
|
database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId.toString()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
public boolean isCurrentMember(@NonNull GroupId.Push groupId, @NonNull RecipientId recipientId) {
|
||||||
|
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||||
|
|
||||||
|
try (Cursor cursor = database.query(TABLE_NAME, new String[] {MEMBERS},
|
||||||
|
GROUP_ID + " = ?", new String[] {groupId.toString()},
|
||||||
|
null, null, null))
|
||||||
|
{
|
||||||
|
if (cursor.moveToNext()) {
|
||||||
|
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS));
|
||||||
|
return RecipientId.serializedListContains(serializedMembers, recipientId);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String serializeV2GroupMembers(@NonNull Context context, @NonNull DecryptedGroup decryptedGroup) {
|
private static String serializeV2GroupMembers(@NonNull Context context, @NonNull DecryptedGroup decryptedGroup) {
|
||||||
List<RecipientId> groupMembers = new ArrayList<>(decryptedGroup.getMembersCount());
|
List<RecipientId> groupMembers = new ArrayList<>(decryptedGroup.getMembersCount());
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ public abstract class GroupId {
|
||||||
private static final int MMS_BYTE_LENGTH = 16;
|
private static final int MMS_BYTE_LENGTH = 16;
|
||||||
private static final int V1_MMS_BYTE_LENGTH = 16;
|
private static final int V1_MMS_BYTE_LENGTH = 16;
|
||||||
private static final int V2_BYTE_LENGTH = GroupIdentifier.SIZE;
|
private static final int V2_BYTE_LENGTH = GroupIdentifier.SIZE;
|
||||||
private static final int V2_ENCODED_LENGTH = ENCODED_SIGNAL_GROUP_PREFIX.length() + V2_BYTE_LENGTH * 2;
|
|
||||||
|
|
||||||
private final String encodedId;
|
private final String encodedId;
|
||||||
|
|
||||||
|
@ -63,6 +62,10 @@ public abstract class GroupId {
|
||||||
.getGroupIdentifier());
|
.getGroupIdentifier());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GroupId.Push push(byte[] bytes) {
|
||||||
|
return bytes.length == V2_BYTE_LENGTH ? v2(bytes) : v1(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
public static @NonNull GroupId parse(@NonNull String encodedGroupId) {
|
public static @NonNull GroupId parse(@NonNull String encodedGroupId) {
|
||||||
try {
|
try {
|
||||||
if (!isEncodedGroup(encodedGroupId)) {
|
if (!isEncodedGroup(encodedGroupId)) {
|
||||||
|
@ -71,10 +74,7 @@ public abstract class GroupId {
|
||||||
|
|
||||||
byte[] bytes = extractDecodedId(encodedGroupId);
|
byte[] bytes = extractDecodedId(encodedGroupId);
|
||||||
|
|
||||||
if (encodedGroupId.startsWith(ENCODED_MMS_GROUP_PREFIX)) return mms(bytes);
|
return encodedGroupId.startsWith(ENCODED_MMS_GROUP_PREFIX) ? mms(bytes) : push(bytes);
|
||||||
else if (encodedGroupId.length() == V2_ENCODED_LENGTH) return v2(bytes);
|
|
||||||
else return v1(bytes);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1324,8 +1324,14 @@ public final class PushProcessMessageJob extends BaseJob {
|
||||||
long threadId;
|
long threadId;
|
||||||
|
|
||||||
if (typingMessage.getGroupId().isPresent()) {
|
if (typingMessage.getGroupId().isPresent()) {
|
||||||
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupId.v1(typingMessage.getGroupId().get()));
|
GroupId.Push groupId = GroupId.push(typingMessage.getGroupId().get());
|
||||||
Recipient groupRecipient = Recipient.resolved(recipientId);
|
|
||||||
|
if (!DatabaseFactory.getGroupDatabase(context).isCurrentMember(groupId, author.getId())) {
|
||||||
|
Log.w(TAG, "Seen typing indicator for non-member");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Recipient groupRecipient = Recipient.externalGroup(context, groupId);
|
||||||
|
|
||||||
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class RecipientId implements Parcelable, Comparable<RecipientId> {
|
public class RecipientId implements Parcelable, Comparable<RecipientId> {
|
||||||
|
|
||||||
|
@ -62,6 +63,12 @@ public class RecipientId implements Parcelable, Comparable<RecipientId> {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean serializedListContains(@NonNull String serialized, @NonNull RecipientId recipientId) {
|
||||||
|
return Pattern.compile("\\b" + recipientId.serialize() + "\\b")
|
||||||
|
.matcher(serialized)
|
||||||
|
.find();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isUnknown() {
|
public boolean isUnknown() {
|
||||||
return id == UNKNOWN_ID;
|
return id == UNKNOWN_ID;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package org.thoughtcrime.securesms.util;
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class DelimiterUtil {
|
public class DelimiterUtil {
|
||||||
|
@ -16,7 +14,7 @@ public class DelimiterUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String[] split(String value, char delimiter) {
|
public static String[] split(String value, char delimiter) {
|
||||||
if (TextUtils.isEmpty(value)) {
|
if (value == null || value.length() == 0) {
|
||||||
return new String[0];
|
return new String[0];
|
||||||
} else {
|
} else {
|
||||||
String regex = "(?<!\\\\)" + Pattern.quote(delimiter + "");
|
String regex = "(?<!\\\\)" + Pattern.quote(delimiter + "");
|
||||||
|
|
|
@ -281,4 +281,18 @@ public final class GroupIdTest {
|
||||||
assertEquals("__textsecure_group__!090a0b0c0d0e0f000102030405060708", v1.toString());
|
assertEquals("__textsecure_group__!090a0b0c0d0e0f000102030405060708", v1.toString());
|
||||||
assertTrue(v1.isV1());
|
assertTrue(v1.isV1());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parse_bytes_to_v1_via_push() {
|
||||||
|
GroupId.V1 v1 = GroupId.push(new byte[]{ 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8 }).requireV1();
|
||||||
|
|
||||||
|
assertEquals("__textsecure_group__!090a0b0c0d0e0f000102030405060708", v1.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parse_bytes_to_v2_via_by_push() {
|
||||||
|
GroupId.V2 v2 = GroupId.push(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }).requireV2();
|
||||||
|
|
||||||
|
assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", v2.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package org.thoughtcrime.securesms.recipients;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public final class RecipientIdSerializationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toSerializedList_empty() {
|
||||||
|
assertEquals("", RecipientId.toSerializedList(emptyList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toSerializedList_one_item() {
|
||||||
|
assertEquals("123", RecipientId.toSerializedList(singletonList(RecipientId.from(123))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toSerializedList_two_items() {
|
||||||
|
assertEquals("123,987", RecipientId.toSerializedList(asList(RecipientId.from(123), RecipientId.from("987"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromSerializedList_empty() {
|
||||||
|
assertThat(RecipientId.fromSerializedList(""), is(emptyList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromSerializedList_one_item() {
|
||||||
|
assertThat(RecipientId.fromSerializedList("123"), is(singletonList(RecipientId.from(123))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromSerializedList_two_items() {
|
||||||
|
assertThat(RecipientId.fromSerializedList("123,456"), is(asList(RecipientId.from(123), RecipientId.from(456))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializedListContains_empty_list_does_not_contain_item() {
|
||||||
|
assertFalse(RecipientId.serializedListContains("", RecipientId.from(456)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializedListContains_single_list_does_not_contain_item() {
|
||||||
|
assertFalse(RecipientId.serializedListContains("123", RecipientId.from(456)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializedListContains_single_list_does_contain_item() {
|
||||||
|
assertTrue(RecipientId.serializedListContains("456", RecipientId.from(456)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializedListContains_double_list_does_contain_item_in_first_position() {
|
||||||
|
assertTrue(RecipientId.serializedListContains("456,123", RecipientId.from(456)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializedListContains_double_list_does_contain_item_in_second_position() {
|
||||||
|
assertTrue(RecipientId.serializedListContains("123,456", RecipientId.from(456)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializedListContains_single_list_does_not_contain_item_due_to_extra_digit_at_start() {
|
||||||
|
assertFalse(RecipientId.serializedListContains("1456", RecipientId.from(456)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializedListContains_single_list_does_not_contain_item_due_to_extra_digit_at_end() {
|
||||||
|
assertFalse(RecipientId.serializedListContains("4561", RecipientId.from(456)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializedListContains_find_all_items_in_triple_list() {
|
||||||
|
assertTrue(RecipientId.serializedListContains("11,12,13", RecipientId.from(11)));
|
||||||
|
assertTrue(RecipientId.serializedListContains("11,12,13", RecipientId.from(12)));
|
||||||
|
assertTrue(RecipientId.serializedListContains("11,12,13", RecipientId.from(13)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializedListContains_cant_find_similar_items_in_triple_list() {
|
||||||
|
assertFalse(RecipientId.serializedListContains("11,12,13", RecipientId.from(1)));
|
||||||
|
assertFalse(RecipientId.serializedListContains("11,12,13", RecipientId.from(2)));
|
||||||
|
assertFalse(RecipientId.serializedListContains("11,12,13", RecipientId.from(3)));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue