]> nmode's Git Repositories - signal-cli/blobdiff - lib/src/main/java/org/asamk/signal/manager/Manager.java
Update group with remaining options, after creating it
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / Manager.java
index 9d73ac814d61d612d1624df98142ff0f787957f2..f285e2ef19d07e7af28a4f507b97c27fc0fa7bb8 100644 (file)
@@ -23,10 +23,13 @@ import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
 import org.asamk.signal.manager.groups.GroupId;
 import org.asamk.signal.manager.groups.GroupIdV1;
 import org.asamk.signal.manager.groups.GroupInviteLinkUrl;
+import org.asamk.signal.manager.groups.GroupLinkState;
 import org.asamk.signal.manager.groups.GroupNotFoundException;
+import org.asamk.signal.manager.groups.GroupPermission;
 import org.asamk.signal.manager.groups.GroupUtils;
+import org.asamk.signal.manager.groups.LastGroupAdminException;
 import org.asamk.signal.manager.groups.NotAGroupMemberException;
-import org.asamk.signal.manager.helper.GroupHelper;
+import org.asamk.signal.manager.helper.GroupV2Helper;
 import org.asamk.signal.manager.helper.PinHelper;
 import org.asamk.signal.manager.helper.ProfileHelper;
 import org.asamk.signal.manager.helper.UnidentifiedAccessHelper;
@@ -191,7 +194,7 @@ public class Manager implements Closeable {
 
     private final UnidentifiedAccessHelper unidentifiedAccessHelper;
     private final ProfileHelper profileHelper;
-    private final GroupHelper groupHelper;
+    private final GroupV2Helper groupV2Helper;
     private final PinHelper pinHelper;
     private final AvatarStore avatarStore;
     private final AttachmentStore attachmentStore;
@@ -259,7 +262,7 @@ public class Manager implements Closeable {
                 unidentified -> unidentified ? getOrCreateUnidentifiedMessagePipe() : getOrCreateMessagePipe(),
                 () -> messageReceiver,
                 this::resolveSignalServiceAddress);
-        this.groupHelper = new GroupHelper(this::getRecipientProfileKeyCredential,
+        this.groupV2Helper = new GroupV2Helper(this::getRecipientProfileKeyCredential,
                 this::getRecipientProfile,
                 account::getSelfRecipientId,
                 groupsV2Operations,
@@ -761,7 +764,9 @@ public class Manager implements Closeable {
         return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfRecipientId()));
     }
 
-    public Pair<Long, List<SendMessageResult>> sendQuitGroupMessage(GroupId groupId) throws GroupNotFoundException, IOException, NotAGroupMemberException {
+    public Pair<Long, List<SendMessageResult>> sendQuitGroupMessage(
+            GroupId groupId, Set<String> groupAdmins
+    ) throws GroupNotFoundException, IOException, NotAGroupMemberException, InvalidNumberException, LastGroupAdminException {
         SignalServiceDataMessage.Builder messageBuilder;
 
         final var g = getGroupForUpdating(groupId);
@@ -773,7 +778,18 @@ public class Manager implements Closeable {
             account.getGroupStore().updateGroup(groupInfoV1);
         } else {
             final var groupInfoV2 = (GroupInfoV2) g;
-            final var groupGroupChangePair = groupHelper.leaveGroup(groupInfoV2);
+            final var currentAdmins = g.getAdminMembers();
+            final var newAdmins = getSignalServiceAddresses(groupAdmins);
+            newAdmins.removeAll(currentAdmins);
+            newAdmins.retainAll(g.getMembers());
+            if (currentAdmins.contains(getSelfRecipientId())
+                    && currentAdmins.size() == 1
+                    && g.getMembers().size() > 1
+                    && newAdmins.size() == 0) {
+                // Last admin can't leave the group, unless she's also the last member
+                throw new LastGroupAdminException(g.getGroupId(), g.getTitle());
+            }
+            final var groupGroupChangePair = groupV2Helper.leaveGroup(groupInfoV2, newAdmins);
             groupInfoV2.setGroup(groupGroupChangePair.first(), this::resolveRecipient);
             messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().toByteArray());
             account.getGroupStore().updateGroup(groupInfoV2);
@@ -797,7 +813,7 @@ public class Manager implements Closeable {
             members.remove(selfRecipientId);
         }
 
-        var gv2Pair = groupHelper.createGroupV2(name == null ? "" : name,
+        var gv2Pair = groupV2Helper.createGroup(name == null ? "" : name,
                 members == null ? Set.of() : members,
                 avatarFile);
 
@@ -831,31 +847,69 @@ public class Manager implements Closeable {
             String description,
             List<String> members,
             List<String> removeMembers,
-            File avatarFile
+            List<String> admins,
+            List<String> removeAdmins,
+            boolean resetGroupLink,
+            GroupLinkState groupLinkState,
+            GroupPermission addMemberPermission,
+            GroupPermission editDetailsPermission,
+            File avatarFile,
+            Integer expirationTimer
     ) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException {
         return updateGroup(groupId,
                 name,
                 description,
                 members == null ? null : getSignalServiceAddresses(members),
                 removeMembers == null ? null : getSignalServiceAddresses(removeMembers),
-                avatarFile);
+                admins == null ? null : getSignalServiceAddresses(admins),
+                removeAdmins == null ? null : getSignalServiceAddresses(removeAdmins),
+                resetGroupLink,
+                groupLinkState,
+                addMemberPermission,
+                editDetailsPermission,
+                avatarFile,
+                expirationTimer);
     }
 
     private Pair<Long, List<SendMessageResult>> updateGroup(
-            GroupId groupId,
-            String name,
-            String description,
-            Set<RecipientId> members,
+            final GroupId groupId,
+            final String name,
+            final String description,
+            final Set<RecipientId> members,
             final Set<RecipientId> removeMembers,
-            File avatarFile
+            final Set<RecipientId> admins,
+            final Set<RecipientId> removeAdmins,
+            final boolean resetGroupLink,
+            final GroupLinkState groupLinkState,
+            final GroupPermission addMemberPermission,
+            final GroupPermission editDetailsPermission,
+            final File avatarFile,
+            final Integer expirationTimer
     ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
         var group = getGroupForUpdating(groupId);
 
         if (group instanceof GroupInfoV2) {
-            return updateGroupV2((GroupInfoV2) group, name, description, members, removeMembers, avatarFile);
+            return updateGroupV2((GroupInfoV2) group,
+                    name,
+                    description,
+                    members,
+                    removeMembers,
+                    admins,
+                    removeAdmins,
+                    resetGroupLink,
+                    groupLinkState,
+                    addMemberPermission,
+                    editDetailsPermission,
+                    avatarFile,
+                    expirationTimer);
+        }
+
+        final var gv1 = (GroupInfoV1) group;
+        final var result = updateGroupV1(gv1, name, members, avatarFile);
+        if (expirationTimer != null) {
+            setExpirationTimer(gv1, expirationTimer);
         }
-
-        return updateGroupV1((GroupInfoV1) group, name, members, avatarFile);
+        return result;
     }
 
     private Pair<Long, List<SendMessageResult>> updateGroupV1(
@@ -913,11 +967,18 @@ public class Manager implements Closeable {
             final String description,
             final Set<RecipientId> members,
             final Set<RecipientId> removeMembers,
-            final File avatarFile
+            final Set<RecipientId> admins,
+            final Set<RecipientId> removeAdmins,
+            final boolean resetGroupLink,
+            final GroupLinkState groupLinkState,
+            final GroupPermission addMemberPermission,
+            final GroupPermission editDetailsPermission,
+            final File avatarFile,
+            Integer expirationTimer
     ) throws IOException {
         Pair<Long, List<SendMessageResult>> result = null;
         if (group.isPendingMember(account.getSelfRecipientId())) {
-            var groupGroupChangePair = groupHelper.acceptInvite(group);
+            var groupGroupChangePair = groupV2Helper.acceptInvite(group);
             result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
         }
 
@@ -925,7 +986,7 @@ public class Manager implements Closeable {
             final var newMembers = new HashSet<>(members);
             newMembers.removeAll(group.getMembers());
             if (newMembers.size() > 0) {
-                var groupGroupChangePair = groupHelper.updateGroupV2(group, newMembers);
+                var groupGroupChangePair = groupV2Helper.addMembers(group, newMembers);
                 result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
             }
         }
@@ -935,20 +996,72 @@ public class Manager implements Closeable {
             existingRemoveMembers.retainAll(group.getMembers());
             existingRemoveMembers.remove(getSelfRecipientId());// self can be removed with sendQuitGroupMessage
             if (existingRemoveMembers.size() > 0) {
-                var groupGroupChangePair = groupHelper.removeMembers(group, existingRemoveMembers);
+                var groupGroupChangePair = groupV2Helper.removeMembers(group, existingRemoveMembers);
                 result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
             }
 
             var pendingRemoveMembers = new HashSet<>(removeMembers);
             pendingRemoveMembers.retainAll(group.getPendingMembers());
             if (pendingRemoveMembers.size() > 0) {
-                var groupGroupChangePair = groupHelper.revokeInvitedMembers(group, pendingRemoveMembers);
+                var groupGroupChangePair = groupV2Helper.revokeInvitedMembers(group, pendingRemoveMembers);
                 result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
             }
         }
 
-        if (result == null || name != null || description != null || avatarFile != null) {
-            var groupGroupChangePair = groupHelper.updateGroupV2(group, name, description, avatarFile);
+        if (admins != null) {
+            final var newAdmins = new HashSet<>(admins);
+            newAdmins.retainAll(group.getMembers());
+            newAdmins.removeAll(group.getAdminMembers());
+            if (newAdmins.size() > 0) {
+                for (var admin : newAdmins) {
+                    var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, true);
+                    result = sendUpdateGroupV2Message(group,
+                            groupGroupChangePair.first(),
+                            groupGroupChangePair.second());
+                }
+            }
+        }
+
+        if (removeAdmins != null) {
+            final var existingRemoveAdmins = new HashSet<>(removeAdmins);
+            existingRemoveAdmins.retainAll(group.getAdminMembers());
+            if (existingRemoveAdmins.size() > 0) {
+                for (var admin : existingRemoveAdmins) {
+                    var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, false);
+                    result = sendUpdateGroupV2Message(group,
+                            groupGroupChangePair.first(),
+                            groupGroupChangePair.second());
+                }
+            }
+        }
+
+        if (resetGroupLink) {
+            var groupGroupChangePair = groupV2Helper.resetGroupLinkPassword(group);
+            result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+        }
+
+        if (groupLinkState != null) {
+            var groupGroupChangePair = groupV2Helper.setGroupLinkState(group, groupLinkState);
+            result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+        }
+
+        if (addMemberPermission != null) {
+            var groupGroupChangePair = groupV2Helper.setAddMemberPermission(group, addMemberPermission);
+            result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+        }
+
+        if (editDetailsPermission != null) {
+            var groupGroupChangePair = groupV2Helper.setEditDetailsPermission(group, editDetailsPermission);
+            result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+        }
+
+        if (expirationTimer != null) {
+            var groupGroupChangePair = groupV2Helper.setMessageExpirationTimer(group, expirationTimer);
+            result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+        }
+
+        if (name != null || description != null || avatarFile != null) {
+            var groupGroupChangePair = groupV2Helper.updateGroup(group, name, description, avatarFile);
             if (avatarFile != null) {
                 avatarStore.storeGroupAvatar(group.getGroupId(),
                         outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream));
@@ -962,9 +1075,9 @@ public class Manager implements Closeable {
     public Pair<GroupId, List<SendMessageResult>> joinGroup(
             GroupInviteLinkUrl inviteLinkUrl
     ) throws IOException, GroupLinkNotActiveException {
-        final var groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(),
+        final var groupJoinInfo = groupV2Helper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(),
                 inviteLinkUrl.getPassword());
-        final var groupChange = groupHelper.joinGroup(inviteLinkUrl.getGroupMasterKey(),
+        final var groupChange = groupV2Helper.joinGroup(inviteLinkUrl.getGroupMasterKey(),
                 inviteLinkUrl.getPassword(),
                 groupJoinInfo);
         final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(),
@@ -1243,15 +1356,17 @@ public class Manager implements Closeable {
     /**
      * Change the expiration timer for a group
      */
-    public void setExpirationTimer(GroupId groupId, int messageExpirationTimer) {
-        var g = getGroup(groupId);
-        if (g instanceof GroupInfoV1) {
-            var groupInfoV1 = (GroupInfoV1) g;
-            groupInfoV1.messageExpirationTime = messageExpirationTimer;
-            account.getGroupStore().updateGroup(groupInfoV1);
-        } else {
-            throw new RuntimeException("TODO Not implemented!");
-        }
+    private void setExpirationTimer(
+            GroupInfoV1 groupInfoV1, int messageExpirationTimer
+    ) throws NotAGroupMemberException, GroupNotFoundException, IOException {
+        groupInfoV1.messageExpirationTime = messageExpirationTimer;
+        account.getGroupStore().updateGroup(groupInfoV1);
+        sendExpirationTimerUpdate(groupInfoV1.getGroupId());
+    }
+
+    private void sendExpirationTimerUpdate(GroupIdV1 groupId) throws IOException, NotAGroupMemberException, GroupNotFoundException {
+        final var messageBuilder = SignalServiceDataMessage.newBuilder().asExpirationUpdate();
+        sendGroupMessage(messageBuilder, groupId);
     }
 
     /**
@@ -1762,10 +1877,12 @@ public class Manager implements Closeable {
             if (signedGroupChange != null
                     && groupInfoV2.getGroup() != null
                     && groupInfoV2.getGroup().getRevision() + 1 == revision) {
-                group = groupHelper.getUpdatedDecryptedGroup(groupInfoV2.getGroup(), signedGroupChange, groupMasterKey);
+                group = groupV2Helper.getUpdatedDecryptedGroup(groupInfoV2.getGroup(),
+                        signedGroupChange,
+                        groupMasterKey);
             }
             if (group == null) {
-                group = groupHelper.getDecryptedGroup(groupSecretParams);
+                group = groupV2Helper.getDecryptedGroup(groupSecretParams);
             }
             if (group != null) {
                 storeProfileKeysFromMembers(group);
@@ -2577,7 +2694,7 @@ public class Manager implements Closeable {
         final var group = account.getGroupStore().getGroup(groupId);
         if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() == null) {
             final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(((GroupInfoV2) group).getMasterKey());
-            ((GroupInfoV2) group).setGroup(groupHelper.getDecryptedGroup(groupSecretParams), this::resolveRecipient);
+            ((GroupInfoV2) group).setGroup(groupV2Helper.getDecryptedGroup(groupSecretParams), this::resolveRecipient);
             account.getGroupStore().updateGroup(group);
         }
         return group;