From 5c9ddef19bef09e7a5eeb5f7b7973f9e2d070be6 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Wed, 21 Apr 2021 14:45:42 +1000 Subject: [PATCH 1/9] clean proto --- libsignal/protobuf/SignalService.proto | 6 - .../internal/push/SignalServiceProtos.java | 513 +----------------- 2 files changed, 20 insertions(+), 499 deletions(-) diff --git a/libsignal/protobuf/SignalService.proto b/libsignal/protobuf/SignalService.proto index 614b5e65f..17379e82f 100644 --- a/libsignal/protobuf/SignalService.proto +++ b/libsignal/protobuf/SignalService.proto @@ -304,12 +304,6 @@ message GroupContext { repeated string members = 4; optional AttachmentPointer avatar = 5; repeated string admins = 6; - - // Loki - These fields are only used internally for the Android code base. - // This is so that we can differentiate adding/kicking. - // DO NOT USE WHEN SENDING MESSAGES. - repeated string newMembers = 998; - repeated string removedMembers = 999; } message ContactDetails { diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceProtos.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceProtos.java index 0e4feba1e..bd6b30747 100644 --- a/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceProtos.java +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceProtos.java @@ -26967,70 +26967,6 @@ public final class SignalServiceProtos { */ com.google.protobuf.ByteString getAdminsBytes(int index); - - // repeated string newMembers = 998; - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - java.util.List - getNewMembersList(); - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - int getNewMembersCount(); - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - java.lang.String getNewMembers(int index); - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - com.google.protobuf.ByteString - getNewMembersBytes(int index); - - // repeated string removedMembers = 999; - /** - * repeated string removedMembers = 999; - */ - java.util.List - getRemovedMembersList(); - /** - * repeated string removedMembers = 999; - */ - int getRemovedMembersCount(); - /** - * repeated string removedMembers = 999; - */ - java.lang.String getRemovedMembers(int index); - /** - * repeated string removedMembers = 999; - */ - com.google.protobuf.ByteString - getRemovedMembersBytes(int index); } /** * Protobuf type {@code signalservice.GroupContext} @@ -27133,22 +27069,6 @@ public final class SignalServiceProtos { admins_.add(input.readBytes()); break; } - case 7986: { - if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) { - newMembers_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000040; - } - newMembers_.add(input.readBytes()); - break; - } - case 7994: { - if (!((mutable_bitField0_ & 0x00000080) == 0x00000080)) { - removedMembers_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000080; - } - removedMembers_.add(input.readBytes()); - break; - } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { @@ -27163,12 +27083,6 @@ public final class SignalServiceProtos { if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) { admins_ = new com.google.protobuf.UnmodifiableLazyStringList(admins_); } - if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) { - newMembers_ = new com.google.protobuf.UnmodifiableLazyStringList(newMembers_); - } - if (((mutable_bitField0_ & 0x00000080) == 0x00000080)) { - removedMembers_ = new com.google.protobuf.UnmodifiableLazyStringList(removedMembers_); - } this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } @@ -27483,90 +27397,6 @@ public final class SignalServiceProtos { return admins_.getByteString(index); } - // repeated string newMembers = 998; - public static final int NEWMEMBERS_FIELD_NUMBER = 998; - private com.google.protobuf.LazyStringList newMembers_; - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - public java.util.List - getNewMembersList() { - return newMembers_; - } - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - public int getNewMembersCount() { - return newMembers_.size(); - } - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - public java.lang.String getNewMembers(int index) { - return newMembers_.get(index); - } - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - public com.google.protobuf.ByteString - getNewMembersBytes(int index) { - return newMembers_.getByteString(index); - } - - // repeated string removedMembers = 999; - public static final int REMOVEDMEMBERS_FIELD_NUMBER = 999; - private com.google.protobuf.LazyStringList removedMembers_; - /** - * repeated string removedMembers = 999; - */ - public java.util.List - getRemovedMembersList() { - return removedMembers_; - } - /** - * repeated string removedMembers = 999; - */ - public int getRemovedMembersCount() { - return removedMembers_.size(); - } - /** - * repeated string removedMembers = 999; - */ - public java.lang.String getRemovedMembers(int index) { - return removedMembers_.get(index); - } - /** - * repeated string removedMembers = 999; - */ - public com.google.protobuf.ByteString - getRemovedMembersBytes(int index) { - return removedMembers_.getByteString(index); - } - private void initFields() { id_ = com.google.protobuf.ByteString.EMPTY; type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type.UNKNOWN; @@ -27574,8 +27404,6 @@ public final class SignalServiceProtos { members_ = com.google.protobuf.LazyStringArrayList.EMPTY; avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; - newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -27607,12 +27435,6 @@ public final class SignalServiceProtos { for (int i = 0; i < admins_.size(); i++) { output.writeBytes(6, admins_.getByteString(i)); } - for (int i = 0; i < newMembers_.size(); i++) { - output.writeBytes(998, newMembers_.getByteString(i)); - } - for (int i = 0; i < removedMembers_.size(); i++) { - output.writeBytes(999, removedMembers_.getByteString(i)); - } getUnknownFields().writeTo(output); } @@ -27656,24 +27478,6 @@ public final class SignalServiceProtos { size += dataSize; size += 1 * getAdminsList().size(); } - { - int dataSize = 0; - for (int i = 0; i < newMembers_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(newMembers_.getByteString(i)); - } - size += dataSize; - size += 2 * getNewMembersList().size(); - } - { - int dataSize = 0; - for (int i = 0; i < removedMembers_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(removedMembers_.getByteString(i)); - } - size += dataSize; - size += 2 * getRemovedMembersList().size(); - } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -27807,10 +27611,6 @@ public final class SignalServiceProtos { bitField0_ = (bitField0_ & ~0x00000010); admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; bitField0_ = (bitField0_ & ~0x00000020); - newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000040); - removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000080); return this; } @@ -27871,18 +27671,6 @@ public final class SignalServiceProtos { bitField0_ = (bitField0_ & ~0x00000020); } result.admins_ = admins_; - if (((bitField0_ & 0x00000040) == 0x00000040)) { - newMembers_ = new com.google.protobuf.UnmodifiableLazyStringList( - newMembers_); - bitField0_ = (bitField0_ & ~0x00000040); - } - result.newMembers_ = newMembers_; - if (((bitField0_ & 0x00000080) == 0x00000080)) { - removedMembers_ = new com.google.protobuf.UnmodifiableLazyStringList( - removedMembers_); - bitField0_ = (bitField0_ & ~0x00000080); - } - result.removedMembers_ = removedMembers_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -27933,26 +27721,6 @@ public final class SignalServiceProtos { } onChanged(); } - if (!other.newMembers_.isEmpty()) { - if (newMembers_.isEmpty()) { - newMembers_ = other.newMembers_; - bitField0_ = (bitField0_ & ~0x00000040); - } else { - ensureNewMembersIsMutable(); - newMembers_.addAll(other.newMembers_); - } - onChanged(); - } - if (!other.removedMembers_.isEmpty()) { - if (removedMembers_.isEmpty()) { - removedMembers_ = other.removedMembers_; - bitField0_ = (bitField0_ & ~0x00000080); - } else { - ensureRemovedMembersIsMutable(); - removedMembers_.addAll(other.removedMembers_); - } - onChanged(); - } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -28461,246 +28229,6 @@ public final class SignalServiceProtos { return this; } - // repeated string newMembers = 998; - private com.google.protobuf.LazyStringList newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureNewMembersIsMutable() { - if (!((bitField0_ & 0x00000040) == 0x00000040)) { - newMembers_ = new com.google.protobuf.LazyStringArrayList(newMembers_); - bitField0_ |= 0x00000040; - } - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public java.util.List - getNewMembersList() { - return java.util.Collections.unmodifiableList(newMembers_); - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public int getNewMembersCount() { - return newMembers_.size(); - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public java.lang.String getNewMembers(int index) { - return newMembers_.get(index); - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public com.google.protobuf.ByteString - getNewMembersBytes(int index) { - return newMembers_.getByteString(index); - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public Builder setNewMembers( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureNewMembersIsMutable(); - newMembers_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public Builder addNewMembers( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureNewMembersIsMutable(); - newMembers_.add(value); - onChanged(); - return this; - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public Builder addAllNewMembers( - java.lang.Iterable values) { - ensureNewMembersIsMutable(); - super.addAll(values, newMembers_); - onChanged(); - return this; - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public Builder clearNewMembers() { - newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000040); - onChanged(); - return this; - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public Builder addNewMembersBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureNewMembersIsMutable(); - newMembers_.add(value); - onChanged(); - return this; - } - - // repeated string removedMembers = 999; - private com.google.protobuf.LazyStringList removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureRemovedMembersIsMutable() { - if (!((bitField0_ & 0x00000080) == 0x00000080)) { - removedMembers_ = new com.google.protobuf.LazyStringArrayList(removedMembers_); - bitField0_ |= 0x00000080; - } - } - /** - * repeated string removedMembers = 999; - */ - public java.util.List - getRemovedMembersList() { - return java.util.Collections.unmodifiableList(removedMembers_); - } - /** - * repeated string removedMembers = 999; - */ - public int getRemovedMembersCount() { - return removedMembers_.size(); - } - /** - * repeated string removedMembers = 999; - */ - public java.lang.String getRemovedMembers(int index) { - return removedMembers_.get(index); - } - /** - * repeated string removedMembers = 999; - */ - public com.google.protobuf.ByteString - getRemovedMembersBytes(int index) { - return removedMembers_.getByteString(index); - } - /** - * repeated string removedMembers = 999; - */ - public Builder setRemovedMembers( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureRemovedMembersIsMutable(); - removedMembers_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string removedMembers = 999; - */ - public Builder addRemovedMembers( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureRemovedMembersIsMutable(); - removedMembers_.add(value); - onChanged(); - return this; - } - /** - * repeated string removedMembers = 999; - */ - public Builder addAllRemovedMembers( - java.lang.Iterable values) { - ensureRemovedMembersIsMutable(); - super.addAll(values, removedMembers_); - onChanged(); - return this; - } - /** - * repeated string removedMembers = 999; - */ - public Builder clearRemovedMembers() { - removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000080); - onChanged(); - return this; - } - /** - * repeated string removedMembers = 999; - */ - public Builder addRemovedMembersBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureRemovedMembersIsMutable(); - removedMembers_.add(value); - onChanged(); - return this; - } - // @@protoc_insertion_point(builder_scope:signalservice.GroupContext) } @@ -33464,29 +32992,28 @@ public final class SignalServiceProtos { "\020\n\010fileName\030\007 \001(\t\022\r\n\005flags\030\010 \001(\r\022\r\n\005widt", "h\030\t \001(\r\022\016\n\006height\030\n \001(\r\022\017\n\007caption\030\013 \001(\t" + "\022\013\n\003url\030e \001(\t\"\032\n\005Flags\022\021\n\rVOICE_MESSAGE\020" + - "\001\"\243\002\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022.\n\004type\030\002" + + "\001\"\365\001\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022.\n\004type\030\002" + " \001(\0162 .signalservice.GroupContext.Type\022\014" + "\n\004name\030\003 \001(\t\022\017\n\007members\030\004 \003(\t\0220\n\006avatar\030" + "\005 \001(\0132 .signalservice.AttachmentPointer\022" + - "\016\n\006admins\030\006 \003(\t\022\023\n\nnewMembers\030\346\007 \003(\t\022\027\n\016" + - "removedMembers\030\347\007 \003(\t\"H\n\004Type\022\013\n\007UNKNOWN" + - "\020\000\022\n\n\006UPDATE\020\001\022\013\n\007DELIVER\020\002\022\010\n\004QUIT\020\003\022\020\n" + - "\014REQUEST_INFO\020\004\"\356\001\n\016ContactDetails\022\016\n\006nu", - "mber\030\001 \001(\t\022\014\n\004name\030\002 \001(\t\0224\n\006avatar\030\003 \001(\013" + - "2$.signalservice.ContactDetails.Avatar\022\r" + - "\n\005color\030\004 \001(\t\022\022\n\nprofileKey\030\006 \001(\014\022\017\n\007blo" + - "cked\030\007 \001(\010\022\023\n\013expireTimer\030\010 \001(\r\022\020\n\010nickn" + - "ame\030e \001(\t\032-\n\006Avatar\022\023\n\013contentType\030\001 \001(\t" + - "\022\016\n\006length\030\002 \001(\r\"\367\001\n\014GroupDetails\022\n\n\002id\030" + - "\001 \001(\014\022\014\n\004name\030\002 \001(\t\022\017\n\007members\030\003 \003(\t\0222\n\006" + - "avatar\030\004 \001(\0132\".signalservice.GroupDetail" + - "s.Avatar\022\024\n\006active\030\005 \001(\010:\004true\022\023\n\013expire" + - "Timer\030\006 \001(\r\022\r\n\005color\030\007 \001(\t\022\017\n\007blocked\030\010 ", - "\001(\010\022\016\n\006admins\030\t \003(\t\032-\n\006Avatar\022\023\n\013content" + - "Type\030\001 \001(\t\022\016\n\006length\030\002 \001(\r\"\"\n\016PublicChat" + - "Info\022\020\n\010serverID\030\001 \001(\004BB\n+org.session.li" + - "bsignal.service.internal.pushB\023SignalSer" + - "viceProtos" + "\016\n\006admins\030\006 \003(\t\"H\n\004Type\022\013\n\007UNKNOWN\020\000\022\n\n\006" + + "UPDATE\020\001\022\013\n\007DELIVER\020\002\022\010\n\004QUIT\020\003\022\020\n\014REQUE" + + "ST_INFO\020\004\"\356\001\n\016ContactDetails\022\016\n\006number\030\001" + + " \001(\t\022\014\n\004name\030\002 \001(\t\0224\n\006avatar\030\003 \001(\0132$.sig", + "nalservice.ContactDetails.Avatar\022\r\n\005colo" + + "r\030\004 \001(\t\022\022\n\nprofileKey\030\006 \001(\014\022\017\n\007blocked\030\007" + + " \001(\010\022\023\n\013expireTimer\030\010 \001(\r\022\020\n\010nickname\030e " + + "\001(\t\032-\n\006Avatar\022\023\n\013contentType\030\001 \001(\t\022\016\n\006le" + + "ngth\030\002 \001(\r\"\367\001\n\014GroupDetails\022\n\n\002id\030\001 \001(\014\022" + + "\014\n\004name\030\002 \001(\t\022\017\n\007members\030\003 \003(\t\0222\n\006avatar" + + "\030\004 \001(\0132\".signalservice.GroupDetails.Avat" + + "ar\022\024\n\006active\030\005 \001(\010:\004true\022\023\n\013expireTimer\030" + + "\006 \001(\r\022\r\n\005color\030\007 \001(\t\022\017\n\007blocked\030\010 \001(\010\022\016\n" + + "\006admins\030\t \003(\t\032-\n\006Avatar\022\023\n\013contentType\030\001", + " \001(\t\022\016\n\006length\030\002 \001(\r\"\"\n\016PublicChatInfo\022\020" + + "\n\010serverID\030\001 \001(\004BB\n+org.session.libsigna" + + "l.service.internal.pushB\023SignalServicePr" + + "otos" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -33642,7 +33169,7 @@ public final class SignalServiceProtos { internal_static_signalservice_GroupContext_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_signalservice_GroupContext_descriptor, - new java.lang.String[] { "Id", "Type", "Name", "Members", "Avatar", "Admins", "NewMembers", "RemovedMembers", }); + new java.lang.String[] { "Id", "Type", "Name", "Members", "Avatar", "Admins", }); internal_static_signalservice_ContactDetails_descriptor = getDescriptor().getMessageTypes().get(11); internal_static_signalservice_ContactDetails_fieldAccessorTable = new From 0653b2c2d3a96d2b20265a6cbd95c7272a1683b8 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Wed, 21 Apr 2021 16:34:22 +1000 Subject: [PATCH 2/9] only admin can delete members --- .../loki/activities/EditClosedGroupActivity.kt | 16 ++++++++++------ .../activities/EditClosedGroupMembersAdapter.kt | 15 +++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt index e969819d0..7ed4385ac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt @@ -38,7 +38,9 @@ import java.io.IOException class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { private val originalMembers = HashSet() private val members = HashSet() + private val zombies = HashSet() private var hasNameChanged = false + private var isSelfAdmin = false private var isLoading = false set(newValue) { field = newValue; invalidateOptionsMenu() } @@ -54,7 +56,10 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { } private val memberListAdapter by lazy { - EditClosedGroupMembersAdapter(this, GlideApp.with(this), this::onMemberClick) + if (isSelfAdmin) + EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin, this::onMemberClick) + else + EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin) } private lateinit var mainContentContainer: LinearLayout @@ -81,7 +86,10 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { ThemeUtil.getThemedDrawableResId(this, R.attr.actionModeCloseDrawable)) groupID = intent.getStringExtra(groupIDKey)!! - originalName = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title + val groupInfo = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get() + originalName = groupInfo.title + isSelfAdmin = groupInfo.admins.any{ it.serialize() == TextSecurePreferences.getLocalNumber(this) } + name = originalName mainContentContainer = findViewById(R.id.mainContentContainer) @@ -178,10 +186,6 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { this.members.addAll(members) memberListAdapter.setMembers(members) - val admins = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().admins.map { it.toString() }.toMutableSet() - admins.remove(TextSecurePreferences.getLocalNumber(this)) - memberListAdapter.setLockedMembers(admins) - mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt index 3c07136f1..3dc7ff162 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt @@ -7,15 +7,16 @@ import org.session.libsession.messaging.threads.Address import org.thoughtcrime.securesms.loki.views.UserView import org.thoughtcrime.securesms.mms.GlideRequests import org.session.libsession.messaging.threads.recipients.Recipient +import org.session.libsession.utilities.TextSecurePreferences class EditClosedGroupMembersAdapter( private val context: Context, private val glide: GlideRequests, + private val admin: Boolean, private val memberClickListener: ((String) -> Unit)? = null ) : RecyclerView.Adapter() { private val members = ArrayList() - private val lockedMembers = HashSet() fun setMembers(members: Collection) { this.members.clear() @@ -23,12 +24,6 @@ class EditClosedGroupMembersAdapter( notifyDataSetChanged() } - fun setLockedMembers(members: Collection) { - this.lockedMembers.clear() - this.lockedMembers.addAll(members) - notifyDataSetChanged() - } - override fun getItemCount(): Int = members.size override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { @@ -39,15 +34,15 @@ class EditClosedGroupMembersAdapter( override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { val member = members[position] - val lockedMember = lockedMembers.contains(member) + val unlocked = admin && member != TextSecurePreferences.getLocalNumber(context) viewHolder.view.bind(Recipient.from( context, Address.fromSerialized(member), false), glide, - if (lockedMember) UserView.ActionIndicator.None else UserView.ActionIndicator.Menu) + if (unlocked) UserView.ActionIndicator.Menu else UserView.ActionIndicator.None) - if (!lockedMember) { + if (unlocked) { viewHolder.view.setOnClickListener { this.memberClickListener?.invoke(member) } } } From b064f8f5d73045d142b667d79d7e6c95a26a7936 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Fri, 23 Apr 2021 14:07:10 +1000 Subject: [PATCH 3/9] implementation of the zombie members handling logic --- .../securesms/database/GroupDatabase.java | 48 +++++++++++++--- .../securesms/database/Storage.kt | 8 +++ .../database/helpers/SQLCipherOpenHelper.java | 7 ++- .../activities/EditClosedGroupActivity.kt | 57 +++++++++++-------- .../loki/activities/EditClosedGroupLoader.kt | 19 +++++-- .../EditClosedGroupMembersAdapter.kt | 10 ++++ .../libsession/messaging/StorageProtocol.kt | 2 + .../MessageReceiverHandler.kt | 48 ++++++++++++---- .../MessageSenderClosedGroup.kt | 30 +++++++--- 9 files changed, 169 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java index 790b21500..2a41b3c90 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -28,7 +28,6 @@ import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer import org.session.libsignal.service.loki.database.LokiOpenGroupDatabaseProtocol; import java.io.Closeable; -import java.io.IOException; import java.security.SecureRandom; import java.util.Collections; import java.util.LinkedList; @@ -44,6 +43,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt static final String GROUP_ID = "group_id"; private static final String TITLE = "title"; private static final String MEMBERS = "members"; + private static final String ZOMBIE_MEMBERS = "zombie_members"; private static final String AVATAR = "avatar"; private static final String AVATAR_ID = "avatar_id"; private static final String AVATAR_KEY = "avatar_key"; @@ -64,6 +64,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt GROUP_ID + " TEXT, " + TITLE + " TEXT, " + MEMBERS + " TEXT, " + + ZOMBIE_MEMBERS + " TEXT, " + AVATAR + " BLOB, " + AVATAR_ID + " INTEGER, " + AVATAR_KEY + " BLOB, " + @@ -81,7 +82,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt }; private static final String[] GROUP_PROJECTION = { - GROUP_ID, TITLE, MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST, + GROUP_ID, TITLE, MEMBERS, ZOMBIE_MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST, TIMESTAMP, ACTIVE, MMS, AVATAR_URL, ADMINS }; @@ -162,7 +163,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt } public @NonNull List getGroupMembers(String groupId, boolean includeSelf) { - List
members = getCurrentMembers(groupId); + List
members = getCurrentMembers(groupId, false); List recipients = new LinkedList<>(); for (Address member : members) { @@ -177,6 +178,19 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt return recipients; } + public @NonNull List getGroupZombieMembers(String groupId) { + List
members = getCurrentZombieMembers(groupId); + List recipients = new LinkedList<>(); + + for (Address member : members) { + if (member.isContact()) { + recipients.add(Recipient.from(context, member, false)); + } + } + + return recipients; + } + public long create(@NonNull String groupId, @Nullable String title, @NonNull List
members, @Nullable SignalServiceAttachmentPointer avatar, @Nullable String relay, @Nullable List
admins, @NonNull Long formationTimestamp) { @@ -300,6 +314,16 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt }); } + public void updateZombieMembers(String groupId, List
members) { + Collections.sort(members); + + ContentValues contents = new ContentValues(); + contents.put(ZOMBIE_MEMBERS, Address.toSerializedList(members, ',')); + contents.put(ACTIVE, 1); + databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", + new String[] {groupId}); + } + public void updateAdmins(String groupId, List
admins) { Collections.sort(admins); @@ -311,7 +335,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt } public void removeMember(String groupId, Address source) { - List
currentMembers = getCurrentMembers(groupId); + List
currentMembers = getCurrentMembers(groupId, false); currentMembers.remove(source); ContentValues contents = new ContentValues(); @@ -329,18 +353,22 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt }); } - private List
getCurrentMembers(String groupId) { + private List
getCurrentMembers(String groupId, boolean zombieMembers) { Cursor cursor = null; + String membersColumn = MEMBERS; + if (zombieMembers) membersColumn = ZOMBIE_MEMBERS; + try { - cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS}, + cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {membersColumn}, GROUP_ID + " = ?", new String[] {groupId}, null, null, null); if (cursor != null && cursor.moveToFirst()) { - String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)); - return Address.fromSerializedList(serializedMembers, ','); + String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(membersColumn)); + if (serializedMembers != null && !serializedMembers.isEmpty()) + return Address.fromSerializedList(serializedMembers, ','); } return new LinkedList<>(); @@ -350,6 +378,10 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt } } + private List
getCurrentZombieMembers(String groupId) { + return getCurrentMembers(groupId, true); + } + public boolean isActive(String groupId) { Optional record = getGroup(groupId); return record.isPresent() && record.get().isActive(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 0a58ac42f..9279720d6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -395,6 +395,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, DatabaseFactory.getGroupDatabase(context).setActive(groupID, value) } + override fun getZombieMember(groupID: String): Set { + return DatabaseFactory.getGroupDatabase(context).getGroupZombieMembers(groupID).map { it.address.serialize() }.toHashSet() + } + override fun removeMember(groupID: String, member: Address) { DatabaseFactory.getGroupDatabase(context).removeMember(groupID, member) } @@ -403,6 +407,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, DatabaseFactory.getGroupDatabase(context).updateMembers(groupID, members) } + override fun updateZombieMembers(groupID: String, members: List
) { + DatabaseFactory.getGroupDatabase(context).updateZombieMembers(groupID, members) + } + override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection, admins: Collection, sentTimestamp: Long) { val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList()) val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 88d8c679f..82a204488 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -54,9 +54,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int lokiV20 = 41; private static final int lokiV21 = 42; private static final int lokiV22 = 43; + private static final int lokiV23 = 44; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes - private static final int DATABASE_VERSION = lokiV22; + private static final int DATABASE_VERSION = lokiV23; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -272,6 +273,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { "SendDeliveryReceiptJob"); } + if (oldVersion < lokiV23) { + db.execSQL("ALTER TABLE groups ADD COLUMN zombie_members TEXT"); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt index 7ed4385ac..80f39f099 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt @@ -37,8 +37,13 @@ import java.io.IOException class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { private val originalMembers = HashSet() - private val members = HashSet() private val zombies = HashSet() + private val members = HashSet() + private val allMembers: Set + get() { + return if (zombies.isNotEmpty()) (members + zombies) + else members + } private var hasNameChanged = false private var isSelfAdmin = false private var isLoading = false @@ -124,31 +129,35 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { } } - LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks> { + LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks { - override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { + override fun onCreateLoader(id: Int, bundle: Bundle?): Loader { return EditClosedGroupLoader(this@EditClosedGroupActivity, groupID) } - override fun onLoadFinished(loader: Loader>, members: List) { + override fun onLoadFinished(loader: Loader, groupMembers: GroupMembers) { // We no longer need any subsequent loading events // (they will occur on every activity resume). LoaderManager.getInstance(this@EditClosedGroupActivity).destroyLoader(loaderID) + members.clear() + members.addAll(groupMembers.members.toHashSet()) + zombies.clear() + zombies.addAll(groupMembers.zombieMembers.toHashSet()) originalMembers.clear() - originalMembers.addAll(members.toHashSet()) - updateMembers(originalMembers) + originalMembers.addAll(members + zombies) + updateMembers() } - override fun onLoaderReset(loader: Loader>) { - updateMembers(setOf()) + override fun onLoaderReset(loader: Loader) { + updateMembers() } }) } override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_edit_closed_group, menu) - return members.isNotEmpty() && !isLoading + return allMembers.isNotEmpty() && !isLoading } // endregion @@ -161,8 +170,8 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { if (data == null || data.extras == null || !data.hasExtra(SelectContactsActivity.selectedContactsKey)) return val selectedContacts = data.extras!!.getStringArray(SelectContactsActivity.selectedContactsKey)!!.toSet() - val changedMembers = members + selectedContacts - updateMembers(changedMembers) + members.addAll(selectedContacts) + updateMembers() } } } @@ -181,13 +190,12 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { } } - private fun updateMembers(members: Set) { - this.members.clear() - this.members.addAll(members) - memberListAdapter.setMembers(members) + private fun updateMembers() { + memberListAdapter.setMembers(allMembers) + memberListAdapter.setZombieMembers(zombies) - mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE - emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE + mainContentContainer.visibility = if (allMembers.isEmpty()) View.GONE else View.VISIBLE + emptyStateContainer.visibility = if (allMembers.isEmpty()) View.VISIBLE else View.GONE invalidateOptionsMenu() } @@ -204,8 +212,9 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { private fun onMemberClick(member: String) { val bottomSheet = ClosedGroupEditingOptionsBottomSheet() bottomSheet.onRemoveTapped = { - val changedMembers = members - member - updateMembers(changedMembers) + if (zombies.contains(member)) zombies.remove(member) + else members.remove(member) + updateMembers() bottomSheet.dismiss() } bottomSheet.show(supportFragmentManager, "GroupEditingOptionsBottomSheet") @@ -213,7 +222,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { private fun onAddMembersClick() { val intent = Intent(this@EditClosedGroupActivity, SelectContactsActivity::class.java) - intent.putExtra(SelectContactsActivity.usersToExcludeKey, members.toTypedArray()) + intent.putExtra(SelectContactsActivity.usersToExcludeKey, allMembers.toTypedArray()) intent.putExtra(SelectContactsActivity.emptyStateTextKey, "No contacts to add") startActivityForResult(intent, addUsersRequestCode) } @@ -233,7 +242,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { } private fun commitChanges() { - val hasMemberListChanges = (members != originalMembers) + val hasMemberListChanges = (allMembers != originalMembers) if (!hasNameChanged && !hasMemberListChanges) { return finish() @@ -241,15 +250,13 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { val name = if (hasNameChanged) this.name else originalName - val members = this.members.map { + val members = this.allMembers.map { Recipient.from(this, Address.fromSerialized(it), false) }.toSet() val originalMembers = this.originalMembers.map { Recipient.from(this, Address.fromSerialized(it), false) }.toSet() - val admins = members.toSet() //TODO For now, consider all the users to be admins. - var isClosedGroup: Boolean var groupPublicKey: String? try { @@ -307,4 +314,6 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { } } } + + class GroupMembers(val members: List, val zombieMembers: List) { } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt index 4f49501fc..d2bb17a73 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt @@ -4,12 +4,19 @@ import android.content.Context import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.util.AsyncLoader -class EditClosedGroupLoader(context: Context, val groupID: String) : AsyncLoader>(context) { +class EditClosedGroupLoader(context: Context, val groupID: String) : AsyncLoader(context) { - override fun loadInBackground(): List { - val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, true) - return members.map { - it.address.toString() - } + override fun loadInBackground(): EditClosedGroupActivity.GroupMembers { + val groupDatabase = DatabaseFactory.getGroupDatabase(context) + val members = groupDatabase.getGroupMembers(groupID, true) + val zombieMembers = groupDatabase.getGroupZombieMembers(groupID) + return EditClosedGroupActivity.GroupMembers( + members.map { + it.address.toString() + }, + zombieMembers.map { + it.address.toString() + } + ) } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt index 3dc7ff162..63f33d9aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt @@ -17,6 +17,7 @@ class EditClosedGroupMembersAdapter( ) : RecyclerView.Adapter() { private val members = ArrayList() + private val zombieMembers = ArrayList() fun setMembers(members: Collection) { this.members.clear() @@ -24,6 +25,12 @@ class EditClosedGroupMembersAdapter( notifyDataSetChanged() } + fun setZombieMembers(members: Collection) { + this.zombieMembers.clear() + this.zombieMembers.addAll(members) + notifyDataSetChanged() + } + override fun getItemCount(): Int = members.size override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { @@ -42,6 +49,9 @@ class EditClosedGroupMembersAdapter( glide, if (unlocked) UserView.ActionIndicator.Menu else UserView.ActionIndicator.None) + if (zombieMembers.contains(member)) + viewHolder.view.alpha = 0.5F + if (unlocked) { viewHolder.view.setOnClickListener { this.memberClickListener?.invoke(member) } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt index da84c8f6e..80012b8ec 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt @@ -109,8 +109,10 @@ interface StorageProtocol { fun createGroup(groupID: String, title: String?, members: List
, avatar: SignalServiceAttachmentPointer?, relay: String?, admins: List
, formationTimestamp: Long) fun isGroupActive(groupPublicKey: String): Boolean fun setActive(groupID: String, value: Boolean) + fun getZombieMember(groupID: String): Set fun removeMember(groupID: String, member: Address) fun updateMembers(groupID: String, members: List
) + fun updateZombieMembers(groupID: String, members: List
) // Closed Group fun getAllClosedGroupPublicKeys(): Set fun getAllActiveClosedGroupPublicKeys(): Set diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt index fc1b28e1c..185befb9b 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt @@ -355,7 +355,7 @@ private fun MessageReceiver.handleClosedGroupEncryptionKeyPair(message: ClosedGr return } if (!group.admins.map { it.toString() }.contains(senderPublicKey)) { - Log.d("Loki", "Ignoring closed group encryption key pair from non-member.") + Log.d("Loki", "Ignoring closed group encryption key pair from non-admin.") return } // Find our wrapper and decrypt it if possible @@ -458,6 +458,11 @@ private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupCo } } +/// Removes the given members from the group IF +/// • it wasn't the admin that was removed (that should happen through a `MEMBER_LEFT` message). +/// • the admin sent the message (only the admin can truly remove members). +/// If we're among the users that were removed, delete all encryption key pairs and the group public key, unsubscribe +/// from push notifications for this closed group, and remove the given members from the zombie list for this group. private fun MessageReceiver.handleClosedGroupMembersRemoved(message: ClosedGroupControlMessage) { val context = MessagingConfiguration.shared.context val storage = MessagingConfiguration.shared.storage @@ -471,7 +476,7 @@ private fun MessageReceiver.handleClosedGroupMembersRemoved(message: ClosedGroup return } if (!group.isActive) { - Log.d("Loki", "Ignoring closed group info message for inactive group") + Log.d("Loki", "Ignoring closed group info message for inactive group.") return } val name = group.title @@ -482,6 +487,18 @@ private fun MessageReceiver.handleClosedGroupMembersRemoved(message: ClosedGroup // Users that are part of this remove update val updateMembers = kind.members.map { it.toByteArray().toHexString() } + // Check that the admin wasn't removed + if (updateMembers.contains(admins.first())) { + Log.d("Loki", "Ignoring invalid closed group update.") + return + } + + // Check that the message was sent by the group admin + if (!admins.contains(senderPublicKey)) { + Log.d("Loki", "Ignoring invalid closed group update.") + return + } + if (!isValidGroupUpdate(group, message.sentTimestamp!!, senderPublicKey)) { return } // If admin leaves the group is disbanded val didAdminLeave = admins.any { it in updateMembers } @@ -498,12 +515,12 @@ private fun MessageReceiver.handleClosedGroupMembersRemoved(message: ClosedGroup if (didAdminLeave || wasCurrentUserRemoved) { disableLocalGroupAndUnsubscribe(groupPublicKey, groupID, userPublicKey) } else { - val isCurrentUserAdmin = admins.contains(userPublicKey) storage.updateMembers(groupID, newMembers.map { Address.fromSerialized(it) }) - if (isCurrentUserAdmin) { - MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey, newMembers) - } } + // update zombie members + val zombies = storage.getZombieMember(groupID) + storage.updateZombieMembers(groupID, zombies.minus(updateMembers).map { Address.fromSerialized(it) }) + val type = if (senderLeft) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.MEMBER_REMOVED @@ -517,6 +534,10 @@ private fun MessageReceiver.handleClosedGroupMembersRemoved(message: ClosedGroup } } +/// If a regular member left: +/// • Mark them as a zombie (to be removed by the admin later). +/// If the admin left: +/// • Unsubscribe from PNs, delete the group public key, etc. as the group will be disbanded. private fun MessageReceiver.handleClosedGroupMemberLeft(message: ClosedGroupControlMessage) { val context = MessagingConfiguration.shared.context val storage = MessagingConfiguration.shared.storage @@ -549,11 +570,14 @@ private fun MessageReceiver.handleClosedGroupMemberLeft(message: ClosedGroupCont // admin left the group of linked device left the group disableLocalGroupAndUnsubscribe(groupPublicKey, groupID, userPublicKey) } else { - val isCurrentUserAdmin = admins.contains(userPublicKey) + //val isCurrentUserAdmin = admins.contains(userPublicKey) storage.updateMembers(groupID, updatedMemberList.map { Address.fromSerialized(it) }) - if (isCurrentUserAdmin) { - MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey, updatedMemberList) - } + //if (isCurrentUserAdmin) { + // MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey, updatedMemberList) + //} + // update zombie members + val zombies = storage.getZombieMember(groupID) + storage.updateZombieMembers(groupID, zombies.plus(senderPublicKey).map { Address.fromSerialized(it) }) } // Notify the user if (userLeft) { @@ -566,7 +590,7 @@ private fun MessageReceiver.handleClosedGroupMemberLeft(message: ClosedGroupCont } private fun MessageReceiver.handleClosedGroupEncryptionKeyPairRequest(message: ClosedGroupControlMessage) { - val storage = MessagingConfiguration.shared.storage + /*val storage = MessagingConfiguration.shared.storage val senderPublicKey = message.sender ?: return val userPublicKey = storage.getUserPublicKey()!! if (message.kind!! !is ClosedGroupControlMessage.Kind.EncryptionKeyPairRequest) return @@ -587,7 +611,7 @@ private fun MessageReceiver.handleClosedGroupEncryptionKeyPairRequest(message: C Log.d("Loki", "Couldn't get encryption key pair for closed group.") } else { MessageSender.sendEncryptionKeyPair(groupPublicKey, encryptionKeyPair, setOf(senderPublicKey), targetUser = senderPublicKey, force = false) - } + }*/ } private fun isValidGroupUpdate(group: GroupRecord, diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt index 143fa7fb3..8e3622282 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt @@ -169,15 +169,24 @@ fun MessageSender.removeMembers(groupPublicKey: String, membersToRemove: List Date: Fri, 23 Apr 2021 16:35:24 +1000 Subject: [PATCH 4/9] fix encryption key pair handling --- .../messaging/sending_receiving/MessageReceiverHandler.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt index 185befb9b..8c08b879f 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt @@ -341,7 +341,8 @@ private fun MessageReceiver.handleClosedGroupEncryptionKeyPair(message: ClosedGr val storage = MessagingConfiguration.shared.storage val senderPublicKey = message.sender ?: return val kind = message.kind!! as? ClosedGroupControlMessage.Kind.EncryptionKeyPair ?: return - val groupPublicKey = kind.publicKey?.toByteArray()?.toHexString() ?: message.groupPublicKey ?: return + var groupPublicKey = kind.publicKey?.toByteArray()?.toHexString() + if (groupPublicKey.isNullOrEmpty()) groupPublicKey = message.groupPublicKey ?: return val userPublicKey = storage.getUserPublicKey()!! val userKeyPair = storage.getUserX25519KeyPair() // Unwrap the message From 7c6475d9ee5de4326a94caca909c241e8ab3aa7c Mon Sep 17 00:00:00 2001 From: Brice-W Date: Mon, 26 Apr 2021 11:51:11 +1000 Subject: [PATCH 5/9] EncryptionKeyPair: only set a public key for 1-1 message case --- .../messaging/messages/control/ClosedGroupControlMessage.kt | 2 +- .../messaging/sending_receiving/MessageSenderClosedGroup.kt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt index fa6544fc2..e823560ea 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt @@ -170,7 +170,7 @@ class ClosedGroupControlMessage() : ControlMessage() { } is Kind.EncryptionKeyPair -> { closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR - closedGroupControlMessage.publicKey = kind.publicKey + closedGroupControlMessage.publicKey = kind.publicKey ?: ByteString.EMPTY closedGroupControlMessage.addAllWrappers(kind.wrappers.map { it.toProto() }) } is Kind.NameChange -> { diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt index 8e3622282..36104265c 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt @@ -277,7 +277,9 @@ fun MessageSender.sendEncryptionKeyPair(groupPublicKey: String, newKeyPair: ECKe val ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, publicKey) ClosedGroupControlMessage.KeyPairWrapper(publicKey, ByteString.copyFrom(ciphertext)) } - val kind = ClosedGroupControlMessage.Kind.EncryptionKeyPair(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), wrappers) + // we only set public key of EncryptionKeyPair if we send the message to a one-on-one contact, otherwise if sent to a group it is set tu null as we use the one of the envelope + val kind = if (targetUser != null) ClosedGroupControlMessage.Kind.EncryptionKeyPair(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), wrappers) + else ClosedGroupControlMessage.Kind.EncryptionKeyPair(null, wrappers) val sentTime = System.currentTimeMillis() val closedGroupControlMessage = ClosedGroupControlMessage(kind) closedGroupControlMessage.sentTimestamp = sentTime From f0394fb908f631c954ec367151937311d50d4d4c Mon Sep 17 00:00:00 2001 From: Brice-W Date: Mon, 26 Apr 2021 14:14:39 +1000 Subject: [PATCH 6/9] fix avatar display issues --- .../loki/activities/EditClosedGroupMembersAdapter.kt | 2 ++ .../thoughtcrime/securesms/loki/views/ProfilePictureView.kt | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt index 63f33d9aa..7d5ed3408 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt @@ -51,6 +51,8 @@ class EditClosedGroupMembersAdapter( if (zombieMembers.contains(member)) viewHolder.view.alpha = 0.5F + else + viewHolder.view.alpha = 1F if (unlocked) { viewHolder.view.setOnClickListener { this.memberClickListener?.invoke(member) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt index a09284163..3008c9cd9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt @@ -146,13 +146,13 @@ class ProfilePictureView : RelativeLayout { private fun setProfilePictureIfNeeded(imageView: ImageView, publicKey: String, displayName: String?, @DimenRes sizeResId: Int) { if (publicKey.isNotEmpty()) { val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false) - if (imagesCached.contains(recipient.profileAvatar.orEmpty())) return + if (imagesCached.contains(publicKey)) return val signalProfilePicture = recipient.contactPhoto if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0" && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") { glide.clear(imageView) glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC).circleCrop().into(imageView) - imagesCached.add(recipient.profileAvatar.orEmpty()) + imagesCached.add(publicKey) } else { val sizeInPX = resources.getDimensionPixelSize(sizeResId) glide.clear(imageView) @@ -162,7 +162,7 @@ class ProfilePictureView : RelativeLayout { publicKey, displayName )).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView) - imagesCached.add(recipient.profileAvatar.orEmpty()) + imagesCached.add(publicKey) } } else { imageView.setImageDrawable(null) From 6a32fabda5e0a9139a088d44e049dc49f78e0c59 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Mon, 26 Apr 2021 15:38:26 +1000 Subject: [PATCH 7/9] revert change of commit 7c6475d9ee5de4326a94caca909c241e8ab3aa7c --- .../messaging/sending_receiving/MessageSenderClosedGroup.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt index 36104265c..8e3622282 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt @@ -277,9 +277,7 @@ fun MessageSender.sendEncryptionKeyPair(groupPublicKey: String, newKeyPair: ECKe val ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, publicKey) ClosedGroupControlMessage.KeyPairWrapper(publicKey, ByteString.copyFrom(ciphertext)) } - // we only set public key of EncryptionKeyPair if we send the message to a one-on-one contact, otherwise if sent to a group it is set tu null as we use the one of the envelope - val kind = if (targetUser != null) ClosedGroupControlMessage.Kind.EncryptionKeyPair(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), wrappers) - else ClosedGroupControlMessage.Kind.EncryptionKeyPair(null, wrappers) + val kind = ClosedGroupControlMessage.Kind.EncryptionKeyPair(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), wrappers) val sentTime = System.currentTimeMillis() val closedGroupControlMessage = ClosedGroupControlMessage(kind) closedGroupControlMessage.sentTimestamp = sentTime From d4aaebd0a93c05918331694593335b83fe6b9ad7 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Mon, 26 Apr 2021 16:33:54 +1000 Subject: [PATCH 8/9] don't show zombie members in group remove updates --- .../MessageReceiverHandler.kt | 18 ++++++++++++------ .../MessageSenderClosedGroup.kt | 11 ++++++++--- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt index 8c08b879f..38c497803 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt @@ -437,6 +437,7 @@ private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupCo val updateMembers = kind.members.map { it.toByteArray().toHexString() } val newMembers = members + updateMembers storage.updateMembers(groupID, newMembers.map { Address.fromSerialized(it) }) + // Notify the user if (userPublicKey == senderPublicKey) { // sender is a linked device @@ -526,12 +527,17 @@ private fun MessageReceiver.handleClosedGroupMembersRemoved(message: ClosedGroup else SignalServiceGroup.Type.MEMBER_REMOVED // Notify the user - if (userPublicKey == senderPublicKey) { - // sender is a linked device - val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) - storage.insertOutgoingInfoMessage(context, groupID, type, name, updateMembers, admins, threadID, message.sentTimestamp!!) - } else { - storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, type, name, updateMembers, admins, message.sentTimestamp!!) + // we don't display zombie members in the notification as users have already been notified when those members left + val notificationMembers = updateMembers.minus(zombies) + if (notificationMembers.isNotEmpty()) { + // no notification to display when only zombies have been removed + if (userPublicKey == senderPublicKey) { + // sender is a linked device + val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) + storage.insertOutgoingInfoMessage(context, groupID, type, name, notificationMembers, admins, threadID, message.sentTimestamp!!) + } else { + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, type, name, notificationMembers, admins, message.sentTimestamp!!) + } } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt index 8e3622282..57d2dcc2e 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt @@ -201,9 +201,14 @@ fun MessageSender.removeMembers(groupPublicKey: String, membersToRemove: List { From ab68001b1520b512f557e10fa7bdc292ab3005ff Mon Sep 17 00:00:00 2001 From: Brice-W Date: Wed, 28 Apr 2021 16:00:13 +1000 Subject: [PATCH 9/9] clean --- .../org/thoughtcrime/securesms/database/GroupDatabase.java | 2 -- .../securesms/loki/activities/EditClosedGroupActivity.kt | 3 +-- .../messaging/sending_receiving/ReceivedMessageHandler.kt | 4 ---- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java index 2a41b3c90..fb1fbd6d2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -183,9 +183,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt List recipients = new LinkedList<>(); for (Address member : members) { - if (member.isContact()) { recipients.add(Recipient.from(context, member, false)); - } } return recipients; diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt index 80f39f099..88c89f731 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt @@ -41,8 +41,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { private val members = HashSet() private val allMembers: Set get() { - return if (zombies.isNotEmpty()) (members + zombies) - else members + return members + zombies } private var hasNameChanged = false private var isSelfAdmin = false diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt index b46c6d405..81c7387b7 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt @@ -522,11 +522,7 @@ private fun MessageReceiver.handleClosedGroupMemberLeft(message: ClosedGroupCont // admin left the group of linked device left the group disableLocalGroupAndUnsubscribe(groupPublicKey, groupID, userPublicKey) } else { - //val isCurrentUserAdmin = admins.contains(userPublicKey) storage.updateMembers(groupID, updatedMemberList.map { Address.fromSerialized(it) }) - //if (isCurrentUserAdmin) { - // MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey, updatedMemberList) - //} // update zombie members val zombies = storage.getZombieMember(groupID) storage.updateZombieMembers(groupID, zombies.plus(senderPublicKey).map { Address.fromSerialized(it) })