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);
|
Recipient recipient = Recipient.resolved(member);
|
||||||
if (recipient.hasE164()) {
|
if (recipient.hasE164()) {
|
||||||
e164Members.add(recipient.requireE164());
|
e164Members.add(recipient.requireE164());
|
||||||
uuidMembers.add(GroupV1MessageProcessor.createMember(recipient.requireE164()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,6 +215,6 @@ final class GroupManagerV1 {
|
||||||
return Optional.empty();
|
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;
|
break;
|
||||||
case NOT_A_MEMBER:
|
case NOT_A_MEMBER:
|
||||||
Log.w(TAG, "The migrated group already exists, but we are not a member. Doing a local leave.");
|
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;
|
return;
|
||||||
case FULL_OR_PENDING_MEMBER:
|
case FULL_OR_PENDING_MEMBER:
|
||||||
Log.w(TAG, "The migrated group already exists, and we're in it. Continuing on.");
|
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!");
|
throw new IOException("[Local] The group should exist already!");
|
||||||
} catch (GroupNotAMemberException e) {
|
} catch (GroupNotAMemberException e) {
|
||||||
Log.w(TAG, "[Local] We are not in the group. Doing a local leave.");
|
Log.w(TAG, "[Local] We are not in the group. Doing a local leave.");
|
||||||
handleLeftBehind(context, gv1Id, groupRecipient, threadId);
|
handleLeftBehind(gv1Id);
|
||||||
return null;
|
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) {
|
private static void handleLeftBehind(@NonNull GroupId.V1 gv1Id) {
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
SignalDatabase.groups().setActive(gv1Id, false);
|
SignalDatabase.groups().setActive(gv1Id, false);
|
||||||
SignalDatabase.groups().remove(gv1Id, Recipient.self().getId());
|
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()) {
|
if (content != null && content.getDataMessage().isPresent() && content.getDataMessage().get().getGroupContext().isPresent()) {
|
||||||
Log.i(TAG, "Migrating a group message.");
|
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();
|
GroupId groupId = GroupId.v2(content.getDataMessage().get().getGroupContext().get().getMasterKey());
|
||||||
} catch (BadGroupIdException e) {
|
Recipient recipient = Recipient.externalGroupExact(groupId);
|
||||||
Log.w(TAG, "Bad groupId! Using default queue.");
|
|
||||||
}
|
suffix = recipient.getId().toQueueKey();
|
||||||
} else if (content != null) {
|
} else if (content != null) {
|
||||||
Log.i(TAG, "Migrating an individual message.");
|
Log.i(TAG, "Migrating an individual message.");
|
||||||
suffix = RecipientId.from(content.getSender()).toQueueKey();
|
suffix = RecipientId.from(content.getSender()).toQueueKey();
|
||||||
|
|
|
@ -142,7 +142,6 @@ public final class JobManagerFactories {
|
||||||
put(PushDistributionListSendJob.KEY, new PushDistributionListSendJob.Factory());
|
put(PushDistributionListSendJob.KEY, new PushDistributionListSendJob.Factory());
|
||||||
put(PushGroupSendJob.KEY, new PushGroupSendJob.Factory());
|
put(PushGroupSendJob.KEY, new PushGroupSendJob.Factory());
|
||||||
put(PushGroupSilentUpdateSendJob.KEY, new PushGroupSilentUpdateSendJob.Factory());
|
put(PushGroupSilentUpdateSendJob.KEY, new PushGroupSilentUpdateSendJob.Factory());
|
||||||
put(PushGroupUpdateJob.KEY, new PushGroupUpdateJob.Factory());
|
|
||||||
put(PushMediaSendJob.KEY, new PushMediaSendJob.Factory());
|
put(PushMediaSendJob.KEY, new PushMediaSendJob.Factory());
|
||||||
put(PushNotificationReceiveJob.KEY, new PushNotificationReceiveJob.Factory());
|
put(PushNotificationReceiveJob.KEY, new PushNotificationReceiveJob.Factory());
|
||||||
put(PushProcessEarlyMessagesJob.KEY, new PushProcessEarlyMessagesJob.Factory());
|
put(PushProcessEarlyMessagesJob.KEY, new PushProcessEarlyMessagesJob.Factory());
|
||||||
|
@ -156,7 +155,6 @@ public final class JobManagerFactories {
|
||||||
put(RemoteConfigRefreshJob.KEY, new RemoteConfigRefreshJob.Factory());
|
put(RemoteConfigRefreshJob.KEY, new RemoteConfigRefreshJob.Factory());
|
||||||
put(RemoteDeleteSendJob.KEY, new RemoteDeleteSendJob.Factory());
|
put(RemoteDeleteSendJob.KEY, new RemoteDeleteSendJob.Factory());
|
||||||
put(ReportSpamJob.KEY, new ReportSpamJob.Factory());
|
put(ReportSpamJob.KEY, new ReportSpamJob.Factory());
|
||||||
put(RequestGroupInfoJob.KEY, new RequestGroupInfoJob.Factory());
|
|
||||||
put(ResendMessageJob.KEY, new ResendMessageJob.Factory());
|
put(ResendMessageJob.KEY, new ResendMessageJob.Factory());
|
||||||
put(ResumableUploadSpecJob.KEY, new ResumableUploadSpecJob.Factory());
|
put(ResumableUploadSpecJob.KEY, new ResumableUploadSpecJob.Factory());
|
||||||
put(RequestGroupV2InfoWorkerJob.KEY, new RequestGroupV2InfoWorkerJob.Factory());
|
put(RequestGroupV2InfoWorkerJob.KEY, new RequestGroupV2InfoWorkerJob.Factory());
|
||||||
|
@ -240,6 +238,8 @@ public final class JobManagerFactories {
|
||||||
put("StorageSyncJob", new StorageSyncJob.Factory());
|
put("StorageSyncJob", new StorageSyncJob.Factory());
|
||||||
put("WakeGroupV2Job", new FailingJob.Factory());
|
put("WakeGroupV2Job", new FailingJob.Factory());
|
||||||
put("LeaveGroupJob", 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.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.NoCredentialForRedemptionTimeException;
|
import org.whispersystems.signalservice.api.groupsv2.NoCredentialForRedemptionTimeException;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
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 org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -120,27 +120,23 @@ public final class PushProcessMessageJob extends BaseJob {
|
||||||
.setMaxAttempts(Parameters.UNLIMITED);
|
.setMaxAttempts(Parameters.UNLIMITED);
|
||||||
|
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
SignalServiceGroupContext signalServiceGroupContext = GroupUtil.getGroupContextIfPresent(content);
|
SignalServiceGroupV2 signalServiceGroupContext = GroupUtil.getGroupContextIfPresent(content);
|
||||||
|
|
||||||
if (signalServiceGroupContext != null) {
|
if (signalServiceGroupContext != null) {
|
||||||
try {
|
GroupId groupId = GroupId.v2(signalServiceGroupContext.getMasterKey());
|
||||||
GroupId groupId = GroupUtil.idFromGroupContext(signalServiceGroupContext);
|
|
||||||
|
|
||||||
queueName = getQueueName(Recipient.externalPossiblyMigratedGroup(groupId).getId());
|
queueName = getQueueName(Recipient.externalPossiblyMigratedGroup(groupId).getId());
|
||||||
|
|
||||||
if (groupId.isV2()) {
|
if (groupId.isV2()) {
|
||||||
int localRevision = SignalDatabase.groups().getGroupV2Revision(groupId.requireV2());
|
int localRevision = SignalDatabase.groups().getGroupV2Revision(groupId.requireV2());
|
||||||
|
|
||||||
if (signalServiceGroupContext.getGroupV2().get().getRevision() > localRevision ||
|
if (signalServiceGroupContext.getRevision() > localRevision ||
|
||||||
SignalDatabase.groups().getGroupV1ByExpectedV2(groupId.requireV2()).isPresent())
|
SignalDatabase.groups().getGroupV1ByExpectedV2(groupId.requireV2()).isPresent())
|
||||||
{
|
{
|
||||||
Log.i(TAG, "Adding network constraint to group-related job.");
|
Log.i(TAG, "Adding network constraint to group-related job.");
|
||||||
builder.addConstraint(NetworkConstraint.KEY)
|
builder.addConstraint(NetworkConstraint.KEY)
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(30));
|
.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()) {
|
} 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()));
|
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.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.signalservice.api.SignalSessionLock;
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
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.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -177,24 +177,19 @@ public class IncomingMessageProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean needsToEnqueueProcessing(@NonNull DecryptionResult result) {
|
private boolean needsToEnqueueProcessing(@NonNull DecryptionResult result) {
|
||||||
SignalServiceGroupContext groupContext = GroupUtil.getGroupContextIfPresent(result.getContent());
|
SignalServiceGroupV2 groupContext = GroupUtil.getGroupContextIfPresent(result.getContent());
|
||||||
|
|
||||||
if (groupContext != null) {
|
if (groupContext != null) {
|
||||||
try {
|
GroupId groupId = GroupId.v2(groupContext.getMasterKey());
|
||||||
GroupId groupId = GroupUtil.idFromGroupContext(groupContext);
|
|
||||||
|
|
||||||
if (groupId.isV2()) {
|
if (groupId.isV2()) {
|
||||||
String queueName = PushProcessMessageJob.getQueueName(Recipient.externalPossiblyMigratedGroup(groupId).getId());
|
String queueName = PushProcessMessageJob.getQueueName(Recipient.externalPossiblyMigratedGroup(groupId).getId());
|
||||||
GroupDatabase groupDatabase = SignalDatabase.groups();
|
GroupDatabase groupDatabase = SignalDatabase.groups();
|
||||||
|
|
||||||
return !jobManager.isQueueEmpty(queueName) ||
|
return !jobManager.isQueueEmpty(queueName) ||
|
||||||
groupContext.getGroupV2().get().getRevision() > groupDatabase.getGroupV2Revision(groupId.requireV2()) ||
|
groupContext.getRevision() > groupDatabase.getGroupV2Revision(groupId.requireV2()) ||
|
||||||
groupDatabase.getGroupV1ByExpectedV2(groupId.requireV2()).isPresent();
|
groupDatabase.getGroupV1ByExpectedV2(groupId.requireV2()).isPresent();
|
||||||
} else {
|
} else {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (BadGroupIdException e) {
|
|
||||||
Log.w(TAG, "Bad group ID!");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (result.getContent() != null) {
|
} 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.GroupId;
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||||
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
||||||
import org.thoughtcrime.securesms.groups.GroupV1MessageProcessor;
|
|
||||||
import org.thoughtcrime.securesms.groups.GroupsV1MigrationUtil;
|
import org.thoughtcrime.securesms.groups.GroupsV1MigrationUtil;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
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.PushProcessMessageJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob;
|
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.ResendMessageJob;
|
import org.thoughtcrime.securesms.jobs.ResendMessageJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
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.SignalServiceAttachmentPointer;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServicePreview;
|
import org.whispersystems.signalservice.api.messages.SignalServicePreview;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||||
|
@ -283,7 +279,7 @@ public final class MessageContentProcessor {
|
||||||
boolean isGv2Message = groupId.isPresent() && groupId.get().isV2();
|
boolean isGv2Message = groupId.isPresent() && groupId.get().isV2();
|
||||||
|
|
||||||
if (isGv2Message) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,7 +288,6 @@ public final class MessageContentProcessor {
|
||||||
|
|
||||||
if (isInvalidMessage(message)) handleInvalidMessage(content.getSender(), content.getSenderDevice(), groupId, content.getTimestamp(), smsMessageId);
|
if (isInvalidMessage(message)) handleInvalidMessage(content.getSender(), content.getSenderDevice(), groupId, content.getTimestamp(), smsMessageId);
|
||||||
else if (message.isEndSession()) messageId = handleEndSessionMessage(content, smsMessageId, senderRecipient);
|
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.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() && message.getStoryContext().isPresent()) messageId = handleStoryReaction(content, message, senderRecipient);
|
||||||
else if (message.getReaction().isPresent()) messageId = handleReaction(content, message, senderRecipient);
|
else if (message.getReaction().isPresent()) messageId = handleReaction(content, message, senderRecipient);
|
||||||
|
@ -516,20 +511,6 @@ public final class MessageContentProcessor {
|
||||||
return false;
|
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.
|
* 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.
|
* 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;
|
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,
|
private void handleUnknownGroupMessage(@NonNull SignalServiceContent content,
|
||||||
@NonNull SignalServiceGroupContext group,
|
@NonNull SignalServiceGroupV2 group,
|
||||||
@NonNull Recipient senderRecipient)
|
@NonNull Recipient senderRecipient)
|
||||||
throws BadGroupIdException
|
throws BadGroupIdException
|
||||||
{
|
{
|
||||||
log(content.getTimestamp(), "Unknown group message.");
|
log(content.getTimestamp(), "Unknown group message.");
|
||||||
|
|
||||||
if (group.getGroupV1().isPresent()) {
|
warn(content.getTimestamp(), "Received a GV2 message for a group we have no knowledge of -- attempting to fix this state.");
|
||||||
SignalServiceGroup groupV1 = group.getGroupV1().get();
|
ServiceId authServiceId = ServiceId.parseOrNull(content.getDestinationUuid());
|
||||||
if (groupV1.getType() != SignalServiceGroup.Type.REQUEST_INFO) {
|
if (authServiceId == null) {
|
||||||
ApplicationDependencies.getJobManager().add(new RequestGroupInfoJob(senderRecipient.getId(), GroupId.v1(groupV1.getGroupId())));
|
warn(content.getTimestamp(), "Group message missing destination uuid, defaulting to ACI");
|
||||||
} else {
|
authServiceId = SignalStore.account().requireAci();
|
||||||
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.");
|
|
||||||
}
|
}
|
||||||
|
SignalDatabase.groups().fixMissingMasterKey(authServiceId, group.getMasterKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -876,8 +826,8 @@ public final class MessageContentProcessor {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int expiresInSeconds = message.getExpiresInSeconds();
|
int expiresInSeconds = message.getExpiresInSeconds();
|
||||||
Optional<SignalServiceGroupContext> groupContext = message.getGroupContext();
|
Optional<SignalServiceGroupV2> groupContext = message.getGroupContext();
|
||||||
|
|
||||||
if (threadRecipient.getExpiresInSeconds() == expiresInSeconds) {
|
if (threadRecipient.getExpiresInSeconds() == expiresInSeconds) {
|
||||||
log(String.valueOf(content.getTimestamp()), "No change in message expiry for group. Ignoring.");
|
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();
|
SignalServiceDataMessage dataMessage = message.getDataMessage().get();
|
||||||
if (dataMessage.isGroupV2Message()) {
|
if (dataMessage.isGroupV2Message()) {
|
||||||
GroupId.V2 groupId = GroupId.v2(dataMessage.getGroupContext().get().getGroupV2().get().getMasterKey());
|
GroupId.V2 groupId = GroupId.v2(dataMessage.getGroupContext().get().getMasterKey());
|
||||||
if (handleGv2PreProcessing(groupId, content, dataMessage.getGroupContext().get().getGroupV2().get(), senderRecipient)) {
|
if (handleGv2PreProcessing(groupId, content, dataMessage.getGroupContext().get(), senderRecipient)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1224,9 +1174,6 @@ public final class MessageContentProcessor {
|
||||||
handleGroupRecipientUpdate(message, content.getTimestamp());
|
handleGroupRecipientUpdate(message, content.getTimestamp());
|
||||||
} else if (dataMessage.isEndSession()) {
|
} else if (dataMessage.isEndSession()) {
|
||||||
threadId = handleSynchronizeSentEndSessionMessage(message, content.getTimestamp());
|
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()) {
|
} else if (dataMessage.isGroupV2Update()) {
|
||||||
handleSynchronizeSentGv2Update(content, message);
|
handleSynchronizeSentGv2Update(content, message);
|
||||||
threadId = SignalDatabase.threads().getOrCreateThreadIdFor(getSyncMessageDestination(message));
|
threadId = SignalDatabase.threads().getOrCreateThreadIdFor(getSyncMessageDestination(message));
|
||||||
|
@ -1249,7 +1196,7 @@ public final class MessageContentProcessor {
|
||||||
threadId = handleSynchronizeSentTextMessage(message, content.getTimestamp());
|
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);
|
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());
|
log(content.getTimestamp(), "Synchronize sent GV2 update for message with timestamp " + message.getTimestamp());
|
||||||
|
|
||||||
SignalServiceDataMessage dataMessage = message.getDataMessage().get();
|
SignalServiceDataMessage dataMessage = message.getDataMessage().get();
|
||||||
SignalServiceGroupV2 signalServiceGroupV2 = dataMessage.getGroupContext().get().getGroupV2().get();
|
SignalServiceGroupV2 signalServiceGroupV2 = dataMessage.getGroupContext().get();
|
||||||
GroupId.V2 groupIdV2 = GroupId.v2(signalServiceGroupV2.getMasterKey());
|
GroupId.V2 groupIdV2 = GroupId.v2(signalServiceGroupV2.getMasterKey());
|
||||||
|
|
||||||
if (!updateGv2GroupFromServerOrP2PChange(content, signalServiceGroupV2)) {
|
if (!updateGv2GroupFromServerOrP2PChange(content, signalServiceGroupV2)) {
|
||||||
|
@ -3012,9 +2959,7 @@ public final class MessageContentProcessor {
|
||||||
return database.insertMessageInbox(textMessage);
|
return database.insertMessageInbox(textMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recipient getSyncMessageDestination(@NonNull SentTranscriptMessage message)
|
private Recipient getSyncMessageDestination(@NonNull SentTranscriptMessage message) {
|
||||||
throws BadGroupIdException
|
|
||||||
{
|
|
||||||
return getGroupRecipient(message.getDataMessage().get().getGroupContext()).orElseGet(() -> Recipient.externalPush(message.getDestination().get()));
|
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()));
|
return getGroupRecipient(message != null ? message.getGroupContext() : Optional.empty()).orElseGet(() -> Recipient.externalPush(content.getSender()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Recipient> getGroupRecipient(Optional<SignalServiceGroupContext> message)
|
private Optional<Recipient> getGroupRecipient(Optional<SignalServiceGroupV2> message) {
|
||||||
throws BadGroupIdException
|
|
||||||
{
|
|
||||||
if (message.isPresent()) {
|
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) {
|
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient senderRecipient, @NonNull Recipient conversationRecipient, int device) {
|
||||||
|
@ -3053,15 +2997,6 @@ public final class MessageContentProcessor {
|
||||||
GroupDatabase groupDatabase = SignalDatabase.groups();
|
GroupDatabase groupDatabase = SignalDatabase.groups();
|
||||||
Optional<GroupId> groupId = GroupUtil.idFromGroupContext(message.getGroupContext());
|
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())) {
|
if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) {
|
||||||
return sender.isBlocked();
|
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 isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getSticker().isPresent();
|
||||||
boolean isExpireMessage = message.isExpirationUpdate();
|
boolean isExpireMessage = message.isExpirationUpdate();
|
||||||
boolean isGv2Update = message.isGroupV2Update();
|
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 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 {
|
} else {
|
||||||
return sender.isBlocked();
|
return sender.isBlocked();
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,15 +207,7 @@ public final class MessageDecryptionUtil {
|
||||||
|
|
||||||
if (sender == null) throw new NoSenderException();
|
if (sender == null) throw new NoSenderException();
|
||||||
|
|
||||||
GroupId groupId = null;
|
GroupId groupId = e.getGroup().isPresent() ? GroupId.v2(e.getGroup().get().getMasterKey()) : 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ExceptionMetadata(sender, e.getSenderDevice(), groupId);
|
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.groups.GroupId
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview
|
import org.thoughtcrime.securesms.linkpreview.LinkPreview
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
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.SignalServiceAttachment
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
|
||||||
class IncomingMediaMessage(
|
class IncomingMediaMessage(
|
||||||
|
@ -96,7 +95,7 @@ class IncomingMediaMessage(
|
||||||
viewOnce: Boolean,
|
viewOnce: Boolean,
|
||||||
unidentified: Boolean,
|
unidentified: Boolean,
|
||||||
body: Optional<String>,
|
body: Optional<String>,
|
||||||
group: Optional<SignalServiceGroupContext>,
|
group: Optional<SignalServiceGroupV2>,
|
||||||
attachments: Optional<List<SignalServiceAttachment>>,
|
attachments: Optional<List<SignalServiceAttachment>>,
|
||||||
quote: Optional<QuoteModel>,
|
quote: Optional<QuoteModel>,
|
||||||
sharedContacts: Optional<List<Contact>>,
|
sharedContacts: Optional<List<Contact>>,
|
||||||
|
@ -107,7 +106,7 @@ class IncomingMediaMessage(
|
||||||
giftBadge: GiftBadge?
|
giftBadge: GiftBadge?
|
||||||
) : this(
|
) : this(
|
||||||
from = from,
|
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),
|
body = body.orElse(null),
|
||||||
isPushMessage = true,
|
isPushMessage = true,
|
||||||
storyType = storyType,
|
storyType = storyType,
|
||||||
|
|
|
@ -6,31 +6,23 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
|
|
||||||
import org.signal.core.util.StringUtil;
|
import org.signal.core.util.StringUtil;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.signal.libsignal.protocol.InvalidMessageException;
|
|
||||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||||
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
|
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||||
import org.thoughtcrime.securesms.groups.BadGroupIdException;
|
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.mms.MessageGroupContext;
|
import org.thoughtcrime.securesms.mms.MessageGroupContext;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
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.
|
* @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) {
|
if (content == null) {
|
||||||
return null;
|
return null;
|
||||||
} else if (content.getDataMessage().isPresent() && content.getDataMessage().get().getGroupContext().isPresent()) {
|
} 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();
|
return content.getSyncMessage().get().getSent().get().getDataMessage().get().getGroupContext().get();
|
||||||
} else if (content.getStoryMessage().isPresent() && content.getStoryMessage().get().getGroupContext().isPresent()) {
|
} else if (content.getStoryMessage().isPresent() && content.getStoryMessage().get().getGroupContext().isPresent()) {
|
||||||
try {
|
return content.getStoryMessage().get().getGroupContext().get();
|
||||||
return SignalServiceGroupContext.create(null, content.getStoryMessage().get().getGroupContext().get());
|
|
||||||
} catch (InvalidMessageException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -69,36 +57,12 @@ public final class GroupUtil {
|
||||||
/**
|
/**
|
||||||
* Result may be a v1 or v2 GroupId.
|
* Result may be a v1 or v2 GroupId.
|
||||||
*/
|
*/
|
||||||
public static @NonNull GroupId idFromGroupContext(@NonNull SignalServiceGroupContext groupContext)
|
public static @NonNull Optional<GroupId> idFromGroupContext(@NonNull Optional<SignalServiceGroupV2> 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
|
|
||||||
{
|
|
||||||
if (groupContext.isPresent()) {
|
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) {
|
public static @NonNull GroupMasterKey requireMasterKey(@NonNull byte[] masterKey) {
|
||||||
|
@ -129,36 +93,14 @@ public final class GroupUtil {
|
||||||
@NonNull GroupId.Push groupId)
|
@NonNull GroupId.Push groupId)
|
||||||
{
|
{
|
||||||
if (groupId.isV2()) {
|
if (groupId.isV2()) {
|
||||||
GroupDatabase groupDatabase = SignalDatabase.groups();
|
GroupDatabase groupDatabase = SignalDatabase.groups();
|
||||||
GroupDatabase.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
|
GroupDatabase.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
|
||||||
GroupDatabase.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
|
GroupDatabase.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
|
||||||
SignalServiceGroupV2 group = SignalServiceGroupV2.newBuilder(v2GroupProperties.getGroupMasterKey())
|
SignalServiceGroupV2 group = SignalServiceGroupV2.newBuilder(v2GroupProperties.getGroupMasterKey())
|
||||||
.withRevision(v2GroupProperties.getGroupRevision())
|
.withRevision(v2GroupProperties.getGroupRevision())
|
||||||
.build();
|
.build();
|
||||||
dataMessageBuilder.asGroupMessage(group);
|
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GroupDescription {
|
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.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData;
|
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData;
|
||||||
import org.thoughtcrime.securesms.jobs.FailingJob;
|
import org.thoughtcrime.securesms.jobs.FailingJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
|
||||||
|
@ -29,35 +28,6 @@ public class RecipientIdFollowUpJobMigrationTest {
|
||||||
@Mock
|
@Mock
|
||||||
private MockedStatic<Job.Parameters> jobParametersMockedStatic;
|
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
|
@Test
|
||||||
public void migrate_sendDeliveryReceiptJob_good() throws Exception {
|
public void migrate_sendDeliveryReceiptJob_good() throws Exception {
|
||||||
JobData testData = new JobData("SendDeliveryReceiptJob", null, new Data.Builder().putString("recipient", "1")
|
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.MultiDeviceVerifiedUpdateJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
||||||
|
@ -83,24 +81,6 @@ public class RecipientIdJobMigrationTest {
|
||||||
new MultiDeviceViewOnceOpenJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
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
|
@Test
|
||||||
public void migrate_sendDeliveryReceiptJob() throws Exception {
|
public void migrate_sendDeliveryReceiptJob() throws Exception {
|
||||||
JobData testData = new JobData("SendDeliveryReceiptJob", null, new Data.Builder().putString("address", "+16101234567")
|
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());
|
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
|
@Test
|
||||||
public void migrate_directoryRefreshJob_null() throws Exception {
|
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());
|
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.SignalServiceAttachmentRemoteId;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServicePreview;
|
import org.whispersystems.signalservice.api.messages.SignalServicePreview;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
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.CallMessage;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content;
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage;
|
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.GroupContextV2;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage;
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Preview;
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Preview;
|
||||||
|
@ -834,14 +831,7 @@ public class SignalServiceMessageSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.getGroupContext().isPresent()) {
|
if (message.getGroupContext().isPresent()) {
|
||||||
SignalServiceGroupContext groupContext = message.getGroupContext().get();
|
builder.setGroupV2(createGroupContent(message.getGroupContext().get()));
|
||||||
if (groupContext.getGroupV1().isPresent()) {
|
|
||||||
builder.setGroup(createGroupContent(groupContext.getGroupV1().get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (groupContext.getGroupV2().isPresent()) {
|
|
||||||
builder.setGroupV2(createGroupContent(groupContext.getGroupV2().get()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isEndSession()) {
|
if (message.isEndSession()) {
|
||||||
|
@ -1473,47 +1463,6 @@ public class SignalServiceMessageSender {
|
||||||
return builder;
|
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) {
|
private static GroupContextV2 createGroupContent(SignalServiceGroupV2 group) {
|
||||||
GroupContextV2.Builder builder = GroupContextV2.newBuilder()
|
GroupContextV2.Builder builder = GroupContextV2.newBuilder()
|
||||||
.setMasterKey(ByteString.copyFrom(group.getMasterKey().serialize()))
|
.setMasterKey(ByteString.copyFrom(group.getMasterKey().serialize()))
|
||||||
|
|
|
@ -594,15 +594,8 @@ public final class SignalServiceContent {
|
||||||
SignalServiceProtos.DataMessage content)
|
SignalServiceProtos.DataMessage content)
|
||||||
throws UnsupportedDataMessageException, InvalidMessageStructureException
|
throws UnsupportedDataMessageException, InvalidMessageStructureException
|
||||||
{
|
{
|
||||||
SignalServiceGroupV2 groupInfoV2 = createGroupV2Info(content);
|
SignalServiceGroupV2 groupInfoV2 = createGroupV2Info(content);
|
||||||
Optional<SignalServiceGroupContext> groupContext;
|
Optional<SignalServiceGroupV2> groupContext = Optional.ofNullable(groupInfoV2);
|
||||||
|
|
||||||
try {
|
|
||||||
groupContext = SignalServiceGroupContext.createOptional(null, groupInfoV2);
|
|
||||||
} catch (InvalidMessageException e) {
|
|
||||||
throw new InvalidMessageStructureException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
List<SignalServiceAttachment> attachments = new LinkedList<>();
|
List<SignalServiceAttachment> attachments = new LinkedList<>();
|
||||||
boolean endSession = ((content.getFlags() & SignalServiceProtos.DataMessage.Flags.END_SESSION_VALUE ) != 0);
|
boolean endSession = ((content.getFlags() & SignalServiceProtos.DataMessage.Flags.END_SESSION_VALUE ) != 0);
|
||||||
|
@ -649,7 +642,6 @@ public final class SignalServiceContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SignalServiceDataMessage(metadata.getTimestamp(),
|
return new SignalServiceDataMessage(metadata.getTimestamp(),
|
||||||
null,
|
|
||||||
groupInfoV2,
|
groupInfoV2,
|
||||||
attachments,
|
attachments,
|
||||||
content.hasBody() ? content.getBody() : null,
|
content.hasBody() ? content.getBody() : null,
|
||||||
|
|
|
@ -27,7 +27,7 @@ public class SignalServiceDataMessage {
|
||||||
private final long timestamp;
|
private final long timestamp;
|
||||||
private final Optional<List<SignalServiceAttachment>> attachments;
|
private final Optional<List<SignalServiceAttachment>> attachments;
|
||||||
private final Optional<String> body;
|
private final Optional<String> body;
|
||||||
private final Optional<SignalServiceGroupContext> group;
|
private final Optional<SignalServiceGroupV2> group;
|
||||||
private final Optional<byte[]> profileKey;
|
private final Optional<byte[]> profileKey;
|
||||||
private final boolean endSession;
|
private final boolean endSession;
|
||||||
private final boolean expirationUpdate;
|
private final boolean expirationUpdate;
|
||||||
|
@ -50,7 +50,6 @@ public class SignalServiceDataMessage {
|
||||||
* Construct a SignalServiceDataMessage.
|
* Construct a SignalServiceDataMessage.
|
||||||
*
|
*
|
||||||
* @param timestamp The sent timestamp.
|
* @param timestamp The sent timestamp.
|
||||||
* @param group The group information (or null if none).
|
|
||||||
* @param groupV2 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 attachments The attachments (or null if none).
|
||||||
* @param body The message contents.
|
* @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.
|
* @param expiresInSeconds Number of seconds in which the message should disappear after being seen.
|
||||||
*/
|
*/
|
||||||
SignalServiceDataMessage(long timestamp,
|
SignalServiceDataMessage(long timestamp,
|
||||||
SignalServiceGroup group,
|
|
||||||
SignalServiceGroupV2 groupV2,
|
SignalServiceGroupV2 groupV2,
|
||||||
List<SignalServiceAttachment> attachments,
|
List<SignalServiceAttachment> attachments,
|
||||||
String body,
|
String body,
|
||||||
|
@ -80,12 +78,7 @@ public class SignalServiceDataMessage {
|
||||||
StoryContext storyContext,
|
StoryContext storyContext,
|
||||||
GiftBadge giftBadge)
|
GiftBadge giftBadge)
|
||||||
{
|
{
|
||||||
try {
|
this.group = Optional.ofNullable(groupV2);
|
||||||
this.group = SignalServiceGroupContext.createOptional(group, groupV2);
|
|
||||||
} catch (InvalidMessageException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.body = OptionalUtil.absentIfEmpty(body);
|
this.body = OptionalUtil.absentIfEmpty(body);
|
||||||
this.endSession = endSession;
|
this.endSession = endSession;
|
||||||
|
@ -156,7 +149,7 @@ public class SignalServiceDataMessage {
|
||||||
/**
|
/**
|
||||||
* @return The message group context (if any).
|
* @return The message group context (if any).
|
||||||
*/
|
*/
|
||||||
public Optional<SignalServiceGroupContext> getGroupContext() {
|
public Optional<SignalServiceGroupV2> getGroupContext() {
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,20 +165,13 @@ public class SignalServiceDataMessage {
|
||||||
return profileKeyUpdate;
|
return profileKeyUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGroupV1Update() {
|
|
||||||
return group.isPresent() &&
|
|
||||||
group.get().getGroupV1().isPresent() &&
|
|
||||||
group.get().getGroupV1().get().getType() != SignalServiceGroup.Type.DELIVER;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGroupV2Message() {
|
public boolean isGroupV2Message() {
|
||||||
return group.isPresent() &&
|
return group.isPresent();
|
||||||
group.get().getGroupV2().isPresent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGroupV2Update() {
|
public boolean isGroupV2Update() {
|
||||||
return isGroupV2Message() &&
|
return group.isPresent() &&
|
||||||
group.get().getGroupV2().get().hasSignedGroupChange() &&
|
group.get().hasSignedGroupChange() &&
|
||||||
!hasRenderableContent();
|
!hasRenderableContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,8 +251,8 @@ public class SignalServiceDataMessage {
|
||||||
public Optional<byte[]> getGroupId() {
|
public Optional<byte[]> getGroupId() {
|
||||||
byte[] groupId = null;
|
byte[] groupId = null;
|
||||||
|
|
||||||
if (getGroupContext().isPresent() && getGroupContext().get().getGroupV2().isPresent()) {
|
if (getGroupContext().isPresent() && getGroupContext().isPresent()) {
|
||||||
SignalServiceGroupV2 gv2 = getGroupContext().get().getGroupV2().get();
|
SignalServiceGroupV2 gv2 = getGroupContext().get();
|
||||||
groupId = GroupSecretParams.deriveFromMasterKey(gv2.getMasterKey())
|
groupId = GroupSecretParams.deriveFromMasterKey(gv2.getMasterKey())
|
||||||
.getPublicParams()
|
.getPublicParams()
|
||||||
.getGroupIdentifier()
|
.getGroupIdentifier()
|
||||||
|
@ -284,7 +270,6 @@ public class SignalServiceDataMessage {
|
||||||
private List<Mention> mentions = new LinkedList<>();
|
private List<Mention> mentions = new LinkedList<>();
|
||||||
|
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
private SignalServiceGroup group;
|
|
||||||
private SignalServiceGroupV2 groupV2;
|
private SignalServiceGroupV2 groupV2;
|
||||||
private String body;
|
private String body;
|
||||||
private boolean endSession;
|
private boolean endSession;
|
||||||
|
@ -309,18 +294,7 @@ public class SignalServiceDataMessage {
|
||||||
return this;
|
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) {
|
public Builder asGroupMessage(SignalServiceGroupV2 group) {
|
||||||
if (this.group != null) {
|
|
||||||
throw new AssertionError("Can not contain both V1 and V2 group contexts.");
|
|
||||||
}
|
|
||||||
this.groupV2 = group;
|
this.groupV2 = group;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -440,7 +414,7 @@ public class SignalServiceDataMessage {
|
||||||
|
|
||||||
public SignalServiceDataMessage build() {
|
public SignalServiceDataMessage build() {
|
||||||
if (timestamp == 0) timestamp = System.currentTimeMillis();
|
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,
|
expiresInSeconds, expirationUpdate, profileKey,
|
||||||
profileKeyUpdate, quote, sharedContacts, previews,
|
profileKeyUpdate, quote, sharedContacts, previews,
|
||||||
mentions, sticker, viewOnce, reaction, remoteDelete,
|
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;
|
package org.whispersystems.signalservice.internal.push;
|
||||||
|
|
||||||
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -12,14 +12,14 @@ import java.util.Optional;
|
||||||
*/
|
*/
|
||||||
public abstract class UnsupportedDataMessageException extends Exception {
|
public abstract class UnsupportedDataMessageException extends Exception {
|
||||||
|
|
||||||
private final String sender;
|
private final String sender;
|
||||||
private final int senderDevice;
|
private final int senderDevice;
|
||||||
private final Optional<SignalServiceGroupContext> group;
|
private final Optional<SignalServiceGroupV2> group;
|
||||||
|
|
||||||
protected UnsupportedDataMessageException(String message,
|
protected UnsupportedDataMessageException(String message,
|
||||||
String sender,
|
String sender,
|
||||||
int senderDevice,
|
int senderDevice,
|
||||||
Optional<SignalServiceGroupContext> group)
|
Optional<SignalServiceGroupV2> group)
|
||||||
{
|
{
|
||||||
super(message);
|
super(message);
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
|
@ -35,7 +35,7 @@ public abstract class UnsupportedDataMessageException extends Exception {
|
||||||
return senderDevice;
|
return senderDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<SignalServiceGroupContext> getGroup() {
|
public Optional<SignalServiceGroupV2> getGroup() {
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.whispersystems.signalservice.internal.push;
|
package org.whispersystems.signalservice.internal.push;
|
||||||
|
|
||||||
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ public final class UnsupportedDataMessageProtocolVersionException extends Unsupp
|
||||||
int requiredVersion,
|
int requiredVersion,
|
||||||
String sender,
|
String sender,
|
||||||
int senderDevice,
|
int senderDevice,
|
||||||
Optional<SignalServiceGroupContext> group) {
|
Optional<SignalServiceGroupV2> group) {
|
||||||
super("Required version: " + requiredVersion + ", Our version: " + currentVersion, sender, senderDevice, group);
|
super("Required version: " + requiredVersion + ", Our version: " + currentVersion, sender, senderDevice, group);
|
||||||
this.requiredVersion = requiredVersion;
|
this.requiredVersion = requiredVersion;
|
||||||
}
|
}
|
||||||
|
|
|
@ -313,7 +313,7 @@ message DataMessage {
|
||||||
|
|
||||||
optional string body = 1;
|
optional string body = 1;
|
||||||
repeated AttachmentPointer attachments = 2;
|
repeated AttachmentPointer attachments = 2;
|
||||||
optional GroupContext group = 3;
|
reserved /*groupV1*/ 3;
|
||||||
optional GroupContextV2 groupV2 = 15;
|
optional GroupContextV2 groupV2 = 15;
|
||||||
optional uint32 flags = 4;
|
optional uint32 flags = 4;
|
||||||
optional uint32 expireTimer = 5;
|
optional uint32 expireTimer = 5;
|
||||||
|
|
Loading…
Add table
Reference in a new issue