mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-19 19:48:27 +00:00
clean up old groups
This commit is contained in:
parent
5db7f0ecb8
commit
dee7d78acb
@ -47,41 +47,6 @@ public class GroupManager {
|
||||
return DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
|
||||
}
|
||||
|
||||
public static @NonNull GroupActionResult createGroup(@NonNull Context context,
|
||||
@NonNull Set<Recipient> members,
|
||||
@Nullable Bitmap avatar,
|
||||
@Nullable String name,
|
||||
@NonNull Set<Recipient> admins)
|
||||
{
|
||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||
String id = GroupUtil.getEncodedClosedGroupID(database.allocateGroupId()); // TODO: The group id is double encoded here 1
|
||||
return createGroup(id, context, members, avatar, name, admins);
|
||||
}
|
||||
|
||||
public static @NonNull GroupActionResult createGroup(@NonNull String id,
|
||||
@NonNull Context context,
|
||||
@NonNull Set<Recipient> members,
|
||||
@Nullable Bitmap avatar,
|
||||
@Nullable String name,
|
||||
@NonNull Set<Recipient> admins)
|
||||
{
|
||||
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
|
||||
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
final String groupId = GroupUtil.getEncodedClosedGroupID(id.getBytes()); // TODO: The group id is double encoded here 2
|
||||
final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
|
||||
final Set<Address> memberAddresses = getMemberAddresses(members);
|
||||
final Set<Address> adminAddresses = getMemberAddresses(admins);
|
||||
|
||||
String userPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||
|
||||
memberAddresses.add(Address.fromSerialized(userPublicKey));
|
||||
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses), System.currentTimeMillis());
|
||||
|
||||
groupDatabase.updateProfilePicture(groupId, avatarBytes);
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true);
|
||||
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
|
||||
}
|
||||
|
||||
public static @NonNull GroupActionResult createOpenGroup(@NonNull String id,
|
||||
@NonNull Context context,
|
||||
@Nullable Bitmap avatar,
|
||||
|
@ -1,318 +0,0 @@
|
||||
package org.thoughtcrime.securesms.groups;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
|
||||
import org.session.libsession.messaging.threads.Address;
|
||||
import org.session.libsession.messaging.threads.GroupRecord;
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||
import org.session.libsignal.utilities.Base64;
|
||||
import org.session.libsession.utilities.GroupUtil;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceContent;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceGroup;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceGroup.Type;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer;
|
||||
import static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext;
|
||||
|
||||
public class GroupMessageProcessor {
|
||||
|
||||
private static final String TAG = GroupMessageProcessor.class.getSimpleName();
|
||||
|
||||
public static @Nullable Long process(@NonNull Context context,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
boolean outgoing)
|
||||
{
|
||||
if (!message.getGroupInfo().isPresent() || message.getGroupInfo().get().getGroupId() == null) {
|
||||
Log.w(TAG, "Received group message with no id! Ignoring...");
|
||||
return null;
|
||||
}
|
||||
|
||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||
SignalServiceGroup group = message.getGroupInfo().get();
|
||||
String id = GroupUtil.getEncodedId(group);
|
||||
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, message.getTimestamp());
|
||||
} 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, group, 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,
|
||||
Long formationTimestamp)
|
||||
{
|
||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||
String id = GroupUtil.getEncodedId(group);
|
||||
GroupContext.Builder builder = createGroupContext(group);
|
||||
builder.setType(GroupContext.Type.UPDATE);
|
||||
|
||||
SignalServiceAttachment avatar = group.getAvatar().orNull();
|
||||
List<Address> members = group.getMembers().isPresent() ? new LinkedList<>() : null;
|
||||
List<Address> admins = group.getAdmins().isPresent() ? new LinkedList<>() : null;
|
||||
|
||||
if (group.getMembers().isPresent()) {
|
||||
for (String member : group.getMembers().get()) {
|
||||
members.add(Address.fromExternal(context, member));
|
||||
}
|
||||
}
|
||||
|
||||
// Loki - Parse admins
|
||||
if (group.getAdmins().isPresent()) {
|
||||
for (String admin : group.getAdmins().get()) {
|
||||
admins.add(Address.fromExternal(context, admin));
|
||||
}
|
||||
}
|
||||
|
||||
database.create(id, group.getName().orNull(), members,
|
||||
avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins, formationTimestamp);
|
||||
|
||||
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 = DatabaseFactory.getGroupDatabase(context);
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
String id = GroupUtil.getEncodedId(group);
|
||||
Address address = Address.fromExternal(context, GroupUtil.getEncodedId(group));
|
||||
Recipient recipient = Recipient.from(context, address, false);
|
||||
|
||||
String userPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||
|
||||
if (content.getSender().equals(userPublicKey)) {
|
||||
long threadId = threadDatabase.getThreadIdIfExistsFor(recipient);
|
||||
return threadId == -1 ? null : threadId;
|
||||
}
|
||||
|
||||
if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) {
|
||||
// Loki - Only update the group if the group admin sent the message
|
||||
if (!groupRecord.getAdmins().contains(Address.fromSerialized(content.getSender()))) {
|
||||
Log.d("Loki", "Received a group update message from a non-admin user for: " + id +"; ignoring.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Loki - Only process update messages if we're part of the group
|
||||
Address userMasterDeviceAddress = Address.fromSerialized(userPublicKey);
|
||||
if (!groupRecord.getMembers().contains(userMasterDeviceAddress) &&
|
||||
!group.getMembers().or(Collections.emptyList()).contains(userPublicKey)) {
|
||||
Log.d("Loki", "Received a group update message from a group we're not a member of: " + id + "; ignoring.");
|
||||
database.setActive(id, false);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Set<Address> currentMembers = new HashSet<>(groupRecord.getMembers());
|
||||
Set<Address> newMembers = new HashSet<>();
|
||||
|
||||
for (String messageMember : group.getMembers().get()) {
|
||||
newMembers.add(Address.fromExternal(context, messageMember));
|
||||
}
|
||||
|
||||
// Added members are the members who are present in newMembers but not in currentMembers
|
||||
Set<Address> addedMembers = new HashSet<>(newMembers);
|
||||
addedMembers.removeAll(currentMembers);
|
||||
|
||||
// Kicked members are members who are present in currentMembers but not in newMembers
|
||||
Set<Address> removedMembers = new HashSet<>(currentMembers);
|
||||
removedMembers.removeAll(newMembers);
|
||||
|
||||
GroupContext.Builder builder = createGroupContext(group);
|
||||
builder.setType(GroupContext.Type.UPDATE);
|
||||
|
||||
// Update our group members if they're different
|
||||
if (!currentMembers.equals(newMembers)) {
|
||||
database.updateMembers(id, new LinkedList<>(newMembers));
|
||||
}
|
||||
|
||||
// Add any new or removed members to the group context.
|
||||
// This will allow us later to iterate over them to check if they left or were added for UI purposes.
|
||||
for (Address addedMember : addedMembers) {
|
||||
builder.addNewMembers(addedMember.serialize());
|
||||
}
|
||||
|
||||
for (Address removedMember : removedMembers) {
|
||||
builder.addRemovedMembers(removedMember.serialize());
|
||||
}
|
||||
|
||||
if (group.getName().isPresent() || group.getAvatar().isPresent()) {
|
||||
SignalServiceAttachment avatar = group.getAvatar().orNull();
|
||||
database.update(id, group.getName().orNull(), avatar != null ? avatar.asPointer() : null);
|
||||
}
|
||||
|
||||
if (group.getName().isPresent() && group.getName().get().equals(groupRecord.getTitle())) {
|
||||
builder.clearName();
|
||||
}
|
||||
|
||||
// If we were removed then we need to disable the chat
|
||||
if (removedMembers.contains(Address.fromSerialized(userPublicKey))) {
|
||||
database.setActive(id, false);
|
||||
} else {
|
||||
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 SignalServiceGroup group,
|
||||
@NonNull GroupRecord record)
|
||||
{
|
||||
if (record.getMembers().contains(Address.fromSerialized(content.getSender()))) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new PushGroupUpdateJob(content.getSender(), group.getGroupId()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Long handleGroupLeave(@NonNull Context context,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
@NonNull GroupRecord record,
|
||||
boolean outgoing)
|
||||
{
|
||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||
String id = GroupUtil.getEncodedId(group);
|
||||
List<Address> members = record.getMembers();
|
||||
|
||||
GroupContext.Builder builder = createGroupContext(group);
|
||||
builder.setType(GroupContext.Type.QUIT);
|
||||
|
||||
if (members.contains(Address.fromExternal(context, content.getSender()))) {
|
||||
database.removeMember(id, Address.fromExternal(context, content.getSender()));
|
||||
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()) {
|
||||
ApplicationContext.getInstance(context).getJobManager()
|
||||
.add(new AvatarDownloadJob(GroupUtil.getEncodedId(group)));
|
||||
}
|
||||
|
||||
try {
|
||||
if (outgoing) {
|
||||
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
|
||||
Address address = Address.fromExternal(context, GroupUtil.getEncodedId(group));
|
||||
Recipient recipient = Recipient.from(context, address, false);
|
||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, 0, null, Collections.emptyList(), Collections.emptyList());
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
Address senderAddress = Address.fromExternal(context, content.getSender());
|
||||
MessageRecord existingMessage = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(content.getTimestamp(), senderAddress);
|
||||
long messageId;
|
||||
if (existingMessage != null) {
|
||||
messageId = existingMessage.getId();
|
||||
} else {
|
||||
messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);
|
||||
}
|
||||
|
||||
mmsDatabase.markAsSent(messageId, true);
|
||||
|
||||
return threadId;
|
||||
} else {
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
String body = Base64.encodeBytes(storage.toByteArray());
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, content.getSender()), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt());
|
||||
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
|
||||
|
||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, 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()) {
|
||||
builder.setAvatar(AttachmentPointer.newBuilder()
|
||||
.setId(group.getAvatar().get().asPointer().getId())
|
||||
.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.addAllMembers(group.getMembers().get());
|
||||
}
|
||||
|
||||
if (group.getAdmins().isPresent()) {
|
||||
builder.addAllAdmins(group.getAdmins().get());
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
@ -49,7 +49,6 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.linkpreview.Link;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||
@ -229,9 +228,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
if (message.getClosedGroupControlMessage().isPresent()) {
|
||||
ClosedGroupsProtocolV2.handleMessage(context, message.getClosedGroupControlMessage().get(), message.getTimestamp(), envelope.getSource(), content.getSender());
|
||||
}
|
||||
if (message.isGroupUpdate()) {
|
||||
handleGroupMessage(content, message, smsMessageId);
|
||||
} else if (message.isExpirationUpdate()) {
|
||||
if (message.isExpirationUpdate()) {
|
||||
handleExpirationUpdate(content, message, smsMessageId);
|
||||
} else if (isMediaMessage) {
|
||||
handleMediaMessage(content, message, smsMessageId, Optional.absent());
|
||||
@ -279,50 +276,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEndSessionMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromSerialized(content.getSender()),
|
||||
content.getSenderDevice(),
|
||||
content.getTimestamp(),
|
||||
"", Optional.absent(), 0,
|
||||
content.isNeedsReceipt());
|
||||
|
||||
Long threadId;
|
||||
|
||||
if (!smsMessageId.isPresent()) {
|
||||
IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage);
|
||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(incomingEndSessionMessage);
|
||||
|
||||
if (insertResult.isPresent()) threadId = insertResult.get().getThreadId();
|
||||
else threadId = null;
|
||||
} else {
|
||||
smsDatabase.markAsEndSession(smsMessageId.get());
|
||||
threadId = smsDatabase.getThreadIdForMessage(smsMessageId.get());
|
||||
}
|
||||
|
||||
if (threadId != null) {
|
||||
messageNotifier.updateNotification(context, threadId);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGroupMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
throws StorageFailedException
|
||||
{
|
||||
GroupMessageProcessor.process(context, content, message, false);
|
||||
|
||||
if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getMessageDestination(content, message).getExpireMessages()) {
|
||||
handleExpirationUpdate(content, message, Optional.absent());
|
||||
}
|
||||
|
||||
if (smsMessageId.isPresent()) {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUnknownGroupMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user