Guard against malformed group ids.

This commit is contained in:
Alan Evans 2020-04-20 12:01:31 -03:00 committed by Greyson Parrelli
parent 00ee6d0bbd
commit 9a8094cb8a
21 changed files with 200 additions and 94 deletions

View File

@ -211,7 +211,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private void initializeExistingGroup() {
final GroupId groupId = GroupId.parseNullable(getIntent().getStringExtra(GROUP_ID_EXTRA));
final GroupId groupId = GroupId.parseNullableOrThrow(getIntent().getStringExtra(GROUP_ID_EXTRA));
if (groupId != null) {
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupId);

View File

@ -25,7 +25,6 @@ import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
@ -186,7 +185,7 @@ public final class GroupDatabase extends Database {
null, null, null);
try {
if (cursor != null && cursor.moveToNext()) {
return GroupId.parse(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)))
return GroupId.parseOrThrow(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)))
.requireMms();
} else {
GroupId.Mms groupId = GroupId.createMms(new SecureRandom());
@ -519,7 +518,7 @@ public final class GroupDatabase extends Database {
return null;
}
return new GroupRecord(GroupId.parse(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID))),
return new GroupRecord(GroupId.parseOrThrow(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID))),
RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))),
cursor.getString(cursor.getColumnIndexOrThrow(TITLE)),
cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)),

View File

@ -561,7 +561,7 @@ public class RecipientDatabase extends Database {
for (SignalGroupV1Record insert : groupV1Inserts) {
db.insertOrThrow(TABLE_NAME, null, getValuesForStorageGroupV1(insert));
Recipient recipient = Recipient.externalGroup(context, GroupId.v1(insert.getGroupId()));
Recipient recipient = Recipient.externalGroup(context, GroupId.v1orThrow(insert.getGroupId()));
threadDatabase.setArchived(recipient.getId(), insert.isArchived());
recipient.live().refresh();
@ -575,7 +575,7 @@ public class RecipientDatabase extends Database {
throw new AssertionError("Had an update, but it didn't match any rows!");
}
Recipient recipient = Recipient.externalGroup(context, GroupId.v1(update.getOld().getGroupId()));
Recipient recipient = Recipient.externalGroup(context, GroupId.v1orThrow(update.getOld().getGroupId()));
threadDatabase.setArchived(recipient.getId(), update.getNew().isArchived());
recipient.live().refresh();
@ -670,7 +670,7 @@ public class RecipientDatabase extends Database {
private static @NonNull ContentValues getValuesForStorageGroupV1(@NonNull SignalGroupV1Record groupV1) {
ContentValues values = new ContentValues();
values.put(GROUP_ID, GroupId.v1(groupV1.getGroupId()).toString());
values.put(GROUP_ID, GroupId.v1orThrow(groupV1.getGroupId()).toString());
values.put(GROUP_TYPE, GroupType.SIGNAL_V1.getId());
values.put(PROFILE_SHARING, groupV1.isProfileSharingEnabled() ? "1" : "0");
values.put(BLOCKED, groupV1.isBlocked() ? "1" : "0");
@ -733,7 +733,7 @@ public class RecipientDatabase extends Database {
String username = cursor.getString(cursor.getColumnIndexOrThrow(USERNAME));
String e164 = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL));
GroupId groupId = GroupId.parseNullable(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)));
GroupId groupId = GroupId.parseNullableOrThrow(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)));
int groupType = cursor.getInt(cursor.getColumnIndexOrThrow(GROUP_TYPE));
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCKED)) == 1;
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(MESSAGE_RINGTONE));
@ -1406,7 +1406,7 @@ public class RecipientDatabase extends Database {
db.update(TABLE_NAME, setBlocked, UUID + " = ?", new String[] { uuid });
}
List<GroupId.V1> groupIdStrings = Stream.of(groupIds).map(GroupId::v1).toList();
List<GroupId.V1> groupIdStrings = Stream.of(groupIds).map(GroupId::v1orThrow).toList();
for (GroupId.V1 groupId : groupIdStrings) {
db.update(TABLE_NAME, setBlocked, GROUP_ID + " = ?", new String[] { groupId.toString() });

View File

@ -0,0 +1,15 @@
package org.thoughtcrime.securesms.groups;
public final class BadGroupIdException extends Exception {
BadGroupIdException(String message) {
super(message);
}
BadGroupIdException() {
super();
}
BadGroupIdException(Exception e) {
super(e);
}
}

View File

@ -30,30 +30,46 @@ public abstract class GroupId {
return new GroupId.Mms(mmsGroupIdBytes);
}
public static @NonNull GroupId.V1 v1(byte[] gv1GroupIdBytes) {
public static @NonNull GroupId.V1 v1orThrow(byte[] gv1GroupIdBytes) {
try {
return v1(gv1GroupIdBytes);
} catch (BadGroupIdException e) {
throw new AssertionError(e);
}
}
public static @NonNull GroupId.V1 v1(byte[] gv1GroupIdBytes) throws BadGroupIdException {
if (gv1GroupIdBytes.length == V2_BYTE_LENGTH) {
throw new AssertionError();
throw new BadGroupIdException();
}
return new GroupId.V1(gv1GroupIdBytes);
}
public static GroupId.V1 createV1(@NonNull SecureRandom secureRandom) {
return v1(Util.getSecretBytes(secureRandom, V1_MMS_BYTE_LENGTH));
return v1orThrow(Util.getSecretBytes(secureRandom, V1_MMS_BYTE_LENGTH));
}
public static GroupId.Mms createMms(@NonNull SecureRandom secureRandom) {
return mms(Util.getSecretBytes(secureRandom, MMS_BYTE_LENGTH));
}
public static GroupId.V2 v2(@NonNull byte[] bytes) {
public static GroupId.V2 v2orThrow(@NonNull byte[] bytes) {
try {
return v2(bytes);
} catch (BadGroupIdException e) {
throw new AssertionError(e);
}
}
public static GroupId.V2 v2(@NonNull byte[] bytes) throws BadGroupIdException {
if (bytes.length != V2_BYTE_LENGTH) {
throw new AssertionError();
throw new BadGroupIdException();
}
return new GroupId.V2(bytes);
}
public static GroupId.V2 v2(@NonNull GroupIdentifier groupIdentifier) {
return v2(groupIdentifier.serialize());
return v2orThrow(groupIdentifier.serialize());
}
public static GroupId.V2 v2(@NonNull GroupMasterKey masterKey) {
@ -62,25 +78,41 @@ public abstract class GroupId {
.getGroupIdentifier());
}
public static GroupId.Push push(byte[] bytes) {
public static GroupId.Push push(byte[] bytes) throws BadGroupIdException {
return bytes.length == V2_BYTE_LENGTH ? v2(bytes) : v1(bytes);
}
public static @NonNull GroupId parse(@NonNull String encodedGroupId) {
public static GroupId.Push pushOrThrow(byte[] bytes) {
try {
return push(bytes);
} catch (BadGroupIdException e) {
throw new AssertionError(e);
}
}
public static @NonNull GroupId parseOrThrow(@NonNull String encodedGroupId) {
try {
return parse(encodedGroupId);
} catch (BadGroupIdException e) {
throw new AssertionError(e);
}
}
public static @NonNull GroupId parse(@NonNull String encodedGroupId) throws BadGroupIdException {
try {
if (!isEncodedGroup(encodedGroupId)) {
throw new IOException("Invalid encoding");
throw new BadGroupIdException("Invalid encoding");
}
byte[] bytes = extractDecodedId(encodedGroupId);
return encodedGroupId.startsWith(ENCODED_MMS_GROUP_PREFIX) ? mms(bytes) : push(bytes);
} catch (IOException e) {
throw new AssertionError(e);
throw new BadGroupIdException(e);
}
}
public static @Nullable GroupId parseNullable(@Nullable String encodedGroupId) {
public static @Nullable GroupId parseNullable(@Nullable String encodedGroupId) throws BadGroupIdException {
if (encodedGroupId == null) {
return null;
}
@ -88,6 +120,14 @@ public abstract class GroupId {
return parse(encodedGroupId);
}
public static @Nullable GroupId parseNullableOrThrow(@Nullable String encodedGroupId) {
if (encodedGroupId == null) {
return null;
}
return parseOrThrow(encodedGroupId);
}
public static boolean isEncodedGroup(@NonNull String groupId) {
return groupId.startsWith(ENCODED_SIGNAL_GROUP_PREFIX) || groupId.startsWith(ENCODED_MMS_GROUP_PREFIX);
}

View File

@ -70,7 +70,7 @@ public final class GroupV1MessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
SignalServiceGroup group = groupV1.get();
GroupId id = GroupId.v1(group.getGroupId());
GroupId id = GroupId.v1orThrow(group.getGroupId());
Optional<GroupRecord> record = database.getGroup(id);
if (record.isPresent() && group.getType() == Type.UPDATE) {
@ -93,7 +93,7 @@ public final class GroupV1MessageProcessor {
boolean outgoing)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
GroupId.V1 id = GroupId.v1(group.getGroupId());
GroupId.V1 id = GroupId.v1orThrow(group.getGroupId());
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.UPDATE);
@ -127,7 +127,7 @@ public final class GroupV1MessageProcessor {
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
GroupId.V1 id = GroupId.v1(group.getGroupId());
GroupId.V1 id = GroupId.v1orThrow(group.getGroupId());
Set<RecipientId> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<RecipientId> messageMembers = new HashSet<>();
@ -203,7 +203,7 @@ public final class GroupV1MessageProcessor {
boolean outgoing)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
GroupId id = GroupId.v1(group.getGroupId());
GroupId id = GroupId.v1orThrow(group.getGroupId());
List<RecipientId> members = record.getMembers();
GroupContext.Builder builder = createGroupContext(group);
@ -228,13 +228,13 @@ public final class GroupV1MessageProcessor {
{
if (group.getAvatar().isPresent()) {
ApplicationDependencies.getJobManager()
.add(new AvatarGroupsV1DownloadJob(GroupId.v1(group.getGroupId())));
.add(new AvatarGroupsV1DownloadJob(GroupId.v1orThrow(group.getGroupId())));
}
try {
if (outgoing) {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupId.v1(group.getGroupId()));
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupId.v1orThrow(group.getGroupId()));
Recipient recipient = Recipient.resolved(recipientId);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList());
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
@ -246,7 +246,7 @@ public final class GroupV1MessageProcessor {
} else {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerTimestamp(), body, Optional.of(GroupId.v1(group.getGroupId())), 0, content.isNeedsReceipt());
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerTimestamp(), body, Optional.of(GroupId.v1orThrow(group.getGroupId())), 0, content.isNeedsReceipt());
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);

View File

@ -37,7 +37,7 @@ public class PendingMemberInvitesActivity extends PassphraseRequiredActionBarAct
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, PendingMemberInvitesFragment.newInstance(GroupId.parse(getIntent().getStringExtra(GROUP_ID)).requireV2()))
.replace(R.id.container, PendingMemberInvitesFragment.newInstance(GroupId.parseOrThrow(getIntent().getStringExtra(GROUP_ID)).requireV2()))
.commitNow();
}

View File

@ -80,7 +80,7 @@ public class PendingMemberInvitesFragment extends Fragment {
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
GroupId.V2 groupId = GroupId.parse(Objects.requireNonNull(requireArguments().getString(GROUP_ID))).requireV2();
GroupId.V2 groupId = GroupId.parseOrThrow(Objects.requireNonNull(requireArguments().getString(GROUP_ID))).requireV2();
PendingMemberInvitesViewModel.Factory factory = new PendingMemberInvitesViewModel.Factory(requireContext(), groupId);

View File

@ -113,7 +113,7 @@ public final class AvatarGroupsV1DownloadJob extends BaseJob {
public static final class Factory implements Job.Factory<AvatarGroupsV1DownloadJob> {
@Override
public @NonNull AvatarGroupsV1DownloadJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new AvatarGroupsV1DownloadJob(parameters, GroupId.parse(data.getString(KEY_GROUP_ID)).requireV1());
return new AvatarGroupsV1DownloadJob(parameters, GroupId.parseOrThrow(data.getString(KEY_GROUP_ID)).requireV1());
}
}
}

View File

@ -124,7 +124,7 @@ public final class AvatarGroupsV2DownloadJob extends BaseJob {
@Override
public @NonNull AvatarGroupsV2DownloadJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new AvatarGroupsV2DownloadJob(parameters,
GroupId.parse(data.getString(KEY_GROUP_ID)).requireV2(),
GroupId.parseOrThrow(data.getString(KEY_GROUP_ID)).requireV2(),
data.getString(CDN_KEY));
}
}

View File

@ -166,7 +166,7 @@ public class LeaveGroupJob extends BaseJob {
public static class Factory implements Job.Factory<LeaveGroupJob> {
@Override
public @NonNull LeaveGroupJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new LeaveGroupJob(GroupId.v1(Base64.decodeOrThrow(data.getString(KEY_GROUP_ID))),
return new LeaveGroupJob(GroupId.v1orThrow(Base64.decodeOrThrow(data.getString(KEY_GROUP_ID))),
data.getString(KEY_GROUP_NAME),
RecipientId.fromSerializedList(data.getString(KEY_MEMBERS)),
RecipientId.fromSerializedList(data.getString(KEY_RECIPIENTS)),

View File

@ -29,6 +29,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.BadGroupIdException;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
@ -226,14 +228,23 @@ public final class PushDecryptMessageJob extends BaseJob {
}
}
private static PushProcessMessageJob.ExceptionMetadata toExceptionMetadata(@NonNull UnsupportedDataMessageException e) throws NoSenderException {
private static PushProcessMessageJob.ExceptionMetadata toExceptionMetadata(@NonNull UnsupportedDataMessageException e)
throws NoSenderException
{
String sender = e.getSender();
if (sender == null) throw new NoSenderException();
GroupId groupId = null;
try {
groupId = GroupUtil.idFromGroupContext(e.getGroup().orNull());
} catch (BadGroupIdException ex) {
Log.w(TAG, "Bad group id found in unsupported data message", ex);
}
return new PushProcessMessageJob.ExceptionMetadata(sender,
e.getSenderDevice(),
e.getGroup().transform(GroupUtil::idFromGroupContext).orNull());
groupId);
}
private static PushProcessMessageJob.ExceptionMetadata toExceptionMetadata(@NonNull ProtocolException e) throws NoSenderException {

View File

@ -28,7 +28,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
@ -140,7 +139,7 @@ public class PushGroupUpdateJob extends BaseJob {
public @NonNull PushGroupUpdateJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) {
return new PushGroupUpdateJob(parameters,
RecipientId.from(data.getString(KEY_SOURCE)),
GroupId.parse(data.getString(KEY_GROUP_ID)));
GroupId.parseOrThrow(data.getString(KEY_GROUP_ID)));
}
}
}

View File

@ -43,6 +43,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.ReactionRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.BadGroupIdException;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupV1MessageProcessor;
import org.thoughtcrime.securesms.jobmanager.Data;
@ -342,6 +343,8 @@ public final class PushProcessMessageJob extends BaseJob {
} catch (StorageFailedException e) {
Log.w(TAG, e);
handleCorruptMessage(e.getSender(), e.getSenderDevice(), timestamp, smsMessageId);
} catch (BadGroupIdException e) {
Log.w(TAG, "Ignoring message with bad group id", e);
}
}
@ -518,6 +521,7 @@ public final class PushProcessMessageJob extends BaseJob {
}
private long handleSynchronizeSentEndSessionMessage(@NonNull SentTranscriptMessage message)
throws BadGroupIdException
{
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
Recipient recipient = getSyncMessageDestination(message);
@ -544,7 +548,7 @@ public final class PushProcessMessageJob extends BaseJob {
private void handleGroupV1Message(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
throws StorageFailedException
throws StorageFailedException, BadGroupIdException
{
GroupV1MessageProcessor.process(context, content, message, false);
@ -559,6 +563,7 @@ public final class PushProcessMessageJob extends BaseJob {
private void handleUnknownGroupMessage(@NonNull SignalServiceContent content,
@NonNull SignalServiceGroupContext group)
throws BadGroupIdException
{
if (group.getGroupV1().isPresent()) {
SignalServiceGroup groupV1 = group.getGroupV1().get();
@ -575,7 +580,7 @@ public final class PushProcessMessageJob extends BaseJob {
private void handleExpirationUpdate(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
throws StorageFailedException
throws StorageFailedException, BadGroupIdException
{
try {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
@ -717,7 +722,9 @@ public final class PushProcessMessageJob extends BaseJob {
}
}
private void handleSynchronizeMessageRequestResponse(@NonNull MessageRequestResponseMessage response) {
private void handleSynchronizeMessageRequestResponse(@NonNull MessageRequestResponseMessage response)
throws BadGroupIdException
{
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
@ -761,8 +768,7 @@ public final class PushProcessMessageJob extends BaseJob {
private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content,
@NonNull SentTranscriptMessage message)
throws StorageFailedException
throws StorageFailedException, BadGroupIdException
{
try {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
@ -880,7 +886,7 @@ public final class PushProcessMessageJob extends BaseJob {
private void handleMediaMessage(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
throws StorageFailedException
throws StorageFailedException, BadGroupIdException
{
notifyTypingStoppedFromIncomingMessage(getMessageDestination(content, message), content.getSender(), content.getSenderDevice());
@ -944,7 +950,9 @@ public final class PushProcessMessageJob extends BaseJob {
}
}
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) throws MmsException {
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message)
throws MmsException, BadGroupIdException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipient = getSyncMessageDestination(message);
@ -963,7 +971,7 @@ public final class PushProcessMessageJob extends BaseJob {
}
private long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message)
throws MmsException
throws MmsException, BadGroupIdException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipients = getSyncMessageDestination(message);
@ -1042,7 +1050,9 @@ public final class PushProcessMessageJob extends BaseJob {
return threadId;
}
private void handleGroupRecipientUpdate(@NonNull SentTranscriptMessage message) {
private void handleGroupRecipientUpdate(@NonNull SentTranscriptMessage message)
throws BadGroupIdException
{
Recipient recipient = getSyncMessageDestination(message);
if (!recipient.isGroup()) {
@ -1091,7 +1101,7 @@ public final class PushProcessMessageJob extends BaseJob {
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId,
@NonNull Optional<GroupId> groupId)
throws StorageFailedException
throws StorageFailedException, BadGroupIdException
{
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
String body = message.getBody().isPresent() ? message.getBody().get() : "";
@ -1132,7 +1142,7 @@ public final class PushProcessMessageJob extends BaseJob {
}
private long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message)
throws MmsException
throws MmsException, BadGroupIdException
{
Recipient recipient = getSyncMessageDestination(message);
String body = message.getMessage().getBody().or("");
@ -1361,6 +1371,7 @@ public final class PushProcessMessageJob extends BaseJob {
private void handleTypingMessage(@NonNull SignalServiceContent content,
@NonNull SignalServiceTypingMessage typingMessage)
throws BadGroupIdException
{
if (!TextSecurePreferences.isTypingIndicatorsEnabled(context)) {
return;
@ -1554,20 +1565,28 @@ public final class PushProcessMessageJob extends BaseJob {
return database.insertMessageInbox(textMessage);
}
private Recipient getSyncMessageDestination(@NonNull SentTranscriptMessage message) {
private Recipient getSyncMessageDestination(@NonNull SentTranscriptMessage message)
throws BadGroupIdException
{
return getGroupRecipient(message.getMessage().getGroupContext())
.or(() -> Recipient.externalPush(context, message.getDestination().get()));
}
private Recipient getMessageDestination(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message)
throws BadGroupIdException
{
return getGroupRecipient(message.getGroupContext())
.or(() -> Recipient.externalPush(context, content.getSender()));
}
private Optional<Recipient> getGroupRecipient(Optional<SignalServiceGroupContext> message) {
return message.transform(groupContext -> Recipient.externalGroup(context, GroupUtil.idFromGroupContext(groupContext)));
private Optional<Recipient> getGroupRecipient(Optional<SignalServiceGroupContext> message)
throws BadGroupIdException
{
if (message.isPresent()) {
return Optional.of(Recipient.externalGroup(context, GroupUtil.idFromGroupContext(message.get())));
}
return Optional.absent();
}
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull SignalServiceAddress sender, int device) {
@ -1580,7 +1599,9 @@ public final class PushProcessMessageJob extends BaseJob {
}
}
private boolean shouldIgnore(@Nullable SignalServiceContent content) {
private boolean shouldIgnore(@Nullable SignalServiceContent content)
throws BadGroupIdException
{
if (content == null) {
Log.w(TAG, "Got a message with null content.");
return true;
@ -1596,7 +1617,7 @@ public final class PushProcessMessageJob extends BaseJob {
return true;
} else if (conversation.isGroup()) {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
Optional<GroupId> groupId = message.getGroupContext().transform(GroupUtil::idFromGroupContext);
Optional<GroupId> groupId = GroupUtil.idFromGroupContext(message.getGroupContext());
if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) {
return false;
@ -1694,7 +1715,7 @@ public final class PushProcessMessageJob extends BaseJob {
} else {
ExceptionMetadata exceptionMetadata = new ExceptionMetadata(data.getString(KEY_EXCEPTION_SENDER),
data.getInt(KEY_EXCEPTION_DEVICE),
GroupId.parseNullable(data.getStringOrDefault(KEY_EXCEPTION_GROUP_ID, null)));
GroupId.parseNullableOrThrow(data.getStringOrDefault(KEY_EXCEPTION_GROUP_ID, null)));
return new PushProcessMessageJob(parameters,
state,

View File

@ -99,7 +99,7 @@ public class RequestGroupInfoJob extends BaseJob {
public @NonNull RequestGroupInfoJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new RequestGroupInfoJob(parameters,
RecipientId.from(data.getString(KEY_SOURCE)),
GroupId.parse(data.getString(KEY_GROUP_ID)));
GroupId.parseOrThrow(data.getString(KEY_GROUP_ID)));
}
}
}

View File

@ -92,7 +92,7 @@ public class IncomingMediaMessage {
this.quote = quote.orNull();
this.unidentified = unidentified;
if (group.isPresent()) this.groupId = GroupUtil.idFromGroupContext(group.get());
if (group.isPresent()) this.groupId = GroupUtil.idFromGroupContextOrThrow(group.get());
else this.groupId = null;
this.attachments.addAll(PointerAttachment.forPointers(attachments));

View File

@ -271,7 +271,7 @@ public class Recipient {
}
}
} else if (GroupId.isEncodedGroup(identifier)) {
id = db.getOrInsertFromGroupId(GroupId.parse(identifier));
id = db.getOrInsertFromGroupId(GroupId.parseOrThrow(identifier));
} else if (NumberUtil.isValidEmail(identifier)) {
id = db.getOrInsertFromEmail(identifier);
} else {

View File

@ -96,7 +96,7 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = in.readString();
this.sentTimestampMillis = in.readLong();
this.serverTimestampMillis = in.readLong();
this.groupId = GroupId.parseNullable(in.readString());
this.groupId = GroupId.parseNullableOrThrow(in.readString());
this.push = (in.readInt() == 1);
this.subscriptionId = in.readInt();
this.expiresInMillis = in.readLong();

View File

@ -18,12 +18,12 @@ class GroupV1ConflictMerger implements StorageSyncHelper.ConflictMerger<SignalGr
private final Map<GroupId, SignalGroupV1Record> localByGroupId;
GroupV1ConflictMerger(@NonNull Collection<SignalGroupV1Record> localOnly) {
localByGroupId = Stream.of(localOnly).collect(Collectors.toMap(g -> GroupId.v1(g.getGroupId()), g -> g));
localByGroupId = Stream.of(localOnly).collect(Collectors.toMap(g -> GroupId.v1orThrow(g.getGroupId()), g -> g));
}
@Override
public @NonNull Optional<SignalGroupV1Record> getMatching(@NonNull SignalGroupV1Record record) {
return Optional.fromNullable(localByGroupId.get(GroupId.v1(record.getGroupId())));
return Optional.fromNullable(localByGroupId.get(GroupId.v1orThrow(record.getGroupId())));
}
@Override

View File

@ -11,6 +11,7 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.groups.BadGroupIdException;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
@ -38,7 +39,9 @@ public final class GroupUtil {
/**
* Result may be a v1 or v2 GroupId.
*/
public static GroupId idFromGroupContext(@NonNull SignalServiceGroupContext groupContext) {
public static @NonNull GroupId idFromGroupContext(@NonNull SignalServiceGroupContext groupContext)
throws BadGroupIdException
{
if (groupContext.getGroupV1().isPresent()) {
return GroupId.v1(groupContext.getGroupV1().get().getGroupId());
} else if (groupContext.getGroupV2().isPresent()) {
@ -48,11 +51,24 @@ public final class GroupUtil {
}
}
public static @NonNull GroupId idFromGroupContextOrThrow(@NonNull SignalServiceGroupContext groupContext) {
try {
return idFromGroupContext(groupContext);
} catch (BadGroupIdException e) {
throw new AssertionError(e);
}
}
/**
* Result may be a v1 or v2 GroupId.
*/
public static @NonNull Optional<GroupId> idFromGroupContext(@NonNull Optional<SignalServiceGroupContext> groupContext) {
return groupContext.transform(GroupUtil::idFromGroupContext);
public static @NonNull Optional<GroupId> idFromGroupContext(@NonNull Optional<SignalServiceGroupContext> groupContext)
throws BadGroupIdException
{
if (groupContext.isPresent()) {
return Optional.of(idFromGroupContext(groupContext.get()));
}
return Optional.absent();
}
@WorkerThread

View File

@ -23,7 +23,7 @@ public final class GroupIdTest {
@Test
public void can_create_for_gv1() {
GroupId.V1 groupId = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
GroupId.V1 groupId = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f", groupId.toString());
assertFalse(groupId.isMms());
@ -31,7 +31,7 @@ public final class GroupIdTest {
@Test
public void can_parse_gv1() {
GroupId groupId = GroupId.parse("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
GroupId groupId = GroupId.parseOrThrow("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f", groupId.toString());
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
@ -67,7 +67,7 @@ public final class GroupIdTest {
@Test
public void can_parse_gv2() throws IOException {
GroupId groupId = GroupId.parse("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e");
GroupId groupId = GroupId.parseOrThrow("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e");
assertEquals("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e", groupId.toString());
assertArrayEquals(Hex.fromStringCondensed("9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"), groupId.getDecodedId());
@ -90,7 +90,7 @@ public final class GroupIdTest {
@Test
public void can_parse_mms() {
GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
GroupId groupId = GroupId.parseOrThrow("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
assertEquals("__signal_mms_group__!000102030405060708090a0b0c0d0e0f", groupId.toString());
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
@ -103,14 +103,14 @@ public final class GroupIdTest {
@SuppressWarnings("ConstantConditions")
@Test
public void can_parse_null() {
GroupId groupId = GroupId.parseNullable(null);
GroupId groupId = GroupId.parseNullableOrThrow(null);
assertNull(groupId);
}
@Test
public void can_parse_gv1_with_parseNullable() {
GroupId groupId = GroupId.parseNullable("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
GroupId groupId = GroupId.parseNullableOrThrow("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f", groupId.toString());
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
@ -122,41 +122,41 @@ public final class GroupIdTest {
@Test(expected = AssertionError.class)
public void bad_encoding__bad_prefix__parseNullable() {
GroupId.parseNullable("__BAD_PREFIX__!000102030405060708090a0b0c0d0e0f");
GroupId.parseNullableOrThrow("__BAD_PREFIX__!000102030405060708090a0b0c0d0e0f");
}
@Test(expected = AssertionError.class)
public void bad_encoding__empty__parseNullable() {
GroupId.parseNullable("");
GroupId.parseNullableOrThrow("");
}
@Test(expected = AssertionError.class)
public void bad_encoding__odd_hex__parseNullable() {
GroupId.parseNullable("__textsecure_group__!0001020305060708090bODD_HEX");
GroupId.parseNullableOrThrow("__textsecure_group__!0001020305060708090bODD_HEX");
}
@Test(expected = AssertionError.class)
public void bad_encoding__bad_prefix__parse() {
GroupId.parse("__BAD_PREFIX__!000102030405060708090a0b0c0d0e0f");
GroupId.parseOrThrow("__BAD_PREFIX__!000102030405060708090a0b0c0d0e0f");
}
@Test(expected = AssertionError.class)
public void bad_encoding__odd_hex__parse() {
GroupId.parse("__textsecure_group__!0001020305060708090b0c0d0e0fODD_HEX");
GroupId.parseOrThrow("__textsecure_group__!0001020305060708090b0c0d0e0fODD_HEX");
}
@Test
public void get_bytes() {
byte[] bytes = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
GroupId groupId = GroupId.v1(bytes);
GroupId groupId = GroupId.v1orThrow(bytes);
assertArrayEquals(bytes, groupId.getDecodedId());
}
@Test
public void equality() {
GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
GroupId groupId2 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
GroupId groupId1 = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
GroupId groupId2 = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
assertNotSame(groupId1, groupId2);
assertEquals(groupId1, groupId2);
@ -165,8 +165,8 @@ public final class GroupIdTest {
@Test
public void inequality_by_bytes() {
GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
GroupId groupId2 = GroupId.v1(new byte[]{ 0, 3, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
GroupId groupId1 = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
GroupId groupId2 = GroupId.v1orThrow(new byte[]{ 0, 3, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
assertNotSame(groupId1, groupId2);
assertNotEquals(groupId1, groupId2);
@ -175,7 +175,7 @@ public final class GroupIdTest {
@Test
public void inequality_of_sms_and_mms() {
GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
GroupId groupId1 = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
GroupId groupId2 = GroupId.mms(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
assertNotSame(groupId1, groupId2);
@ -185,14 +185,14 @@ public final class GroupIdTest {
@Test
public void inequality_with_null() {
GroupId groupId = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
GroupId groupId = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
assertNotEquals(groupId, null);
}
@Test
public void require_mms() {
GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
GroupId groupId = GroupId.parseOrThrow("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
GroupId.Mms mms = groupId.requireMms();
@ -201,7 +201,7 @@ public final class GroupIdTest {
@Test
public void require_v1_and_push() {
GroupId groupId = GroupId.parse("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
GroupId groupId = GroupId.parseOrThrow("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
GroupId.V1 v1 = groupId.requireV1();
GroupId.Push push = groupId.requirePush();
@ -212,7 +212,7 @@ public final class GroupIdTest {
@Test
public void require_v2_and_push() {
GroupId groupId = GroupId.parse("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e");
GroupId groupId = GroupId.parseOrThrow("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e");
GroupId.V2 v2 = groupId.requireV2();
GroupId.Push push = groupId.requirePush();
@ -222,35 +222,35 @@ public final class GroupIdTest {
}
@Test(expected = AssertionError.class)
public void cannot_require_push_of_mms() {
public void cannot_require_push_of_mms() throws BadGroupIdException {
GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
groupId.requirePush();
}
@Test(expected = AssertionError.class)
public void cannot_require_v1_of_mms() {
public void cannot_require_v1_of_mms() throws BadGroupIdException {
GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
groupId.requireV1();
}
@Test(expected = AssertionError.class)
public void cannot_require_v2_of_mms() {
public void cannot_require_v2_of_mms() throws BadGroupIdException {
GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
groupId.requireV2();
}
@Test(expected = AssertionError.class)
public void cannot_require_v1_of_v2() {
public void cannot_require_v1_of_v2() throws BadGroupIdException {
GroupId groupId = GroupId.parse("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e");
groupId.requireV1();
}
@Test(expected = AssertionError.class)
public void cannot_require_v2_of_v1() {
public void cannot_require_v2_of_v1() throws BadGroupIdException {
GroupId groupId = GroupId.parse("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
groupId.requireV2();
@ -258,12 +258,17 @@ public final class GroupIdTest {
@Test(expected = AssertionError.class)
public void cannot_create_v1_with_a_v2_length() throws IOException {
GroupId.v1(Hex.fromStringCondensed("9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"));
GroupId.v1orThrow(Hex.fromStringCondensed("9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"));
}
@Test(expected = BadGroupIdException.class)
public void cannot_create_v2_with_a_v1_length() throws IOException, BadGroupIdException {
GroupId.v2(Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f"));
}
@Test(expected = AssertionError.class)
public void cannot_create_v2_with_a_v1_length() throws IOException {
GroupId.v2(Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f"));
public void cannot_create_v2_with_a_v1_length_assert() throws IOException {
GroupId.v2orThrow(Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f"));
}
@Test
@ -283,14 +288,14 @@ public final class GroupIdTest {
}
@Test
public void parse_bytes_to_v1_via_push() {
public void parse_bytes_to_v1_via_push() throws BadGroupIdException {
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() {
public void parse_bytes_to_v2_via_by_push() throws BadGroupIdException {
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());