}
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()));
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;
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;
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 {