Remove processing of inbound GV1 messages.
This commit is contained in:
parent
5140353722
commit
df3399bde5
22 changed files with 92 additions and 1054 deletions
|
@ -157,7 +157,6 @@ final class GroupManagerV1 {
|
|||
Recipient recipient = Recipient.resolved(member);
|
||||
if (recipient.hasE164()) {
|
||||
e164Members.add(recipient.requireE164());
|
||||
uuidMembers.add(GroupV1MessageProcessor.createMember(recipient.requireE164()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,6 +215,6 @@ final class GroupManagerV1 {
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(GroupUtil.createGroupV1LeaveMessage(groupId, groupRecipient));
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,309 +0,0 @@
|
|||
package org.thoughtcrime.securesms.groups;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase.InsertResult;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.AvatarGroupsV1DownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage;
|
||||
import org.thoughtcrime.securesms.notifications.v2.ConversationId;
|
||||
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.thoughtcrime.securesms.util.Base64;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
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.GroupContext;
|
||||
|
||||
public final class GroupV1MessageProcessor {
|
||||
|
||||
private static final String TAG = Log.tag(GroupV1MessageProcessor.class);
|
||||
|
||||
public static @Nullable Long process(@NonNull Context context,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
boolean outgoing)
|
||||
throws BadGroupIdException
|
||||
{
|
||||
SignalServiceGroupContext signalServiceGroupContext = message.getGroupContext().get();
|
||||
Optional<SignalServiceGroup> groupV1 = signalServiceGroupContext.getGroupV1();
|
||||
|
||||
if (signalServiceGroupContext.getGroupV2().isPresent()) {
|
||||
throw new AssertionError("Cannot process GV2");
|
||||
}
|
||||
|
||||
if (!groupV1.isPresent() || groupV1.get().getGroupId() == null) {
|
||||
Log.w(TAG, "Received group message with no id! Ignoring...");
|
||||
return null;
|
||||
}
|
||||
|
||||
GroupDatabase database = SignalDatabase.groups();
|
||||
SignalServiceGroup group = groupV1.get();
|
||||
GroupId id = GroupId.v1(group.getGroupId());
|
||||
Optional<GroupRecord> record = database.getGroup(id);
|
||||
|
||||
if (record.isPresent() && group.getType() == Type.UPDATE) {
|
||||
return handleGroupUpdate(context, content, group, record.get(), outgoing);
|
||||
} else if (!record.isPresent() && group.getType() == Type.UPDATE) {
|
||||
return handleGroupCreate(context, content, group, outgoing);
|
||||
} else if (record.isPresent() && group.getType() == Type.QUIT) {
|
||||
return handleGroupLeave(context, content, group, record.get(), outgoing);
|
||||
} else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) {
|
||||
return handleGroupInfoRequest(context, content, record.get());
|
||||
} else {
|
||||
Log.w(TAG, "Received unknown type, ignoring...");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable Long handleGroupCreate(@NonNull Context context,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
boolean outgoing)
|
||||
{
|
||||
GroupDatabase database = SignalDatabase.groups();
|
||||
GroupId.V1 id = GroupId.v1orThrow(group.getGroupId());
|
||||
GroupContext.Builder builder = createGroupContext(group);
|
||||
builder.setType(GroupContext.Type.UPDATE);
|
||||
|
||||
SignalServiceAttachment avatar = group.getAvatar().orElse(null);
|
||||
List<RecipientId> members = new LinkedList<>();
|
||||
|
||||
if (group.getMembers().isPresent()) {
|
||||
for (SignalServiceAddress member : group.getMembers().get()) {
|
||||
members.add(Recipient.externalGV1Member(member).getId());
|
||||
}
|
||||
}
|
||||
|
||||
database.create(id, group.getName().orElse(null), members,
|
||||
avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null);
|
||||
|
||||
Recipient sender = Recipient.externalPush(content.getSender());
|
||||
|
||||
if (sender.isSystemContact() || sender.isProfileSharing()) {
|
||||
Log.i(TAG, "Auto-enabling profile sharing because 'adder' is trusted. contact: " + sender.isSystemContact() + ", profileSharing: " + sender.isProfileSharing());
|
||||
SignalDatabase.recipients().setProfileSharing(Recipient.externalGroupExact(id).getId(), true);
|
||||
}
|
||||
|
||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||
}
|
||||
|
||||
private static @Nullable Long handleGroupUpdate(@NonNull Context context,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
@NonNull GroupRecord groupRecord,
|
||||
boolean outgoing)
|
||||
{
|
||||
|
||||
GroupDatabase database = SignalDatabase.groups();
|
||||
GroupId.V1 id = GroupId.v1orThrow(group.getGroupId());
|
||||
|
||||
Set<RecipientId> recordMembers = new HashSet<>(groupRecord.getMembers());
|
||||
Set<RecipientId> messageMembers = new HashSet<>();
|
||||
|
||||
if (group.getMembers().isPresent()) {
|
||||
for (SignalServiceAddress messageMember : group.getMembers().get()) {
|
||||
messageMembers.add(Recipient.externalGV1Member(messageMember).getId());
|
||||
}
|
||||
}
|
||||
|
||||
Set<RecipientId> addedMembers = new HashSet<>(messageMembers);
|
||||
addedMembers.removeAll(recordMembers);
|
||||
|
||||
Set<RecipientId> missingMembers = new HashSet<>(recordMembers);
|
||||
missingMembers.removeAll(messageMembers);
|
||||
|
||||
GroupContext.Builder builder = createGroupContext(group);
|
||||
builder.setType(GroupContext.Type.UPDATE);
|
||||
|
||||
if (addedMembers.size() > 0) {
|
||||
Set<RecipientId> unionMembers = new HashSet<>(recordMembers);
|
||||
unionMembers.addAll(messageMembers);
|
||||
database.updateMembers(id, new LinkedList<>(unionMembers));
|
||||
|
||||
builder.clearMembers();
|
||||
builder.clearMembersE164();
|
||||
|
||||
for (RecipientId addedMember : addedMembers) {
|
||||
Recipient recipient = Recipient.resolved(addedMember);
|
||||
|
||||
if (recipient.getE164().isPresent()) {
|
||||
builder.addMembersE164(recipient.requireE164());
|
||||
builder.addMembers(createMember(recipient.requireE164()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.clearMembers();
|
||||
builder.clearMembersE164();
|
||||
}
|
||||
|
||||
if (missingMembers.size() > 0) {
|
||||
// TODO We should tell added and missing about each-other.
|
||||
}
|
||||
|
||||
if (group.getName().isPresent() || group.getAvatar().isPresent()) {
|
||||
SignalServiceAttachment avatar = group.getAvatar().orElse(null);
|
||||
database.update(id, group.getName().orElse(null), avatar != null ? avatar.asPointer() : null);
|
||||
}
|
||||
|
||||
if (group.getName().isPresent() && group.getName().get().equals(groupRecord.getTitle())) {
|
||||
builder.clearName();
|
||||
}
|
||||
|
||||
if (!groupRecord.isActive()) database.setActive(id, true);
|
||||
|
||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||
}
|
||||
|
||||
private static Long handleGroupInfoRequest(@NonNull Context context,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull GroupRecord record)
|
||||
{
|
||||
Recipient sender = Recipient.externalPush(content.getSender());
|
||||
|
||||
if (record.getMembers().contains(sender.getId())) {
|
||||
ApplicationDependencies.getJobManager().add(new PushGroupUpdateJob(sender.getId(), record.getId()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Long handleGroupLeave(@NonNull Context context,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
@NonNull GroupRecord record,
|
||||
boolean outgoing)
|
||||
{
|
||||
GroupDatabase database = SignalDatabase.groups();
|
||||
GroupId id = GroupId.v1orThrow(group.getGroupId());
|
||||
List<RecipientId> members = record.getMembers();
|
||||
|
||||
GroupContext.Builder builder = createGroupContext(group);
|
||||
builder.setType(GroupContext.Type.QUIT);
|
||||
|
||||
RecipientId senderId = RecipientId.from(content.getSender());
|
||||
|
||||
if (members.contains(senderId)) {
|
||||
database.remove(id, senderId);
|
||||
if (outgoing) database.setActive(id, false);
|
||||
|
||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static @Nullable Long storeMessage(@NonNull Context context,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
@NonNull GroupContext storage,
|
||||
boolean outgoing)
|
||||
{
|
||||
if (group.getAvatar().isPresent()) {
|
||||
ApplicationDependencies.getJobManager()
|
||||
.add(new AvatarGroupsV1DownloadJob(GroupId.v1orThrow(group.getGroupId())));
|
||||
}
|
||||
|
||||
try {
|
||||
if (outgoing) {
|
||||
MessageDatabase mmsDatabase = SignalDatabase.mms();
|
||||
RecipientId recipientId = SignalDatabase.recipients().getOrInsertFromGroupId(GroupId.v1orThrow(group.getGroupId()));
|
||||
Recipient recipient = Recipient.resolved(recipientId);
|
||||
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
|
||||
long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(recipient);
|
||||
long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);
|
||||
|
||||
mmsDatabase.markAsSent(messageId, true);
|
||||
|
||||
return threadId;
|
||||
} else {
|
||||
MessageDatabase smsDatabase = SignalDatabase.sms();
|
||||
String body = Base64.encodeBytes(storage.toByteArray());
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerReceivedTimestamp(), System.currentTimeMillis(), body, Optional.of(GroupId.v1orThrow(group.getGroupId())), 0, content.isNeedsReceipt(), content.getServerUuid());
|
||||
IncomingGroupUpdateMessage groupMessage = new IncomingGroupUpdateMessage(incoming, storage, body);
|
||||
|
||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(insertResult.get().getThreadId()));
|
||||
return insertResult.get().getThreadId();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (MmsException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static GroupContext.Builder createGroupContext(SignalServiceGroup group) {
|
||||
GroupContext.Builder builder = GroupContext.newBuilder();
|
||||
builder.setId(ByteString.copyFrom(group.getGroupId()));
|
||||
|
||||
if (group.getAvatar().isPresent() &&
|
||||
group.getAvatar().get().isPointer() &&
|
||||
group.getAvatar().get().asPointer().getRemoteId().getV2().isPresent())
|
||||
{
|
||||
builder.setAvatar(AttachmentPointer.newBuilder()
|
||||
.setCdnId(group.getAvatar().get().asPointer().getRemoteId().getV2().get())
|
||||
.setKey(ByteString.copyFrom(group.getAvatar().get().asPointer().getKey()))
|
||||
.setContentType(group.getAvatar().get().getContentType()));
|
||||
}
|
||||
|
||||
if (group.getName().isPresent()) {
|
||||
builder.setName(group.getName().get());
|
||||
}
|
||||
|
||||
if (group.getMembers().isPresent()) {
|
||||
builder.addAllMembersE164(Stream.of(group.getMembers().get())
|
||||
.filter(a -> a.getNumber().isPresent())
|
||||
.map(a -> a.getNumber().get())
|
||||
.toList());
|
||||
builder.addAllMembers(Stream.of(group.getMembers().get())
|
||||
.filter(address -> address.getNumber().isPresent())
|
||||
.map(address -> address.getNumber().get())
|
||||
.map(GroupV1MessageProcessor::createMember)
|
||||
.toList());
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static GroupContext.Member createMember(@NonNull String e164) {
|
||||
GroupContext.Member.Builder member = GroupContext.Member.newBuilder();
|
||||
member.setE164(e164);
|
||||
return member.build();
|
||||
}
|
||||
}
|
|
@ -124,7 +124,7 @@ public final class GroupsV1MigrationUtil {
|
|||
break;
|
||||
case NOT_A_MEMBER:
|
||||
Log.w(TAG, "The migrated group already exists, but we are not a member. Doing a local leave.");
|
||||
handleLeftBehind(context, gv1Id, groupRecipient, threadId);
|
||||
handleLeftBehind(gv1Id);
|
||||
return;
|
||||
case FULL_OR_PENDING_MEMBER:
|
||||
Log.w(TAG, "The migrated group already exists, and we're in it. Continuing on.");
|
||||
|
@ -177,7 +177,7 @@ public final class GroupsV1MigrationUtil {
|
|||
throw new IOException("[Local] The group should exist already!");
|
||||
} catch (GroupNotAMemberException e) {
|
||||
Log.w(TAG, "[Local] We are not in the group. Doing a local leave.");
|
||||
handleLeftBehind(context, gv1Id, groupRecipient, threadId);
|
||||
handleLeftBehind(gv1Id);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -195,15 +195,7 @@ public final class GroupsV1MigrationUtil {
|
|||
}
|
||||
}
|
||||
|
||||
private static void handleLeftBehind(@NonNull Context context, @NonNull GroupId.V1 gv1Id, @NonNull Recipient groupRecipient, long threadId) {
|
||||
OutgoingMediaMessage leaveMessage = GroupUtil.createGroupV1LeaveMessage(gv1Id, groupRecipient);
|
||||
try {
|
||||
long id = SignalDatabase.mms().insertMessageOutbox(leaveMessage, threadId, false, null);
|
||||
SignalDatabase.mms().markAsSent(id, true);
|
||||
} catch (MmsException e) {
|
||||
Log.w(TAG, "Failed to insert group leave message!", e);
|
||||
}
|
||||
|
||||
private static void handleLeftBehind(@NonNull GroupId.V1 gv1Id) {
|
||||
SignalDatabase.groups().setActive(gv1Id, false);
|
||||
SignalDatabase.groups().remove(gv1Id, Recipient.self().getId());
|
||||
}
|
||||
|
|
|
@ -56,14 +56,11 @@ public class PushProcessMessageQueueJobMigration extends JobMigration {
|
|||
|
||||
if (content != null && content.getDataMessage().isPresent() && content.getDataMessage().get().getGroupContext().isPresent()) {
|
||||
Log.i(TAG, "Migrating a group message.");
|
||||
try {
|
||||
GroupId groupId = GroupUtil.idFromGroupContext(content.getDataMessage().get().getGroupContext().get());
|
||||
Recipient recipient = Recipient.externalGroupExact(groupId);
|
||||
|
||||
suffix = recipient.getId().toQueueKey();
|
||||
} catch (BadGroupIdException e) {
|
||||
Log.w(TAG, "Bad groupId! Using default queue.");
|
||||
}
|
||||
GroupId groupId = GroupId.v2(content.getDataMessage().get().getGroupContext().get().getMasterKey());
|
||||
Recipient recipient = Recipient.externalGroupExact(groupId);
|
||||
|
||||
suffix = recipient.getId().toQueueKey();
|
||||
} else if (content != null) {
|
||||
Log.i(TAG, "Migrating an individual message.");
|
||||
suffix = RecipientId.from(content.getSender()).toQueueKey();
|
||||
|
|
|
@ -142,7 +142,6 @@ public final class JobManagerFactories {
|
|||
put(PushDistributionListSendJob.KEY, new PushDistributionListSendJob.Factory());
|
||||
put(PushGroupSendJob.KEY, new PushGroupSendJob.Factory());
|
||||
put(PushGroupSilentUpdateSendJob.KEY, new PushGroupSilentUpdateSendJob.Factory());
|
||||
put(PushGroupUpdateJob.KEY, new PushGroupUpdateJob.Factory());
|
||||
put(PushMediaSendJob.KEY, new PushMediaSendJob.Factory());
|
||||
put(PushNotificationReceiveJob.KEY, new PushNotificationReceiveJob.Factory());
|
||||
put(PushProcessEarlyMessagesJob.KEY, new PushProcessEarlyMessagesJob.Factory());
|
||||
|
@ -156,7 +155,6 @@ public final class JobManagerFactories {
|
|||
put(RemoteConfigRefreshJob.KEY, new RemoteConfigRefreshJob.Factory());
|
||||
put(RemoteDeleteSendJob.KEY, new RemoteDeleteSendJob.Factory());
|
||||
put(ReportSpamJob.KEY, new ReportSpamJob.Factory());
|
||||
put(RequestGroupInfoJob.KEY, new RequestGroupInfoJob.Factory());
|
||||
put(ResendMessageJob.KEY, new ResendMessageJob.Factory());
|
||||
put(ResumableUploadSpecJob.KEY, new ResumableUploadSpecJob.Factory());
|
||||
put(RequestGroupV2InfoWorkerJob.KEY, new RequestGroupV2InfoWorkerJob.Factory());
|
||||
|
@ -240,6 +238,8 @@ public final class JobManagerFactories {
|
|||
put("StorageSyncJob", new StorageSyncJob.Factory());
|
||||
put("WakeGroupV2Job", new FailingJob.Factory());
|
||||
put("LeaveGroupJob", new FailingJob.Factory());
|
||||
put("PushGroupUpdateJob", new FailingJob.Factory());
|
||||
put("RequestGroupInfoJob", new FailingJob.Factory());
|
||||
}};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,163 +0,0 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.net.NotPushRegisteredException;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender.IndividualSendEvents;
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
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.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PushGroupUpdateJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "PushGroupUpdateJob";
|
||||
|
||||
private static final String TAG = Log.tag(PushGroupUpdateJob.class);
|
||||
|
||||
private static final String KEY_SOURCE = "source";
|
||||
private static final String KEY_GROUP_ID = "group_id";
|
||||
|
||||
private final RecipientId source;
|
||||
private final GroupId groupId;
|
||||
|
||||
public PushGroupUpdateJob(@NonNull RecipientId source, @NonNull GroupId groupId) {
|
||||
this(new Job.Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.build(),
|
||||
source,
|
||||
groupId);
|
||||
}
|
||||
|
||||
private PushGroupUpdateJob(@NonNull Job.Parameters parameters, RecipientId source, @NonNull GroupId groupId) {
|
||||
super(parameters);
|
||||
|
||||
this.source = source;
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return new Data.Builder().putString(KEY_SOURCE, source.serialize())
|
||||
.putString(KEY_GROUP_ID, groupId.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws IOException, UntrustedIdentityException {
|
||||
if (!Recipient.self().isRegistered()) {
|
||||
throw new NotPushRegisteredException();
|
||||
}
|
||||
|
||||
Recipient sourceRecipient = Recipient.resolved(source);
|
||||
|
||||
if (sourceRecipient.isUnregistered()) {
|
||||
Log.w(TAG, sourceRecipient.getId() + " not registered!");
|
||||
return;
|
||||
}
|
||||
|
||||
GroupDatabase groupDatabase = SignalDatabase.groups();
|
||||
Optional<GroupRecord> record = groupDatabase.getGroup(groupId);
|
||||
SignalServiceAttachment avatar = null;
|
||||
|
||||
if (record == null || !record.isPresent()) {
|
||||
Log.w(TAG, "No information for group record info request: " + groupId.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (AvatarHelper.hasAvatar(context, record.get().getRecipientId())) {
|
||||
avatar = SignalServiceAttachmentStream.newStreamBuilder()
|
||||
.withContentType("image/jpeg")
|
||||
.withStream(AvatarHelper.getAvatar(context, record.get().getRecipientId()))
|
||||
.withLength(AvatarHelper.getAvatarLength(context, record.get().getRecipientId()))
|
||||
.build();
|
||||
}
|
||||
|
||||
List<SignalServiceAddress> members = new LinkedList<>();
|
||||
|
||||
for (RecipientId member : record.get().getMembers()) {
|
||||
Recipient recipient = Recipient.resolved(member);
|
||||
|
||||
if (recipient.isMaybeRegistered()) {
|
||||
members.add(RecipientUtil.toSignalServiceAddress(context, recipient));
|
||||
}
|
||||
}
|
||||
|
||||
SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE)
|
||||
.withAvatar(avatar)
|
||||
.withId(groupId.getDecodedId())
|
||||
.withMembers(members)
|
||||
.withName(record.get().getTitle())
|
||||
.build();
|
||||
|
||||
RecipientId groupRecipientId = SignalDatabase.recipients().getOrInsertFromGroupId(groupId);
|
||||
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
||||
|
||||
SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
|
||||
.asGroupMessage(groupContext)
|
||||
.withTimestamp(System.currentTimeMillis())
|
||||
.withExpiration(groupRecipient.getExpiresInSeconds())
|
||||
.build();
|
||||
|
||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||
|
||||
messageSender.sendDataMessage(RecipientUtil.toSignalServiceAddress(context, sourceRecipient),
|
||||
UnidentifiedAccessUtil.getAccessFor(context, sourceRecipient),
|
||||
ContentHint.DEFAULT,
|
||||
message,
|
||||
IndividualSendEvents.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(@NonNull Exception e) {
|
||||
Log.w(TAG, e);
|
||||
return e instanceof PushNetworkException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<PushGroupUpdateJob> {
|
||||
@Override
|
||||
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.parseOrThrow(data.getString(KEY_GROUP_ID)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ import org.thoughtcrime.securesms.util.Base64;
|
|||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.whispersystems.signalservice.api.groupsv2.NoCredentialForRedemptionTimeException;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -120,27 +120,23 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
.setMaxAttempts(Parameters.UNLIMITED);
|
||||
|
||||
if (content != null) {
|
||||
SignalServiceGroupContext signalServiceGroupContext = GroupUtil.getGroupContextIfPresent(content);
|
||||
SignalServiceGroupV2 signalServiceGroupContext = GroupUtil.getGroupContextIfPresent(content);
|
||||
|
||||
if (signalServiceGroupContext != null) {
|
||||
try {
|
||||
GroupId groupId = GroupUtil.idFromGroupContext(signalServiceGroupContext);
|
||||
GroupId groupId = GroupId.v2(signalServiceGroupContext.getMasterKey());
|
||||
|
||||
queueName = getQueueName(Recipient.externalPossiblyMigratedGroup(groupId).getId());
|
||||
queueName = getQueueName(Recipient.externalPossiblyMigratedGroup(groupId).getId());
|
||||
|
||||
if (groupId.isV2()) {
|
||||
int localRevision = SignalDatabase.groups().getGroupV2Revision(groupId.requireV2());
|
||||
if (groupId.isV2()) {
|
||||
int localRevision = SignalDatabase.groups().getGroupV2Revision(groupId.requireV2());
|
||||
|
||||
if (signalServiceGroupContext.getGroupV2().get().getRevision() > localRevision ||
|
||||
SignalDatabase.groups().getGroupV1ByExpectedV2(groupId.requireV2()).isPresent())
|
||||
{
|
||||
Log.i(TAG, "Adding network constraint to group-related job.");
|
||||
builder.addConstraint(NetworkConstraint.KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(30));
|
||||
}
|
||||
if (signalServiceGroupContext.getRevision() > localRevision ||
|
||||
SignalDatabase.groups().getGroupV1ByExpectedV2(groupId.requireV2()).isPresent())
|
||||
{
|
||||
Log.i(TAG, "Adding network constraint to group-related job.");
|
||||
builder.addConstraint(NetworkConstraint.KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(30));
|
||||
}
|
||||
} catch (BadGroupIdException e) {
|
||||
Log.w(TAG, "Bad groupId! Using default queue. ID: " + content.getTimestamp());
|
||||
}
|
||||
} else if (content.getSyncMessage().isPresent() && content.getSyncMessage().get().getSent().isPresent() && content.getSyncMessage().get().getSent().get().getDestination().isPresent()) {
|
||||
queueName = getQueueName(RecipientId.from(content.getSyncMessage().get().getSent().get().getDestination().get()));
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.net.NotPushRegisteredException;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender.IndividualSendEvents;
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RequestGroupInfoJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "RequestGroupInfoJob";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = Log.tag(RequestGroupInfoJob.class);
|
||||
|
||||
private static final String KEY_SOURCE = "source";
|
||||
private static final String KEY_GROUP_ID = "group_id";
|
||||
|
||||
private final RecipientId source;
|
||||
private final GroupId groupId;
|
||||
|
||||
public RequestGroupInfoJob(@NonNull RecipientId source, @NonNull GroupId groupId) {
|
||||
this(new Job.Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.build(),
|
||||
source,
|
||||
groupId);
|
||||
|
||||
}
|
||||
|
||||
private RequestGroupInfoJob(@NonNull Job.Parameters parameters, @NonNull RecipientId source, @NonNull GroupId groupId) {
|
||||
super(parameters);
|
||||
|
||||
this.source = source;
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return new Data.Builder().putString(KEY_SOURCE, source.serialize())
|
||||
.putString(KEY_GROUP_ID, groupId.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws IOException, UntrustedIdentityException {
|
||||
if (!Recipient.self().isRegistered()) {
|
||||
throw new NotPushRegisteredException();
|
||||
}
|
||||
|
||||
SignalServiceGroup group = SignalServiceGroup.newBuilder(Type.REQUEST_INFO)
|
||||
.withId(groupId.getDecodedId())
|
||||
.build();
|
||||
|
||||
SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
|
||||
.asGroupMessage(group)
|
||||
.withTimestamp(System.currentTimeMillis())
|
||||
.build();
|
||||
|
||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||
Recipient recipient = Recipient.resolved(source);
|
||||
|
||||
if (recipient.isUnregistered()) {
|
||||
Log.w(TAG, recipient.getId() + " is unregistered!");
|
||||
return;
|
||||
}
|
||||
|
||||
messageSender.sendDataMessage(RecipientUtil.toSignalServiceAddress(context, recipient),
|
||||
UnidentifiedAccessUtil.getAccessFor(context, recipient),
|
||||
ContentHint.IMPLICIT,
|
||||
message,
|
||||
IndividualSendEvents.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(@NonNull Exception e) {
|
||||
if (e instanceof ServerRejectedException) return false;
|
||||
return e instanceof PushNetworkException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<RequestGroupInfoJob> {
|
||||
|
||||
@Override
|
||||
public @NonNull RequestGroupInfoJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new RequestGroupInfoJob(parameters,
|
||||
RecipientId.from(data.getString(KEY_SOURCE)),
|
||||
GroupId.parseOrThrow(data.getString(KEY_GROUP_ID)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ import org.thoughtcrime.securesms.util.Stopwatch;
|
|||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
@ -177,24 +177,19 @@ public class IncomingMessageProcessor {
|
|||
}
|
||||
|
||||
private boolean needsToEnqueueProcessing(@NonNull DecryptionResult result) {
|
||||
SignalServiceGroupContext groupContext = GroupUtil.getGroupContextIfPresent(result.getContent());
|
||||
SignalServiceGroupV2 groupContext = GroupUtil.getGroupContextIfPresent(result.getContent());
|
||||
|
||||
if (groupContext != null) {
|
||||
try {
|
||||
GroupId groupId = GroupUtil.idFromGroupContext(groupContext);
|
||||
GroupId groupId = GroupId.v2(groupContext.getMasterKey());
|
||||
|
||||
if (groupId.isV2()) {
|
||||
String queueName = PushProcessMessageJob.getQueueName(Recipient.externalPossiblyMigratedGroup(groupId).getId());
|
||||
GroupDatabase groupDatabase = SignalDatabase.groups();
|
||||
if (groupId.isV2()) {
|
||||
String queueName = PushProcessMessageJob.getQueueName(Recipient.externalPossiblyMigratedGroup(groupId).getId());
|
||||
GroupDatabase groupDatabase = SignalDatabase.groups();
|
||||
|
||||
return !jobManager.isQueueEmpty(queueName) ||
|
||||
groupContext.getGroupV2().get().getRevision() > groupDatabase.getGroupV2Revision(groupId.requireV2()) ||
|
||||
groupDatabase.getGroupV1ByExpectedV2(groupId.requireV2()).isPresent();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (BadGroupIdException e) {
|
||||
Log.w(TAG, "Bad group ID!");
|
||||
return !jobManager.isQueueEmpty(queueName) ||
|
||||
groupContext.getRevision() > groupDatabase.getGroupV2Revision(groupId.requireV2()) ||
|
||||
groupDatabase.getGroupV1ByExpectedV2(groupId.requireV2()).isPresent();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (result.getContent() != null) {
|
||||
|
|
|
@ -69,7 +69,6 @@ import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
|
|||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
||||
import org.thoughtcrime.securesms.groups.GroupV1MessageProcessor;
|
||||
import org.thoughtcrime.securesms.groups.GroupsV1MigrationUtil;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||
|
@ -92,7 +91,6 @@ import org.thoughtcrime.securesms.jobs.PushProcessEarlyMessagesJob;
|
|||
import org.thoughtcrime.securesms.jobs.PushProcessMessageJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob;
|
||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||
import org.thoughtcrime.securesms.jobs.ResendMessageJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||
|
@ -142,8 +140,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
|||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServicePreview;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||
|
@ -283,7 +279,7 @@ public final class MessageContentProcessor {
|
|||
boolean isGv2Message = groupId.isPresent() && groupId.get().isV2();
|
||||
|
||||
if (isGv2Message) {
|
||||
if (handleGv2PreProcessing(groupId.orElse(null).requireV2(), content, content.getDataMessage().get().getGroupContext().get().getGroupV2().get(), senderRecipient)) {
|
||||
if (handleGv2PreProcessing(groupId.orElse(null).requireV2(), content, content.getDataMessage().get().getGroupContext().get(), senderRecipient)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -292,7 +288,6 @@ public final class MessageContentProcessor {
|
|||
|
||||
if (isInvalidMessage(message)) handleInvalidMessage(content.getSender(), content.getSenderDevice(), groupId, content.getTimestamp(), smsMessageId);
|
||||
else if (message.isEndSession()) messageId = handleEndSessionMessage(content, smsMessageId, senderRecipient);
|
||||
else if (message.isGroupV1Update()) handleGroupV1Message(content, message, smsMessageId, groupId.get().requireV1(), senderRecipient, threadRecipient, receivedTime);
|
||||
else if (message.isExpirationUpdate()) messageId = handleExpirationUpdate(content, message, smsMessageId, groupId, senderRecipient, threadRecipient, receivedTime, false);
|
||||
else if (message.getReaction().isPresent() && message.getStoryContext().isPresent()) messageId = handleStoryReaction(content, message, senderRecipient);
|
||||
else if (message.getReaction().isPresent()) messageId = handleReaction(content, message, senderRecipient);
|
||||
|
@ -516,20 +511,6 @@ public final class MessageContentProcessor {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static @Nullable SignalServiceGroupContext getGroupContextIfPresent(@NonNull SignalServiceContent content) {
|
||||
if (content.getDataMessage().isPresent() && content.getDataMessage().get().getGroupContext().isPresent()) {
|
||||
return content.getDataMessage().get().getGroupContext().get();
|
||||
} else if (content.getSyncMessage().isPresent() &&
|
||||
content.getSyncMessage().get().getSent().isPresent() &&
|
||||
content.getSyncMessage().get().getSent().get().getDataMessage().get().getGroupContext().isPresent())
|
||||
{
|
||||
return content.getSyncMessage().get().getSent().get().getDataMessage().get().getGroupContext().get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to update the group to the revision mentioned in the message.
|
||||
* If the local version is at least the revision in the message it will not query the server.
|
||||
|
@ -790,51 +771,20 @@ public final class MessageContentProcessor {
|
|||
return threadId;
|
||||
}
|
||||
|
||||
private void handleGroupV1Message(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
@NonNull Optional<Long> smsMessageId,
|
||||
@NonNull GroupId.V1 groupId,
|
||||
@NonNull Recipient senderRecipient,
|
||||
@NonNull Recipient threadRecipient,
|
||||
long receivedTime)
|
||||
throws StorageFailedException, BadGroupIdException
|
||||
{
|
||||
log(content.getTimestamp(), "GroupV1 message.");
|
||||
|
||||
GroupV1MessageProcessor.process(context, content, message, false);
|
||||
|
||||
handlePossibleExpirationUpdate(content, message, Optional.of(groupId), senderRecipient, threadRecipient, receivedTime);
|
||||
|
||||
if (smsMessageId.isPresent()) {
|
||||
SignalDatabase.sms().deleteMessage(smsMessageId.get());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUnknownGroupMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroupContext group,
|
||||
@NonNull SignalServiceGroupV2 group,
|
||||
@NonNull Recipient senderRecipient)
|
||||
throws BadGroupIdException
|
||||
{
|
||||
log(content.getTimestamp(), "Unknown group message.");
|
||||
|
||||
if (group.getGroupV1().isPresent()) {
|
||||
SignalServiceGroup groupV1 = group.getGroupV1().get();
|
||||
if (groupV1.getType() != SignalServiceGroup.Type.REQUEST_INFO) {
|
||||
ApplicationDependencies.getJobManager().add(new RequestGroupInfoJob(senderRecipient.getId(), GroupId.v1(groupV1.getGroupId())));
|
||||
} else {
|
||||
warn(content.getTimestamp(), "Received a REQUEST_INFO message for a group we don't know about. Ignoring.");
|
||||
}
|
||||
} else if (group.getGroupV2().isPresent()) {
|
||||
warn(content.getTimestamp(), "Received a GV2 message for a group we have no knowledge of -- attempting to fix this state.");
|
||||
ServiceId authServiceId = ServiceId.parseOrNull(content.getDestinationUuid());
|
||||
if (authServiceId == null) {
|
||||
warn(content.getTimestamp(), "Group message missing destination uuid, defaulting to ACI");
|
||||
authServiceId = SignalStore.account().requireAci();
|
||||
}
|
||||
SignalDatabase.groups().fixMissingMasterKey(authServiceId, group.getGroupV2().get().getMasterKey());
|
||||
} else {
|
||||
warn(content.getTimestamp(), "Received a message for a group we don't know about without a group context. Ignoring.");
|
||||
warn(content.getTimestamp(), "Received a GV2 message for a group we have no knowledge of -- attempting to fix this state.");
|
||||
ServiceId authServiceId = ServiceId.parseOrNull(content.getDestinationUuid());
|
||||
if (authServiceId == null) {
|
||||
warn(content.getTimestamp(), "Group message missing destination uuid, defaulting to ACI");
|
||||
authServiceId = SignalStore.account().requireAci();
|
||||
}
|
||||
SignalDatabase.groups().fixMissingMasterKey(authServiceId, group.getMasterKey());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -876,8 +826,8 @@ public final class MessageContentProcessor {
|
|||
return null;
|
||||
}
|
||||
|
||||
int expiresInSeconds = message.getExpiresInSeconds();
|
||||
Optional<SignalServiceGroupContext> groupContext = message.getGroupContext();
|
||||
int expiresInSeconds = message.getExpiresInSeconds();
|
||||
Optional<SignalServiceGroupV2> groupContext = message.getGroupContext();
|
||||
|
||||
if (threadRecipient.getExpiresInSeconds() == expiresInSeconds) {
|
||||
log(String.valueOf(content.getTimestamp()), "No change in message expiry for group. Ignoring.");
|
||||
|
@ -1212,8 +1162,8 @@ public final class MessageContentProcessor {
|
|||
|
||||
SignalServiceDataMessage dataMessage = message.getDataMessage().get();
|
||||
if (dataMessage.isGroupV2Message()) {
|
||||
GroupId.V2 groupId = GroupId.v2(dataMessage.getGroupContext().get().getGroupV2().get().getMasterKey());
|
||||
if (handleGv2PreProcessing(groupId, content, dataMessage.getGroupContext().get().getGroupV2().get(), senderRecipient)) {
|
||||
GroupId.V2 groupId = GroupId.v2(dataMessage.getGroupContext().get().getMasterKey());
|
||||
if (handleGv2PreProcessing(groupId, content, dataMessage.getGroupContext().get(), senderRecipient)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1224,9 +1174,6 @@ public final class MessageContentProcessor {
|
|||
handleGroupRecipientUpdate(message, content.getTimestamp());
|
||||
} else if (dataMessage.isEndSession()) {
|
||||
threadId = handleSynchronizeSentEndSessionMessage(message, content.getTimestamp());
|
||||
} else if (dataMessage.isGroupV1Update()) {
|
||||
Long gv1ThreadId = GroupV1MessageProcessor.process(context, content, dataMessage, true);
|
||||
threadId = gv1ThreadId == null ? -1 : gv1ThreadId;
|
||||
} else if (dataMessage.isGroupV2Update()) {
|
||||
handleSynchronizeSentGv2Update(content, message);
|
||||
threadId = SignalDatabase.threads().getOrCreateThreadIdFor(getSyncMessageDestination(message));
|
||||
|
@ -1249,7 +1196,7 @@ public final class MessageContentProcessor {
|
|||
threadId = handleSynchronizeSentTextMessage(message, content.getTimestamp());
|
||||
}
|
||||
|
||||
if (dataMessage.getGroupContext().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.idFromGroupContext(dataMessage.getGroupContext().get()))) {
|
||||
if (dataMessage.getGroupContext().isPresent() && groupDatabase.isUnknownGroup(GroupId.v2(dataMessage.getGroupContext().get().getMasterKey()))) {
|
||||
handleUnknownGroupMessage(content, dataMessage.getGroupContext().get(), senderRecipient);
|
||||
}
|
||||
|
||||
|
@ -1284,7 +1231,7 @@ public final class MessageContentProcessor {
|
|||
log(content.getTimestamp(), "Synchronize sent GV2 update for message with timestamp " + message.getTimestamp());
|
||||
|
||||
SignalServiceDataMessage dataMessage = message.getDataMessage().get();
|
||||
SignalServiceGroupV2 signalServiceGroupV2 = dataMessage.getGroupContext().get().getGroupV2().get();
|
||||
SignalServiceGroupV2 signalServiceGroupV2 = dataMessage.getGroupContext().get();
|
||||
GroupId.V2 groupIdV2 = GroupId.v2(signalServiceGroupV2.getMasterKey());
|
||||
|
||||
if (!updateGv2GroupFromServerOrP2PChange(content, signalServiceGroupV2)) {
|
||||
|
@ -3012,9 +2959,7 @@ public final class MessageContentProcessor {
|
|||
return database.insertMessageInbox(textMessage);
|
||||
}
|
||||
|
||||
private Recipient getSyncMessageDestination(@NonNull SentTranscriptMessage message)
|
||||
throws BadGroupIdException
|
||||
{
|
||||
private Recipient getSyncMessageDestination(@NonNull SentTranscriptMessage message) {
|
||||
return getGroupRecipient(message.getDataMessage().get().getGroupContext()).orElseGet(() -> Recipient.externalPush(message.getDestination().get()));
|
||||
}
|
||||
|
||||
|
@ -3023,13 +2968,12 @@ public final class MessageContentProcessor {
|
|||
return getGroupRecipient(message != null ? message.getGroupContext() : Optional.empty()).orElseGet(() -> Recipient.externalPush(content.getSender()));
|
||||
}
|
||||
|
||||
private Optional<Recipient> getGroupRecipient(Optional<SignalServiceGroupContext> message)
|
||||
throws BadGroupIdException
|
||||
{
|
||||
private Optional<Recipient> getGroupRecipient(Optional<SignalServiceGroupV2> message) {
|
||||
if (message.isPresent()) {
|
||||
return Optional.of(Recipient.externalPossiblyMigratedGroup(GroupUtil.idFromGroupContext(message.get())));
|
||||
return Optional.of(Recipient.externalPossiblyMigratedGroup(GroupId.v2(message.get().getMasterKey())));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient senderRecipient, @NonNull Recipient conversationRecipient, int device) {
|
||||
|
@ -3053,15 +2997,6 @@ public final class MessageContentProcessor {
|
|||
GroupDatabase groupDatabase = SignalDatabase.groups();
|
||||
Optional<GroupId> groupId = GroupUtil.idFromGroupContext(message.getGroupContext());
|
||||
|
||||
if (groupId.isPresent() &&
|
||||
groupId.get().isV1() &&
|
||||
message.isGroupV1Update() &&
|
||||
groupDatabase.groupExists(groupId.get().requireV1().deriveV2MigrationGroupId()))
|
||||
{
|
||||
warn(String.valueOf(content.getTimestamp()), "Ignoring V1 update for a group we've already migrated to V2.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) {
|
||||
return sender.isBlocked();
|
||||
}
|
||||
|
@ -3070,11 +3005,10 @@ public final class MessageContentProcessor {
|
|||
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getSticker().isPresent();
|
||||
boolean isExpireMessage = message.isExpirationUpdate();
|
||||
boolean isGv2Update = message.isGroupV2Update();
|
||||
boolean isContentMessage = !message.isGroupV1Update() && !isGv2Update && !isExpireMessage && (isTextMessage || isMediaMessage);
|
||||
boolean isContentMessage = !isGv2Update && !isExpireMessage && (isTextMessage || isMediaMessage);
|
||||
boolean isGroupActive = groupId.isPresent() && groupDatabase.isActive(groupId.get());
|
||||
boolean isLeaveMessage = message.getGroupContext().isPresent() && message.getGroupContext().get().getGroupV1Type() == SignalServiceGroup.Type.QUIT;
|
||||
|
||||
return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage && !isGv2Update);
|
||||
return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isGv2Update);
|
||||
} else {
|
||||
return sender.isBlocked();
|
||||
}
|
||||
|
|
|
@ -207,15 +207,7 @@ public final class MessageDecryptionUtil {
|
|||
|
||||
if (sender == null) throw new NoSenderException();
|
||||
|
||||
GroupId groupId = null;
|
||||
|
||||
if (e.getGroup().isPresent()) {
|
||||
try {
|
||||
groupId = GroupUtil.idFromGroupContext(e.getGroup().get());
|
||||
} catch (BadGroupIdException ex) {
|
||||
Log.w(TAG, "Bad group id found in unsupported data message", ex);
|
||||
}
|
||||
}
|
||||
GroupId groupId = e.getGroup().isPresent() ? GroupId.v2(e.getGroup().get().getMasterKey()) : null;
|
||||
|
||||
return new ExceptionMetadata(sender, e.getSenderDevice(), groupId);
|
||||
}
|
||||
|
|
|
@ -11,9 +11,8 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
|
|||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.GroupUtil
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2
|
||||
import java.util.Optional
|
||||
|
||||
class IncomingMediaMessage(
|
||||
|
@ -96,7 +95,7 @@ class IncomingMediaMessage(
|
|||
viewOnce: Boolean,
|
||||
unidentified: Boolean,
|
||||
body: Optional<String>,
|
||||
group: Optional<SignalServiceGroupContext>,
|
||||
group: Optional<SignalServiceGroupV2>,
|
||||
attachments: Optional<List<SignalServiceAttachment>>,
|
||||
quote: Optional<QuoteModel>,
|
||||
sharedContacts: Optional<List<Contact>>,
|
||||
|
@ -107,7 +106,7 @@ class IncomingMediaMessage(
|
|||
giftBadge: GiftBadge?
|
||||
) : this(
|
||||
from = from,
|
||||
groupId = if (group.isPresent) GroupUtil.idFromGroupContextOrThrow(group.get()) else null,
|
||||
groupId = if (group.isPresent) GroupId.v2(group.get().masterKey) else null,
|
||||
body = body.orElse(null),
|
||||
isPushMessage = true,
|
||||
storyType = storyType,
|
||||
|
|
|
@ -6,31 +6,23 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.signal.core.util.StringUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.InvalidMessageException;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.groups.BadGroupIdException;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.mms.MessageGroupContext;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -44,7 +36,7 @@ public final class GroupUtil {
|
|||
/**
|
||||
* @return The group context present on the content if one exists, otherwise null.
|
||||
*/
|
||||
public static @Nullable SignalServiceGroupContext getGroupContextIfPresent(@Nullable SignalServiceContent content) {
|
||||
public static @Nullable SignalServiceGroupV2 getGroupContextIfPresent(@Nullable SignalServiceContent content) {
|
||||
if (content == null) {
|
||||
return null;
|
||||
} else if (content.getDataMessage().isPresent() && content.getDataMessage().get().getGroupContext().isPresent()) {
|
||||
|
@ -56,11 +48,7 @@ public final class GroupUtil {
|
|||
{
|
||||
return content.getSyncMessage().get().getSent().get().getDataMessage().get().getGroupContext().get();
|
||||
} else if (content.getStoryMessage().isPresent() && content.getStoryMessage().get().getGroupContext().isPresent()) {
|
||||
try {
|
||||
return SignalServiceGroupContext.create(null, content.getStoryMessage().get().getGroupContext().get());
|
||||
} catch (InvalidMessageException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
return content.getStoryMessage().get().getGroupContext().get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -69,36 +57,12 @@ public final class GroupUtil {
|
|||
/**
|
||||
* Result may be a v1 or v2 GroupId.
|
||||
*/
|
||||
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()) {
|
||||
return GroupId.v2(groupContext.getGroupV2().get().getMasterKey());
|
||||
} else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
throws BadGroupIdException
|
||||
{
|
||||
public static @NonNull Optional<GroupId> idFromGroupContext(@NonNull Optional<SignalServiceGroupV2> groupContext) {
|
||||
if (groupContext.isPresent()) {
|
||||
return Optional.of(idFromGroupContext(groupContext.get()));
|
||||
return Optional.of(GroupId.v2(groupContext.get().getMasterKey()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static @NonNull GroupMasterKey requireMasterKey(@NonNull byte[] masterKey) {
|
||||
|
@ -129,36 +93,14 @@ public final class GroupUtil {
|
|||
@NonNull GroupId.Push groupId)
|
||||
{
|
||||
if (groupId.isV2()) {
|
||||
GroupDatabase groupDatabase = SignalDatabase.groups();
|
||||
GroupDatabase.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
|
||||
GroupDatabase.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
|
||||
SignalServiceGroupV2 group = SignalServiceGroupV2.newBuilder(v2GroupProperties.getGroupMasterKey())
|
||||
.withRevision(v2GroupProperties.getGroupRevision())
|
||||
.build();
|
||||
dataMessageBuilder.asGroupMessage(group);
|
||||
} else {
|
||||
dataMessageBuilder.asGroupMessage(new SignalServiceGroup(groupId.getDecodedId()));
|
||||
}
|
||||
}
|
||||
|
||||
public static OutgoingGroupUpdateMessage createGroupV1LeaveMessage(@NonNull GroupId.V1 groupId,
|
||||
@NonNull Recipient groupRecipient)
|
||||
{
|
||||
GroupContext groupContext = GroupContext.newBuilder()
|
||||
.setId(ByteString.copyFrom(groupId.getDecodedId()))
|
||||
.setType(GroupContext.Type.QUIT)
|
||||
.build();
|
||||
|
||||
return new OutgoingGroupUpdateMessage(groupRecipient,
|
||||
groupContext,
|
||||
null,
|
||||
System.currentTimeMillis(),
|
||||
0,
|
||||
false,
|
||||
null,
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
GroupDatabase groupDatabase = SignalDatabase.groups();
|
||||
GroupDatabase.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
|
||||
GroupDatabase.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
|
||||
SignalServiceGroupV2 group = SignalServiceGroupV2.newBuilder(v2GroupProperties.getGroupMasterKey())
|
||||
.withRevision(v2GroupProperties.getGroupRevision())
|
||||
.build();
|
||||
dataMessageBuilder.asGroupMessage(group);
|
||||
}
|
||||
}
|
||||
|
||||
public static class GroupDescription {
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.thoughtcrime.securesms.jobmanager.Data;
|
|||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData;
|
||||
import org.thoughtcrime.securesms.jobs.FailingJob;
|
||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
|
@ -29,35 +28,6 @@ public class RecipientIdFollowUpJobMigrationTest {
|
|||
@Mock
|
||||
private MockedStatic<Job.Parameters> jobParametersMockedStatic;
|
||||
|
||||
@Test
|
||||
public void migrate_requestGroupInfoJob_good() throws Exception {
|
||||
JobData testData = new JobData("RequestGroupInfoJob", null, new Data.Builder().putString("source", "1")
|
||||
.putString("group_id", "__textsecure_group__!abcdef0123456789abcdef0123456789")
|
||||
.build());
|
||||
RecipientIdFollowUpJobMigration subject = new RecipientIdFollowUpJobMigration();
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("RequestGroupInfoJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("source"));
|
||||
assertEquals("__textsecure_group__!abcdef0123456789abcdef0123456789", converted.getData().getString("group_id"));
|
||||
|
||||
new RequestGroupInfoJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_requestGroupInfoJob_bad() throws Exception {
|
||||
JobData testData = new JobData("RequestGroupInfoJob", null, new Data.Builder().putString("source", "1")
|
||||
.build());
|
||||
RecipientIdFollowUpJobMigration subject = new RecipientIdFollowUpJobMigration();
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("FailingJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
|
||||
new FailingJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_sendDeliveryReceiptJob_good() throws Exception {
|
||||
JobData testData = new JobData("SendDeliveryReceiptJob", null, new Data.Builder().putString("recipient", "1")
|
||||
|
|
|
@ -19,10 +19,8 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
|
|||
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
||||
|
@ -83,24 +81,6 @@ public class RecipientIdJobMigrationTest {
|
|||
new MultiDeviceViewOnceOpenJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_requestGroupInfoJob() throws Exception {
|
||||
JobData testData = new JobData("RequestGroupInfoJob", null, new Data.Builder().putString("source", "+16101234567")
|
||||
.putString("group_id", "__textsecure_group__!abcdef0123456789abcdef0123456789")
|
||||
.build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("RequestGroupInfoJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("source"));
|
||||
assertEquals("__textsecure_group__!abcdef0123456789abcdef0123456789", converted.getData().getString("group_id"));
|
||||
|
||||
new RequestGroupInfoJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_sendDeliveryReceiptJob() throws Exception {
|
||||
JobData testData = new JobData("SendDeliveryReceiptJob", null, new Data.Builder().putString("address", "+16101234567")
|
||||
|
@ -180,24 +160,6 @@ public class RecipientIdJobMigrationTest {
|
|||
new PushGroupSendJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_pushGroupUpdateJob() throws Exception {
|
||||
JobData testData = new JobData("PushGroupUpdateJob", null, new Data.Builder().putString("source", "+16101234567")
|
||||
.putString("group_id", "__textsecure_group__!abcdef0123456789abcdef0123456789")
|
||||
.build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("PushGroupUpdateJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("source"));
|
||||
assertEquals("__textsecure_group__!abcdef0123456789abcdef0123456789", converted.getData().getString("group_id"));
|
||||
|
||||
new PushGroupUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_directoryRefreshJob_null() throws Exception {
|
||||
JobData testData = new JobData("DirectoryRefreshJob", "DirectoryRefreshJob", new Data.Builder().putString("address", null).putBoolean("notify_of_new_users", true).build());
|
||||
|
|
|
@ -36,8 +36,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPoin
|
|||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServicePreview;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||
|
@ -102,7 +100,6 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Attach
|
|||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Preview;
|
||||
|
@ -834,14 +831,7 @@ public class SignalServiceMessageSender {
|
|||
}
|
||||
|
||||
if (message.getGroupContext().isPresent()) {
|
||||
SignalServiceGroupContext groupContext = message.getGroupContext().get();
|
||||
if (groupContext.getGroupV1().isPresent()) {
|
||||
builder.setGroup(createGroupContent(groupContext.getGroupV1().get()));
|
||||
}
|
||||
|
||||
if (groupContext.getGroupV2().isPresent()) {
|
||||
builder.setGroupV2(createGroupContent(groupContext.getGroupV2().get()));
|
||||
}
|
||||
builder.setGroupV2(createGroupContent(message.getGroupContext().get()));
|
||||
}
|
||||
|
||||
if (message.isEndSession()) {
|
||||
|
@ -1473,47 +1463,6 @@ public class SignalServiceMessageSender {
|
|||
return builder;
|
||||
}
|
||||
|
||||
private GroupContext createGroupContent(SignalServiceGroup group) throws IOException {
|
||||
GroupContext.Builder builder = GroupContext.newBuilder();
|
||||
builder.setId(ByteString.copyFrom(group.getGroupId()));
|
||||
|
||||
if (group.getType() != SignalServiceGroup.Type.DELIVER) {
|
||||
if (group.getType() == SignalServiceGroup.Type.UPDATE) builder.setType(GroupContext.Type.UPDATE);
|
||||
else if (group.getType() == SignalServiceGroup.Type.QUIT) builder.setType(GroupContext.Type.QUIT);
|
||||
else if (group.getType() == SignalServiceGroup.Type.REQUEST_INFO) builder.setType(GroupContext.Type.REQUEST_INFO);
|
||||
else throw new AssertionError("Unknown type: " + group.getType());
|
||||
|
||||
if (group.getName().isPresent()) {
|
||||
builder.setName(group.getName().get());
|
||||
}
|
||||
|
||||
if (group.getMembers().isPresent()) {
|
||||
for (SignalServiceAddress address : group.getMembers().get()) {
|
||||
if (address.getNumber().isPresent()) {
|
||||
builder.addMembersE164(address.getNumber().get());
|
||||
|
||||
GroupContext.Member.Builder memberBuilder = GroupContext.Member.newBuilder();
|
||||
memberBuilder.setE164(address.getNumber().get());
|
||||
|
||||
builder.addMembers(memberBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (group.getAvatar().isPresent()) {
|
||||
if (group.getAvatar().get().isStream()) {
|
||||
builder.setAvatar(createAttachmentPointer(group.getAvatar().get().asStream()));
|
||||
} else {
|
||||
builder.setAvatar(createAttachmentPointer(group.getAvatar().get().asPointer()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.setType(GroupContext.Type.DELIVER);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static GroupContextV2 createGroupContent(SignalServiceGroupV2 group) {
|
||||
GroupContextV2.Builder builder = GroupContextV2.newBuilder()
|
||||
.setMasterKey(ByteString.copyFrom(group.getMasterKey().serialize()))
|
||||
|
|
|
@ -594,15 +594,8 @@ public final class SignalServiceContent {
|
|||
SignalServiceProtos.DataMessage content)
|
||||
throws UnsupportedDataMessageException, InvalidMessageStructureException
|
||||
{
|
||||
SignalServiceGroupV2 groupInfoV2 = createGroupV2Info(content);
|
||||
Optional<SignalServiceGroupContext> groupContext;
|
||||
|
||||
try {
|
||||
groupContext = SignalServiceGroupContext.createOptional(null, groupInfoV2);
|
||||
} catch (InvalidMessageException e) {
|
||||
throw new InvalidMessageStructureException(e);
|
||||
}
|
||||
|
||||
SignalServiceGroupV2 groupInfoV2 = createGroupV2Info(content);
|
||||
Optional<SignalServiceGroupV2> groupContext = Optional.ofNullable(groupInfoV2);
|
||||
|
||||
List<SignalServiceAttachment> attachments = new LinkedList<>();
|
||||
boolean endSession = ((content.getFlags() & SignalServiceProtos.DataMessage.Flags.END_SESSION_VALUE ) != 0);
|
||||
|
@ -649,7 +642,6 @@ public final class SignalServiceContent {
|
|||
}
|
||||
|
||||
return new SignalServiceDataMessage(metadata.getTimestamp(),
|
||||
null,
|
||||
groupInfoV2,
|
||||
attachments,
|
||||
content.hasBody() ? content.getBody() : null,
|
||||
|
|
|
@ -27,7 +27,7 @@ public class SignalServiceDataMessage {
|
|||
private final long timestamp;
|
||||
private final Optional<List<SignalServiceAttachment>> attachments;
|
||||
private final Optional<String> body;
|
||||
private final Optional<SignalServiceGroupContext> group;
|
||||
private final Optional<SignalServiceGroupV2> group;
|
||||
private final Optional<byte[]> profileKey;
|
||||
private final boolean endSession;
|
||||
private final boolean expirationUpdate;
|
||||
|
@ -50,7 +50,6 @@ public class SignalServiceDataMessage {
|
|||
* Construct a SignalServiceDataMessage.
|
||||
*
|
||||
* @param timestamp The sent timestamp.
|
||||
* @param group The group information (or null if none).
|
||||
* @param groupV2 The group information (or null if none).
|
||||
* @param attachments The attachments (or null if none).
|
||||
* @param body The message contents.
|
||||
|
@ -58,7 +57,6 @@ public class SignalServiceDataMessage {
|
|||
* @param expiresInSeconds Number of seconds in which the message should disappear after being seen.
|
||||
*/
|
||||
SignalServiceDataMessage(long timestamp,
|
||||
SignalServiceGroup group,
|
||||
SignalServiceGroupV2 groupV2,
|
||||
List<SignalServiceAttachment> attachments,
|
||||
String body,
|
||||
|
@ -80,12 +78,7 @@ public class SignalServiceDataMessage {
|
|||
StoryContext storyContext,
|
||||
GiftBadge giftBadge)
|
||||
{
|
||||
try {
|
||||
this.group = SignalServiceGroupContext.createOptional(group, groupV2);
|
||||
} catch (InvalidMessageException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
this.group = Optional.ofNullable(groupV2);
|
||||
this.timestamp = timestamp;
|
||||
this.body = OptionalUtil.absentIfEmpty(body);
|
||||
this.endSession = endSession;
|
||||
|
@ -156,7 +149,7 @@ public class SignalServiceDataMessage {
|
|||
/**
|
||||
* @return The message group context (if any).
|
||||
*/
|
||||
public Optional<SignalServiceGroupContext> getGroupContext() {
|
||||
public Optional<SignalServiceGroupV2> getGroupContext() {
|
||||
return group;
|
||||
}
|
||||
|
||||
|
@ -172,20 +165,13 @@ public class SignalServiceDataMessage {
|
|||
return profileKeyUpdate;
|
||||
}
|
||||
|
||||
public boolean isGroupV1Update() {
|
||||
return group.isPresent() &&
|
||||
group.get().getGroupV1().isPresent() &&
|
||||
group.get().getGroupV1().get().getType() != SignalServiceGroup.Type.DELIVER;
|
||||
}
|
||||
|
||||
public boolean isGroupV2Message() {
|
||||
return group.isPresent() &&
|
||||
group.get().getGroupV2().isPresent();
|
||||
return group.isPresent();
|
||||
}
|
||||
|
||||
public boolean isGroupV2Update() {
|
||||
return isGroupV2Message() &&
|
||||
group.get().getGroupV2().get().hasSignedGroupChange() &&
|
||||
return group.isPresent() &&
|
||||
group.get().hasSignedGroupChange() &&
|
||||
!hasRenderableContent();
|
||||
}
|
||||
|
||||
|
@ -265,8 +251,8 @@ public class SignalServiceDataMessage {
|
|||
public Optional<byte[]> getGroupId() {
|
||||
byte[] groupId = null;
|
||||
|
||||
if (getGroupContext().isPresent() && getGroupContext().get().getGroupV2().isPresent()) {
|
||||
SignalServiceGroupV2 gv2 = getGroupContext().get().getGroupV2().get();
|
||||
if (getGroupContext().isPresent() && getGroupContext().isPresent()) {
|
||||
SignalServiceGroupV2 gv2 = getGroupContext().get();
|
||||
groupId = GroupSecretParams.deriveFromMasterKey(gv2.getMasterKey())
|
||||
.getPublicParams()
|
||||
.getGroupIdentifier()
|
||||
|
@ -284,7 +270,6 @@ public class SignalServiceDataMessage {
|
|||
private List<Mention> mentions = new LinkedList<>();
|
||||
|
||||
private long timestamp;
|
||||
private SignalServiceGroup group;
|
||||
private SignalServiceGroupV2 groupV2;
|
||||
private String body;
|
||||
private boolean endSession;
|
||||
|
@ -309,18 +294,7 @@ public class SignalServiceDataMessage {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder asGroupMessage(SignalServiceGroup group) {
|
||||
if (this.groupV2 != null) {
|
||||
throw new AssertionError("Can not contain both V1 and V2 group contexts.");
|
||||
}
|
||||
this.group = group;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder asGroupMessage(SignalServiceGroupV2 group) {
|
||||
if (this.group != null) {
|
||||
throw new AssertionError("Can not contain both V1 and V2 group contexts.");
|
||||
}
|
||||
this.groupV2 = group;
|
||||
return this;
|
||||
}
|
||||
|
@ -440,7 +414,7 @@ public class SignalServiceDataMessage {
|
|||
|
||||
public SignalServiceDataMessage build() {
|
||||
if (timestamp == 0) timestamp = System.currentTimeMillis();
|
||||
return new SignalServiceDataMessage(timestamp, group, groupV2, attachments, body, endSession,
|
||||
return new SignalServiceDataMessage(timestamp, groupV2, attachments, body, endSession,
|
||||
expiresInSeconds, expirationUpdate, profileKey,
|
||||
profileKeyUpdate, quote, sharedContacts, previews,
|
||||
mentions, sticker, viewOnce, reaction, remoteDelete,
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
package org.whispersystems.signalservice.api.messages;
|
||||
|
||||
import org.signal.libsignal.protocol.InvalidMessageException;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
public final class SignalServiceGroupContext {
|
||||
|
||||
private final Optional<SignalServiceGroup> groupV1;
|
||||
private final Optional<SignalServiceGroupV2> groupV2;
|
||||
|
||||
private SignalServiceGroupContext(SignalServiceGroup groupV1) {
|
||||
this.groupV1 = Optional.of(groupV1);
|
||||
this.groupV2 = Optional.empty();
|
||||
}
|
||||
|
||||
private SignalServiceGroupContext(SignalServiceGroupV2 groupV2) {
|
||||
this.groupV1 = Optional.empty();
|
||||
this.groupV2 = Optional.of(groupV2);
|
||||
}
|
||||
|
||||
public Optional<SignalServiceGroup> getGroupV1() {
|
||||
return groupV1;
|
||||
}
|
||||
|
||||
public Optional<SignalServiceGroupV2> getGroupV2() {
|
||||
return groupV2;
|
||||
}
|
||||
|
||||
static Optional<SignalServiceGroupContext> createOptional(SignalServiceGroup groupV1, SignalServiceGroupV2 groupV2)
|
||||
throws InvalidMessageException
|
||||
{
|
||||
return Optional.ofNullable(create(groupV1, groupV2));
|
||||
}
|
||||
|
||||
public static SignalServiceGroupContext create(SignalServiceGroup groupV1, SignalServiceGroupV2 groupV2)
|
||||
throws InvalidMessageException
|
||||
{
|
||||
if (groupV1 == null && groupV2 == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (groupV1 != null && groupV2 != null) {
|
||||
throw new InvalidMessageException("Message cannot have both V1 and V2 group contexts.");
|
||||
}
|
||||
|
||||
if (groupV1 != null) {
|
||||
return new SignalServiceGroupContext(groupV1);
|
||||
} else {
|
||||
return new SignalServiceGroupContext(groupV2);
|
||||
}
|
||||
}
|
||||
|
||||
public SignalServiceGroup.Type getGroupV1Type() {
|
||||
if (groupV1.isPresent()) {
|
||||
return groupV1.get().getType();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -12,14 +12,14 @@ import java.util.Optional;
|
|||
*/
|
||||
public abstract class UnsupportedDataMessageException extends Exception {
|
||||
|
||||
private final String sender;
|
||||
private final int senderDevice;
|
||||
private final Optional<SignalServiceGroupContext> group;
|
||||
private final String sender;
|
||||
private final int senderDevice;
|
||||
private final Optional<SignalServiceGroupV2> group;
|
||||
|
||||
protected UnsupportedDataMessageException(String message,
|
||||
String sender,
|
||||
int senderDevice,
|
||||
Optional<SignalServiceGroupContext> group)
|
||||
Optional<SignalServiceGroupV2> group)
|
||||
{
|
||||
super(message);
|
||||
this.sender = sender;
|
||||
|
@ -35,7 +35,7 @@ public abstract class UnsupportedDataMessageException extends Exception {
|
|||
return senderDevice;
|
||||
}
|
||||
|
||||
public Optional<SignalServiceGroupContext> getGroup() {
|
||||
public Optional<SignalServiceGroupV2> getGroup() {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -16,7 +16,7 @@ public final class UnsupportedDataMessageProtocolVersionException extends Unsupp
|
|||
int requiredVersion,
|
||||
String sender,
|
||||
int senderDevice,
|
||||
Optional<SignalServiceGroupContext> group) {
|
||||
Optional<SignalServiceGroupV2> group) {
|
||||
super("Required version: " + requiredVersion + ", Our version: " + currentVersion, sender, senderDevice, group);
|
||||
this.requiredVersion = requiredVersion;
|
||||
}
|
||||
|
|
|
@ -313,7 +313,7 @@ message DataMessage {
|
|||
|
||||
optional string body = 1;
|
||||
repeated AttachmentPointer attachments = 2;
|
||||
optional GroupContext group = 3;
|
||||
reserved /*groupV1*/ 3;
|
||||
optional GroupContextV2 groupV2 = 15;
|
||||
optional uint32 flags = 4;
|
||||
optional uint32 expireTimer = 5;
|
||||
|
|
Loading…
Add table
Reference in a new issue