diff --git a/man/signal-cli-dbus.5.adoc b/man/signal-cli-dbus.5.adoc index 60ffa64a..26f66aba 100755 --- a/man/signal-cli-dbus.5.adoc +++ b/man/signal-cli-dbus.5.adoc @@ -45,21 +45,22 @@ Phone numbers always have the format + updateGroup(groupId, name, addMembers, avatar) -> groupId:: updateGroup(base64GroupId, name, addMembers, avatar) -> base64GroupId:: +updateGroup(groupId, name, description, addMembers, removeMembers, addAdmins, removeAdmins, resetGroupLink, groupLinkState, addMemberPermission, editDetailsPermission, avatar expiration) -> groupId:: updateGroup(base64GroupId, name, description, addMembers, removeMembers, addAdmins, removeAdmins, resetGroupLink, groupLinkState, addMemberPermission, editDetailsPermission, avatar expiration) -> base64GroupId:: -* groupId : Byte array representing the internal group identifier -* base64GroupId : String representing the internal group identifier in Base64 format +* groupId : Byte array representing the internal group identifier (create new group if null) +* base64GroupId : String representing the internal group identifier in Base64 format (create new group if empty) * name : Name of group (empty if unchanged) * description : Description (empty if unchanged) * addMembers : String array of new members to be invited to group (empty if unchanged) * removeMembers : String array of members to be removed from group (empty if unchanged) * addAdmins : String array of members granted admin privileges (empty if unchanged) * removeAdmins : String array of members denied admin privileges (empty if unchanged) -* resetGroupLink : Boolean (true = change the group link, false or empty = don't change) +* resetGroupLink : Boolean (true = change the group link, false = don't change) * groupLinkState : String ("enabled", "enabled-with-approval", "disabled") (empty if unchanged) * addMemberPermission : String of who may add members ("every-member", "only-admins") (empty if unchanged) * editDetailsPermission : String of who may edit group details ("every-member", "only-admins") (empty if unchanged) * avatar : Filename of avatar picture to be set for group (send the name of an empty file to delete avatar; leave field empty if avatar is unchanged) -* expiration : Expiration time for messages sent to this number (in seconds). Set to 0 to disable. (empty if unchanged) +* expiration : Expiration time for messages sent to this group (in seconds). Set to 0 to disable. (empty if unchanged) Exceptions: AttachmentInvalid, Failure, InvalidNumber, GroupNotFound @@ -93,8 +94,8 @@ Messages from blocked groups will no longer be forwarded via DBus. Exceptions: GroupNotFound -joinGroup(inviteURI) -> <>:: -* inviteURI : String starting with https://signal.group which is generated when you share a group link via Signal App +joinGroup(inviteUri) -> <>:: +* inviteUri : String starting with https://signal.group which is generated when you share a group link via Signal App Exceptions: Failure @@ -113,7 +114,7 @@ isMember(base64GroupId) -> active:: * base64GroupId : String representing the internal group identifier in Base64 format * active : Boolean representing whether you are a member of the group -Exceptions: None; returns false for a non-existing/unknown group +Exceptions: GroupNotFound sendEndSessionMessage(recipients) -> <>:: * recipients : String array of phone numbers @@ -209,9 +210,9 @@ Exception: Failure trust(number, safetyNumber) -> <>:: * number : Phone number -* safetyNumber : Verify the safety number associated with the phone number. +* safetyNumber : Verify the safety number associated with the phone number. -Exceptions: Failure, InvalidNumber; +Exceptions: Failure, InvalidNumber sendTyping(typingAction, base64GroupId, recipients) -> <>:: * typingAction : true = start typing, false = stop typing @@ -263,15 +264,47 @@ groupName : The display name of the group groupId : Byte array representing the internal group identifier base64GroupId : String representing the internal group identifier in Base64 format -Exceptions: None; if the group name is not found an empty string is returned +Exception: Failure if base64GroupId or groupId is malformed; GroupNotFound getGroupMembers(groupId) -> members:: getGroupMembers(base64GroupId) -> members:: -members : String array with the phone numbers of all active members of a group -groupId : Byte array representing the internal group identifier -base64GroupId : String representing the internal group identifier in Base64 format +* members : String array with the phone numbers of all active members of a group +* groupId : Byte array representing the internal group identifier +* base64GroupId : String representing the internal group identifier in Base64 format -Exceptions: None; if the group name is not found an empty array is returned +Exception: Failure if base64GroupId or groupId is malformed; GroupNotFound + +getGroupAdminMembers(groupId) -> adminMembers:: +getGroupAdminMembers(base64GroupId) -> adminMembers:: +* groupId : Byte array representing the internal group identifier +* base64GroupId : String representing the internal group identifier in Base64 format +* adminMembers : String array of members granted admin privileges + +Exception: Failure if base64GroupId or groupId is malformed; GroupNotFound + +getGroupPendingMembers(groupId) -> pendingMembers:: +getGroupPendingMembers(base64GroupId) -> pendingMembers:: +* groupId : Byte array representing the internal group identifier +* base64GroupId : String representing the internal group identifier in Base64 format +* pendingMembers : String array of pending members + +Exception: Failure if base64GroupId or groupId is malformed; GroupNotFound + +getGroupRequestingMembers(groupId) -> requestingMembers:: +getGroupRequestingMembers(base64GroupId) -> requestingMembers:: +* groupId : Byte array representing the internal group identifier +* base64GroupId : String representing the internal group identifier in Base64 format +* requestingMembers : String array of requesting members (awaiting admin approval for membership) + +Exception: Failure if base64GroupId or groupId is malformed; GroupNotFound + +getGroupInviteUri(groupId) -> inviteUri:: +getGroupInviteUri(base64GroupId) -> inviteUri:: +* groupId : Byte array representing the internal group identifier +* base64GroupId : String representing the internal group identifier in Base64 format +* inviteUri : String starting with https://signal.group which is generated when you share a group link via Signal App + +Exceptions: Failure if base64GroupId or groupId is malformed; GroupNotFound listNumbers() -> numbers:: numbers : String array of all known numbers @@ -300,7 +333,7 @@ isGroupBlocked(base64GroupId) -> state:: * base64GroupId : String representing the internal group identifier in Base64 format * state : true=blocked, false=not blocked -Exceptions: None; for unknown groups false is returned +Exceptions: Failure if base64GroupId or groupId is malformed; GroupNotFound isRegistered(number) -> result:: isRegistered(numbers) -> results:: diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 36c08424..b025066e 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -86,7 +86,8 @@ public interface Signal extends DBusInterface { void trust(String number, String safetyNumber) throws Error.Failure, Error.InvalidNumber; - void sendTyping(boolean typingAction, String base64GroupId, Listrecipients) throws Error.Failure, Error.UntrustedIdentity; + void sendTyping(boolean typingAction, String base64GroupId, Listrecipients) throws Error.Failure, Error.UntrustedIdentity, Error.GroupNotFound; + void sendTyping(boolean typingAction, byte[] groupId, Listrecipients) throws Error.Failure, Error.UntrustedIdentity, Error.GroupNotFound; String getContactName(String number) throws Error.InvalidNumber; @@ -97,17 +98,56 @@ public interface Signal extends DBusInterface { void setContactBlocked(String number, boolean blocked) throws Error.InvalidNumber; void setGroupBlocked(byte[] groupId, boolean blocked) throws Error.GroupNotFound; + void setGroupBlocked(String base64GroupId, boolean blocked) throws Error.GroupNotFound; List getGroupIds(); + // To get the group Ids in base 64 format, either use the getBaseGroupIds() method, or + // the getGroupIds(dummy) form, where dummy represents any string + List getBase64GroupIds(); + List getGroupIds(String dummy); - String getGroupName(byte[] groupId); + String getGroupName(byte[] groupId) throws Error.GroupNotFound, Error.Failure; + String getGroupName(String base64GroupId) throws Error.GroupNotFound, Error.Failure; - List getGroupMembers(byte[] groupId); + List getGroupMembers(byte[] groupId) throws Error.GroupNotFound, Error.Failure; + List getGroupMembers(String base64GroupId) throws Error.GroupNotFound, Error.Failure; + + List getGroupAdminMembers(byte[] groupId) throws Error.GroupNotFound, Error.Failure; + List getGroupAdminMembers(String base64GroupId) throws Error.GroupNotFound, Error.Failure; + + List getGroupPendingMembers(byte[] groupId) throws Error.GroupNotFound, Error.Failure; + List getGroupPendingMembers(String base64GroupId) throws Error.GroupNotFound, Error.Failure; + + List getGroupRequestingMembers(byte[] groupId) throws Error.GroupNotFound, Error.Failure; + List getGroupRequestingMembers(String base64GroupId) throws Error.GroupNotFound, Error.Failure; + + String getGroupInviteUri(byte[] groupId) throws Error.GroupNotFound, Error.Failure; + String getGroupInviteUri(String base64GroupId) throws Error.GroupNotFound, Error.Failure; byte[] updateGroup( byte[] groupId, String name, List members, String avatar ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound; + String updateGroup( + String base64GroupId, String name, List members, String avatar + ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound; + + byte[] updateGroup( + byte[] groupId, + String name, + String description, + List addMembers, + List removeMembers, + List addAdmins, + List removeAdmins, + boolean resetGroupLink, + String groupLinkState, + String addMemberPermission, + String editDetailsPermission, + String avatar, + Integer expirationTimer + ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound; + String updateGroup( String base64GroupId, String name, @@ -124,17 +164,6 @@ public interface Signal extends DBusInterface { Integer expirationTimer ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound; - String getGroupName(String base64GroupId); - - // To get the group Ids in base 64 format, either use the getBaseGroupIds() method, or - // the getGroupIds(dummy) form, where dummy represents any string - List getBase64GroupIds(); - List getGroupIds(String dummy); - - void setGroupBlocked(String base64GroupId, boolean blocked) throws Error.GroupNotFound; - - List getGroupMembers(String base64GroupId); - long sendGroupMessage( String message, List attachments, String base64GroupId ) throws Error.GroupNotFound, Error.Failure, Error.AttachmentInvalid; @@ -143,10 +172,6 @@ public interface Signal extends DBusInterface { String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, String base64GroupId ) throws Error.GroupNotFound, Error.Failure, Error.InvalidNumber; - String updateGroup( - String base64GroupId, String name, List members, String avatar - ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound; - boolean isRegistered(String number) throws Error.Failure, Error.InvalidNumber; List isRegistered(List numbers) throws Error.Failure, Error.InvalidNumber; @@ -201,11 +226,15 @@ public interface Signal extends DBusInterface { void quitGroup(final byte[] groupId) throws Error.GroupNotFound, Error.Failure; + void quitGroup(final String base64GroupId) throws Error.GroupNotFound, Error.Failure; + boolean isContactBlocked(final String number) throws Error.InvalidNumber; - boolean isGroupBlocked(final byte[] groupId); + boolean isGroupBlocked(final byte[] groupId) throws Error.GroupNotFound, Error.Failure; + boolean isGroupBlocked(final String base64GroupId) throws Error.GroupNotFound, Error.Failure; - boolean isMember(final byte[] groupId); + boolean isMember(final byte[] groupId) throws Error.GroupNotFound, Error.Failure; + boolean isMember(final String base64GroupId) throws Error.GroupNotFound, Error.Failure; void joinGroup(final String groupLink) throws Error.Failure; diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 99bd7ed2..1e68159e 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -30,6 +30,7 @@ import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupPermission; import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; +import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.identities.IdentityInfo; import org.asamk.signal.util.DateUtils; import org.asamk.signal.util.ErrorUtils; @@ -226,7 +227,17 @@ public class DbusSignalImpl implements Signal { final long targetSentTimestamp, final byte[] groupId ) { try { - final var results = m.sendGroupRemoteDeleteMessage(targetSentTimestamp, GroupId.unknownVersion(groupId)); + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + + final var results = m.sendGroupRemoteDeleteMessage(targetSentTimestamp, group.getGroupId()); checkSendMessageResults(results.first(), results.second()); return results.first(); } catch (IOException e) { @@ -298,7 +309,17 @@ public class DbusSignalImpl implements Signal { @Override public long sendGroupMessage(final String message, final List attachments, final byte[] groupId) { try { - var results = m.sendGroupMessage(message, attachments, GroupId.unknownVersion(groupId)); + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + + var results = m.sendGroupMessage(message, attachments, group.getGroupId()); checkSendMessageResults(results.first(), results.second()); return results.first(); } catch (IOException e) { @@ -311,8 +332,12 @@ public class DbusSignalImpl implements Signal { } @Override public long sendGroupMessage(final String message, final List attachments, final String base64GroupId) { - byte[] groupId = Base64.getDecoder().decode(base64GroupId); - return sendGroupMessage(message, attachments, groupId); + try { + byte[] groupId = GroupId.fromBase64(base64GroupId).serialize(); + return sendGroupMessage(message, attachments, groupId); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Invalid group id format: " + e.getMessage()); + } } @Override @@ -324,11 +349,21 @@ public class DbusSignalImpl implements Signal { final byte[] groupId ) { try { + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + final var results = m.sendGroupMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, - GroupId.unknownVersion(groupId)); + group.getGroupId()); checkSendMessageResults(results.first(), results.second()); return results.first(); } catch (IOException e) { @@ -348,8 +383,12 @@ public class DbusSignalImpl implements Signal { final long targetSentTimestamp, final String base64GroupId ) { - byte[] groupId = Base64.getDecoder().decode(base64GroupId); - return sendGroupMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, groupId); + try { + byte[] groupId = GroupId.fromBase64(base64GroupId).serialize(); + return sendGroupMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, groupId); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Invalid group id format: " + e.getMessage()); + } } @Override @@ -454,6 +493,12 @@ public class DbusSignalImpl implements Signal { } } + @Override + public void sendTyping(boolean typingAction, byte[] groupId, Listrecipients) { + String base64GroupId = Base64.getEncoder().encodeToString(groupId); + sendTyping(typingAction, base64GroupId, recipients); + } + // Since contact names might be empty if not defined, also potentially return // the profile name @Override @@ -500,8 +545,18 @@ public class DbusSignalImpl implements Signal { @Override public void setGroupBlocked(final byte[] groupId, final boolean blocked) { + GroupInfo group = null; try { - m.setGroupBlocked(GroupId.unknownVersion(groupId), blocked); + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + + try { + m.setGroupBlocked(group.getGroupId(), blocked); } catch (GroupNotFoundException e) { throw new Error.GroupNotFound(e.getMessage()); } @@ -509,8 +564,12 @@ public class DbusSignalImpl implements Signal { @Override public void setGroupBlocked(final String base64GroupId, final boolean blocked) { - byte[] groupId = Base64.getDecoder().decode(base64GroupId); - setGroupBlocked(groupId, blocked); + try { + byte[] groupId = GroupId.fromBase64(base64GroupId).serialize(); + setGroupBlocked(groupId, blocked); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Invalid group id format: " + e.getMessage()); + } } @Override @@ -525,9 +584,14 @@ public class DbusSignalImpl implements Signal { @Override public String getGroupName(final byte[] groupId) { - var group = m.getGroup(GroupId.unknownVersion(groupId)); - if (group == null) { - return ""; + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); } else { return group.getTitle(); } @@ -535,9 +599,14 @@ public class DbusSignalImpl implements Signal { @Override public List getGroupMembers(final byte[] groupId) { - var group = m.getGroup(GroupId.unknownVersion(groupId)); - if (group == null) { - return List.of(); + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); } else { return group.getMembers() .stream() @@ -571,10 +640,22 @@ public class DbusSignalImpl implements Signal { @Override public String getGroupName(final String base64GroupId) { - byte[] groupId = Base64.getDecoder().decode(base64GroupId); - var group = m.getGroup(GroupId.unknownVersion(groupId)); - if (group == null) { - return ""; + byte[] groupId = null; + GroupInfo group = null; + try { + groupId = GroupId.fromBase64(base64GroupId).serialize(); + } catch (GroupIdFormatException e) { + throw new Error.Failure(e.getMessage()); + } + + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); } else { return group.getTitle(); } @@ -582,10 +663,20 @@ public class DbusSignalImpl implements Signal { @Override public List getGroupMembers(final String base64GroupId) { - byte[] groupId = Base64.getDecoder().decode(base64GroupId); - var group = m.getGroup(GroupId.unknownVersion(groupId)); - if (group == null) { - return List.of(); + byte[] groupId = null; + try { + groupId = GroupId.fromBase64(base64GroupId).serialize(); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Invalid group id format: " + e.getMessage()); + } + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); } else { return group.getMembers() .stream() @@ -615,7 +706,17 @@ public class DbusSignalImpl implements Signal { checkSendMessageResults(0, results.second()); return results.first().serialize(); } else { - final var results = m.updateGroup(GroupId.unknownVersion(groupId), + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + + final var results = m.updateGroup(group.getGroupId(), name, null, members, @@ -646,14 +747,22 @@ public class DbusSignalImpl implements Signal { @Override public String updateGroup(String base64GroupId, String name, List members, String avatar) { - byte[] groupId = Base64.getDecoder().decode(base64GroupId); + byte[] groupId = null; + if (!base64GroupId.isEmpty()) { + try { + groupId = GroupId.fromBase64(base64GroupId).serialize(); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Invalid group id format: " + e.getMessage()); + } + } groupId = updateGroup(groupId, name, members, avatar); + return Base64.getEncoder().encodeToString(groupId); } @Override - public String updateGroup( - String base64GroupId, + public byte[] updateGroup( + byte[] groupId, String name, String description, List addMembers, @@ -668,13 +777,7 @@ public class DbusSignalImpl implements Signal { Integer expirationTimer ) { try { - byte[] groupId = null; File avatarFile = null; - if (base64GroupId.isEmpty()) { - throw new Error.GroupNotFound("No group specified."); - } else { - groupId = Base64.getDecoder().decode(base64GroupId); - } if (name.isEmpty()) { name = null; } @@ -711,7 +814,17 @@ public class DbusSignalImpl implements Signal { long fileSize = avatarFile.length(); if (fileSize == 0) { try { - AvatarStore.deleteGroupAvatar(GroupId.unknownVersion(groupId)); + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError ae) { + throw new Error.Failure(ae.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + + AvatarStore.deleteGroupAvatar(group.getGroupId()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); } @@ -720,9 +833,19 @@ public class DbusSignalImpl implements Signal { if (groupId == null) { final var results = m.createGroup(name, addMembers, avatar == null ? null : new File(avatar)); checkSendMessageResults(0, results.second()); - return Base64.getEncoder().encodeToString(results.first().serialize()); + return results.first().serialize(); } else { - final var results = m.updateGroup(GroupId.unknownVersion(groupId), + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + + final var results = m.updateGroup(group.getGroupId(), name, description, addMembers, @@ -739,7 +862,7 @@ public class DbusSignalImpl implements Signal { if (results != null) { checkSendMessageResults(results.first(), results.second()); } - return base64GroupId; + return groupId; } } catch (UserErrorException | IOException e) { throw new Error.Failure(e.getMessage()); @@ -752,6 +875,216 @@ public class DbusSignalImpl implements Signal { } } + @Override + public String updateGroup( + String base64GroupId, + String name, + String description, + List addMembers, + List removeMembers, + List addAdmins, + List removeAdmins, + boolean resetGroupLink, + String groupLinkState, + String addMemberPermission, + String editDetailsPermission, + String avatar, + Integer expirationTimer + ) { + byte[] groupId = null; + if (!base64GroupId.isEmpty()) { + try { + groupId = GroupId.fromBase64(base64GroupId).serialize(); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Invalid group id format: " + e.getMessage()); + } + } + groupId = updateGroup( + groupId, + name, + description, + addMembers, + removeMembers, + addAdmins, + removeAdmins, + resetGroupLink, + groupLinkState, + addMemberPermission, + editDetailsPermission, + avatar, + expirationTimer + ); + return Base64.getEncoder().encodeToString(groupId); + } + + @Override + public String getGroupInviteUri(byte[] groupId) { + GroupInfo group = null; + GroupInviteLinkUrl groupInviteUri = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + groupInviteUri = group.getGroupInviteLink(); + if (groupInviteUri == null) { + return ""; + } + return groupInviteUri.getUrl(); + } + + @Override + public String getGroupInviteUri(String base64GroupId) { + GroupInfo group = null; + try { + group = m.getGroup(GroupId.fromBase64(base64GroupId)); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Incorrect format: " + e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + GroupInviteLinkUrl groupInviteUri = group.getGroupInviteLink(); + return groupInviteUri.getUrl(); + } + + @Override + public List getGroupPendingMembers(final byte[] groupId) { + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } else { + return group.getPendingMembers() + .stream() + .map(m::resolveSignalServiceAddress) + .map(Util::getLegacyIdentifier) + .collect(Collectors.toList()); + } + } + + @Override + public List getGroupPendingMembers(final String base64GroupId) { + byte [] groupId = null; + try { + groupId = GroupId.fromBase64(base64GroupId).serialize(); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Incorrect format: " + e.getMessage()); + } + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } else { + return group.getPendingMembers() + .stream() + .map(m::resolveSignalServiceAddress) + .map(Util::getLegacyIdentifier) + .collect(Collectors.toList()); + } + } + + @Override + public List getGroupRequestingMembers(final byte[] groupId) { + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } else { + return group.getRequestingMembers() + .stream() + .map(m::resolveSignalServiceAddress) + .map(Util::getLegacyIdentifier) + .collect(Collectors.toList()); + } + } + + @Override + public List getGroupRequestingMembers(final String base64GroupId) { + byte [] groupId = null; + try { + groupId = GroupId.fromBase64(base64GroupId).serialize(); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Incorrect format: " + e.getMessage()); + } + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } else { + return group.getRequestingMembers() + .stream() + .map(m::resolveSignalServiceAddress) + .map(Util::getLegacyIdentifier) + .collect(Collectors.toList()); + } + } + + @Override + public List getGroupAdminMembers(final byte[] groupId) { + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } else { + return group.getAdminMembers() + .stream() + .map(m::resolveSignalServiceAddress) + .map(Util::getLegacyIdentifier) + .collect(Collectors.toList()); + } + } + + @Override + public List getGroupAdminMembers(final String base64GroupId) { + byte[] groupId = null; + try { + groupId = GroupId.fromBase64(base64GroupId).serialize(); + } catch (GroupIdFormatException e) { + throw new Error.Failure("base64GroupId" + " is not a valid Base 64 string " + e.getMessage()); + } + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } else { + return group.getAdminMembers() + .stream() + .map(m::resolveSignalServiceAddress) + .map(Util::getLegacyIdentifier) + .collect(Collectors.toList()); + } + } + + + @Override public boolean isRegistered(String number) { if (number.isEmpty()) { @@ -968,9 +1301,45 @@ public class DbusSignalImpl implements Signal { @Override public void quitGroup(final byte[] groupId) { - var group = GroupId.unknownVersion(groupId); + GroupInfo group = null; try { - m.sendQuitGroupMessage(group, Set.of()); + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + try { + m.sendQuitGroupMessage(group.getGroupId(), Set.of()); + } catch (GroupNotFoundException | NotAGroupMemberException e) { + throw new Error.GroupNotFound(e.getMessage()); + } catch (IOException | LastGroupAdminException e) { + throw new Error.Failure(e.getMessage()); + } catch (InvalidNumberException e) { + throw new Error.InvalidNumber(e.getMessage()); + } + } + + @Override + public void quitGroup(final String base64GroupId) { + byte [] groupId = null; + try { + groupId = GroupId.fromBase64(base64GroupId).serialize(); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Incorrect format: " + e.getMessage()); + } + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); + } + try { + m.sendQuitGroupMessage(group.getGroupId(), Set.of()); } catch (GroupNotFoundException | NotAGroupMemberException e) { throw new Error.GroupNotFound(e.getMessage()); } catch (IOException | LastGroupAdminException e) { @@ -1008,9 +1377,35 @@ public class DbusSignalImpl implements Signal { @Override public boolean isGroupBlocked(final byte[] groupId) { - var group = m.getGroup(GroupId.unknownVersion(groupId)); + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } if (group == null) { - return false; + throw new Error.GroupNotFound("Error finding group"); + } else { + return group.isBlocked(); + } + } + + @Override + public boolean isGroupBlocked(final String base64GroupId) { + GroupInfo group = null; + byte [] groupId = null; + try { + groupId = GroupId.fromBase64(base64GroupId).serialize(); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Incorrect format: " + e.getMessage()); + } + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); } else { return group.isBlocked(); } @@ -1018,9 +1413,35 @@ public class DbusSignalImpl implements Signal { @Override public boolean isMember(final byte[] groupId) { - var group = m.getGroup(GroupId.unknownVersion(groupId)); + GroupInfo group = null; + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } if (group == null) { - return false; + throw new Error.GroupNotFound("Error finding group"); + } else { + return group.isMember(m.getSelfRecipientId()); + } + } + + @Override + public boolean isMember(final String base64GroupId) { + GroupInfo group = null; + byte[] groupId = null; + try { + groupId = GroupId.fromBase64(base64GroupId).serialize(); + } catch (GroupIdFormatException e) { + throw new Error.Failure("Incorrect format: " + e.getMessage()); + } + try { + group = m.getGroup(GroupId.unknownVersion(groupId)); + } catch (AssertionError e) { + throw new Error.Failure(e.getMessage()); + } + if (group == null) { + throw new Error.GroupNotFound("Error finding group"); } else { return group.isMember(m.getSelfRecipientId()); }