]> nmode's Git Repositories - signal-cli/commitdiff
Implement quit group
authorAsamK <asamk@gmx.de>
Mon, 14 Dec 2020 18:14:49 +0000 (19:14 +0100)
committerAsamK <asamk@gmx.de>
Mon, 14 Dec 2020 18:14:58 +0000 (19:14 +0100)
src/main/java/org/asamk/signal/manager/Manager.java
src/main/java/org/asamk/signal/manager/helper/GroupHelper.java

index 887f9e425642e21f1b024fb103d33ce9b3a07260..309482b1d5f9ca2f74938b8a0f3ad168ca53f7fe 100644 (file)
@@ -739,17 +739,24 @@ public class Manager implements Closeable {
     }
 
     public Pair<Long, List<SendMessageResult>> sendQuitGroupMessage(byte[] groupId) throws GroupNotFoundException, IOException, NotAGroupMemberException {
-        SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT).withId(groupId).build();
 
-        SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group);
+        SignalServiceDataMessage.Builder messageBuilder;
 
         final GroupInfo g = getGroupForSending(groupId);
         if (g instanceof GroupInfoV1) {
             GroupInfoV1 groupInfoV1 = (GroupInfoV1) g;
+            SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT)
+                    .withId(groupId)
+                    .build();
+            messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group);
             groupInfoV1.removeMember(account.getSelfAddress());
             account.getGroupStore().updateGroup(groupInfoV1);
         } else {
-            throw new RuntimeException("TODO Not implemented!");
+            final GroupInfoV2 groupInfoV2 = (GroupInfoV2) g;
+            final Pair<DecryptedGroup, GroupChange> groupGroupChangePair = groupHelper.leaveGroup(groupInfoV2);
+            groupInfoV2.setGroup(groupGroupChangePair.first());
+            messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().toByteArray());
+            account.getGroupStore().updateGroup(groupInfoV2);
         }
 
         return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfAddress()));
index 153f8cd0d45db157bab7e5dbafd514e652967c69..7c0339c92c187a9f0c79558c9afb01c879e30157 100644 (file)
@@ -8,9 +8,12 @@ import org.signal.storageservice.protos.groups.GroupChange;
 import org.signal.storageservice.protos.groups.Member;
 import org.signal.storageservice.protos.groups.local.DecryptedGroup;
 import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
+import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
+import org.signal.zkgroup.InvalidInputException;
 import org.signal.zkgroup.VerificationFailedException;
 import org.signal.zkgroup.groups.GroupMasterKey;
 import org.signal.zkgroup.groups.GroupSecretParams;
+import org.signal.zkgroup.groups.UuidCiphertext;
 import org.signal.zkgroup.profiles.ProfileKeyCredential;
 import org.whispersystems.libsignal.util.Pair;
 import org.whispersystems.libsignal.util.guava.Optional;
@@ -28,6 +31,7 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
@@ -198,6 +202,40 @@ public class GroupHelper {
         return commitChange(groupInfoV2, change);
     }
 
+    public Pair<DecryptedGroup, GroupChange> leaveGroup(GroupInfoV2 groupInfoV2) throws IOException {
+        List<DecryptedPendingMember> pendingMembersList = groupInfoV2.getGroup().getPendingMembersList();
+        final UUID selfUuid = selfAddressProvider.getSelfAddress().getUuid().get();
+        Optional<DecryptedPendingMember> selfPendingMember = DecryptedGroupUtil.findPendingByUuid(pendingMembersList,
+                selfUuid);
+
+        if (selfPendingMember.isPresent()) {
+            return revokeInvites(groupInfoV2, Set.of(selfPendingMember.get()));
+        } else {
+            return ejectMembers(groupInfoV2, Set.of(selfUuid));
+        }
+    }
+
+    public Pair<DecryptedGroup, GroupChange> revokeInvites(
+            GroupInfoV2 groupInfoV2, Set<DecryptedPendingMember> pendingMembers
+    ) throws IOException {
+        final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
+        final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams);
+        final Set<UuidCiphertext> uuidCipherTexts = pendingMembers.stream().map(member -> {
+            try {
+                return new UuidCiphertext(member.getUuidCipherText().toByteArray());
+            } catch (InvalidInputException e) {
+                throw new AssertionError(e);
+            }
+        }).collect(Collectors.toSet());
+        return commitChange(groupInfoV2, groupOperations.createRemoveInvitationChange(uuidCipherTexts));
+    }
+
+    public Pair<DecryptedGroup, GroupChange> ejectMembers(GroupInfoV2 groupInfoV2, Set<UUID> uuids) throws IOException {
+        final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
+        final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams);
+        return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(uuids));
+    }
+
     private Pair<DecryptedGroup, GroupChange> commitChange(
             GroupInfoV2 groupInfoV2, GroupChange.Actions.Builder change
     ) throws IOException {