From: AsamK Date: Mon, 14 Dec 2020 18:14:49 +0000 (+0100) Subject: Implement quit group X-Git-Tag: v0.7.0~2 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/1098b64711e3a1fc9235072171a25fc130b0b044 Implement quit group --- diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 887f9e42..309482b1 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -739,17 +739,24 @@ public class Manager implements Closeable { } public Pair> 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 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())); diff --git a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 153f8cd0..7c0339c9 100644 --- a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -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 leaveGroup(GroupInfoV2 groupInfoV2) throws IOException { + List pendingMembersList = groupInfoV2.getGroup().getPendingMembersList(); + final UUID selfUuid = selfAddressProvider.getSelfAddress().getUuid().get(); + Optional selfPendingMember = DecryptedGroupUtil.findPendingByUuid(pendingMembersList, + selfUuid); + + if (selfPendingMember.isPresent()) { + return revokeInvites(groupInfoV2, Set.of(selfPendingMember.get())); + } else { + return ejectMembers(groupInfoV2, Set.of(selfUuid)); + } + } + + public Pair revokeInvites( + GroupInfoV2 groupInfoV2, Set pendingMembers + ) throws IOException { + final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); + final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); + final Set 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 ejectMembers(GroupInfoV2 groupInfoV2, Set 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 commitChange( GroupInfoV2 groupInfoV2, GroupChange.Actions.Builder change ) throws IOException {