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.GroupUtils;
import org.asamk.signal.manager.groups.NotAGroupMemberException;
List<String> removeMembers,
List<String> admins,
List<String> removeAdmins,
+ boolean resetGroupLink,
+ GroupLinkState groupLinkState,
File avatarFile
) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException {
return updateGroup(groupId,
removeMembers == null ? null : getSignalServiceAddresses(removeMembers),
admins == null ? null : getSignalServiceAddresses(admins),
removeAdmins == null ? null : getSignalServiceAddresses(removeAdmins),
+ resetGroupLink,
+ groupLinkState,
avatarFile);
}
final Set<RecipientId> removeMembers,
final Set<RecipientId> admins,
final Set<RecipientId> removeAdmins,
+ final boolean resetGroupLink,
+ final GroupLinkState groupLinkState,
final File avatarFile
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
var group = getGroupForUpdating(groupId);
removeMembers,
admins,
removeAdmins,
+ resetGroupLink,
+ groupLinkState,
avatarFile);
}
final Set<RecipientId> removeMembers,
final Set<RecipientId> admins,
final Set<RecipientId> removeAdmins,
+ final boolean resetGroupLink,
+ final GroupLinkState groupLinkState,
final File avatarFile
) throws IOException {
Pair<Long, List<SendMessageResult>> result = null;
}
}
+ 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 (result == null || name != null || description != null || avatarFile != null) {
var groupGroupChangePair = groupV2Helper.updateGroup(group, name, description, avatarFile);
if (avatarFile != null) {
--- /dev/null
+package org.asamk.signal.manager.groups;
+
+public enum GroupLinkState {
+ ENABLED,
+ ENABLED_WITH_APPROVAL,
+ DISABLED,
+}
import com.google.protobuf.InvalidProtocolBufferException;
import org.asamk.signal.manager.groups.GroupLinkPassword;
+import org.asamk.signal.manager.groups.GroupLinkState;
import org.asamk.signal.manager.groups.GroupUtils;
import org.asamk.signal.manager.storage.groups.GroupInfoV2;
import org.asamk.signal.manager.storage.recipients.Profile;
return revokeInvites(groupInfoV2, memberUuids);
}
+ public Pair<DecryptedGroup, GroupChange> resetGroupLinkPassword(GroupInfoV2 groupInfoV2) throws IOException {
+ final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
+ final var newGroupLinkPassword = GroupLinkPassword.createNew().serialize();
+ final var change = groupOperations.createModifyGroupLinkPasswordChange(newGroupLinkPassword);
+ return commitChange(groupInfoV2, change);
+ }
+
+ public Pair<DecryptedGroup, GroupChange> setGroupLinkState(
+ GroupInfoV2 groupInfoV2, GroupLinkState state
+ ) throws IOException {
+ final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
+
+ final var accessRequired = toAccessControl(state);
+ final var requiresNewPassword = state != GroupLinkState.DISABLED && groupInfoV2.getGroup()
+ .getInviteLinkPassword()
+ .isEmpty();
+
+ final var change = requiresNewPassword ? groupOperations.createModifyGroupLinkPasswordAndRightsChange(
+ GroupLinkPassword.createNew().serialize(),
+ accessRequired) : groupOperations.createChangeJoinByLinkRights(accessRequired);
+ return commitChange(groupInfoV2, change);
+ }
+
public GroupChange joinGroup(
GroupMasterKey groupMasterKey,
GroupLinkPassword groupLinkPassword,
return commitChange(groupInfoV2, change);
}
+ private AccessControl.AccessRequired toAccessControl(final GroupLinkState state) {
+ switch (state) {
+ case DISABLED:
+ return AccessControl.AccessRequired.UNSATISFIABLE;
+ case ENABLED:
+ return AccessControl.AccessRequired.ANY;
+ case ENABLED_WITH_APPROVAL:
+ return AccessControl.AccessRequired.ADMINISTRATOR;
+ default:
+ throw new AssertionError();
+ }
+ }
+
private GroupsV2Operations.GroupOperations getGroupOperations(final GroupInfoV2 groupInfoV2) {
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
return groupsV2Operations.forGroup(groupSecretParams);
@Override
public GroupInviteLinkUrl getGroupInviteLink() {
- if (this.group == null || this.group.getInviteLinkPassword() == null || (
+ if (this.group == null || this.group.getInviteLinkPassword().isEmpty() || (
this.group.getAccessControl().getAddFromInviteLink() != AccessControl.AccessRequired.ANY
&& this.group.getAccessControl().getAddFromInviteLink()
!= AccessControl.AccessRequired.ADMINISTRATOR
--- /dev/null
+package org.asamk.signal;
+
+public enum GroupLinkState {
+ ENABLED {
+ @Override
+ public String toString() {
+ return "enabled";
+ }
+ },
+ ENABLED_WITH_APPROVAL {
+ @Override
+ public String toString() {
+ return "enabled-with-approval";
+ }
+ },
+ DISABLED {
+ @Override
+ public String toString() {
+ return "disabled";
+ }
+ };
+
+ public org.asamk.signal.manager.groups.GroupLinkState toLinkState() {
+ switch (this) {
+ case ENABLED:
+ return org.asamk.signal.manager.groups.GroupLinkState.ENABLED;
+ case ENABLED_WITH_APPROVAL:
+ return org.asamk.signal.manager.groups.GroupLinkState.ENABLED_WITH_APPROVAL;
+ case DISABLED:
+ return org.asamk.signal.manager.groups.GroupLinkState.DISABLED;
+ default:
+ throw new AssertionError();
+ }
+ }
+}
package org.asamk.signal.commands;
+import net.sourceforge.argparse4j.impl.Arguments;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
import org.asamk.Signal;
+import org.asamk.signal.GroupLinkState;
import org.asamk.signal.PlainTextWriterImpl;
import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.commands.exceptions.UnexpectedErrorException;
.nargs("*")
.help("Specify one or more members to remove group admin privileges");
+ subparser.addArgument("--reset-link")
+ .action(Arguments.storeTrue())
+ .help("Reset group link and create new link password");
+ subparser.addArgument("--link")
+ .help("Set group link state, with or without admin approval")
+ .type(Arguments.enumStringType(GroupLinkState.class));
}
@Override
var groupDescription = ns.getString("description");
- List<String> groupMembers = ns.getList("member");
+ var groupMembers = ns.<String>getList("member");
- List<String> groupRemoveMembers = ns.getList("remove-member");
+ var groupRemoveMembers = ns.<String>getList("remove-member");
- List<String> groupAdmins = ns.getList("admin");
+ var groupAdmins = ns.<String>getList("admin");
- List<String> groupRemoveAdmins = ns.getList("remove-admin");
+ var groupRemoveAdmins = ns.<String>getList("remove-admin");
var groupAvatar = ns.getString("avatar");
+ var groupResetLink = ns.getBoolean("reset-link");
+
+ var groupLinkState = ns.<GroupLinkState>get("link");
+
try {
if (groupId == null) {
var results = m.createGroup(groupName,
groupRemoveMembers,
groupAdmins,
groupRemoveAdmins,
+ groupResetLink,
+ groupLinkState != null ? groupLinkState.toLinkState() : null,
groupAvatar == null ? null : new File(groupAvatar));
ErrorUtils.handleTimestampAndSendMessageResults(writer, results.first(), results.second());
}
null,
null,
null,
+ false,
+ null,
avatar == null ? null : new File(avatar));
checkSendMessageResults(results.first(), results.second());
return groupId;