X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/17608ce5220142b979f777cbd1512416043f52bb..445e8592c491438e3ea863c2382ea42b1161fe84:/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java 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 1f7e69e3..d66831bb 100644 --- a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -2,12 +2,15 @@ package org.asamk.signal.manager.helper; import com.google.protobuf.InvalidProtocolBufferException; +import org.asamk.signal.manager.GroupLinkPassword; import org.asamk.signal.storage.groups.GroupInfoV2; import org.asamk.signal.util.IOUtils; +import org.signal.storageservice.protos.groups.AccessControl; 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.DecryptedGroupJoinInfo; import org.signal.storageservice.protos.groups.local.DecryptedPendingMember; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.VerificationFailedException; @@ -19,6 +22,7 @@ import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil; import org.whispersystems.signalservice.api.groupsv2.GroupCandidate; +import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api; import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; @@ -66,6 +70,27 @@ public class GroupHelper { this.groupAuthorizationProvider = groupAuthorizationProvider; } + public DecryptedGroup getDecryptedGroup(final GroupSecretParams groupSecretParams) { + try { + final GroupsV2AuthorizationString groupsV2AuthorizationString = groupAuthorizationProvider.getAuthorizationForToday( + groupSecretParams); + return groupsV2Api.getGroup(groupSecretParams, groupsV2AuthorizationString); + } catch (IOException | VerificationFailedException | InvalidGroupStateException e) { + System.err.println("Failed to retrieve Group V2 info, ignoring ..."); + return null; + } + } + + public DecryptedGroupJoinInfo getDecryptedGroupJoinInfo( + GroupMasterKey groupMasterKey, GroupLinkPassword password + ) throws IOException, GroupLinkNotActiveException { + GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); + + return groupsV2Api.getGroupJoinInfo(groupSecretParams, + Optional.fromNullable(password).transform(GroupLinkPassword::serialize), + groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams)); + } + public GroupInfoV2 createGroupV2( String name, Collection members, String avatarFile ) throws IOException { @@ -223,6 +248,32 @@ public class GroupHelper { } } + public GroupChange joinGroup( + GroupMasterKey groupMasterKey, + GroupLinkPassword groupLinkPassword, + DecryptedGroupJoinInfo decryptedGroupJoinInfo + ) throws IOException { + final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); + final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); + + final SignalServiceAddress selfAddress = this.selfAddressProvider.getSelfAddress(); + final ProfileKeyCredential profileKeyCredential = profileKeyCredentialProvider.getProfileKeyCredential( + selfAddress); + if (profileKeyCredential == null) { + throw new IOException("Cannot join a V2 group as self does not have a versioned profile"); + } + + boolean requestToJoin = decryptedGroupJoinInfo.getAddFromInviteLink() + == AccessControl.AccessRequired.ADMINISTRATOR; + GroupChange.Actions.Builder change = requestToJoin + ? groupOperations.createGroupJoinRequest(profileKeyCredential) + : groupOperations.createGroupJoinDirect(profileKeyCredential); + + change.setSourceUuid(UuidUtil.toByteString(selfAddress.getUuid().get())); + + return commitChange(groupSecretParams, decryptedGroupJoinInfo.getRevision(), change, groupLinkPassword); + } + public Pair acceptInvite(GroupInfoV2 groupInfoV2) throws IOException { final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); @@ -284,13 +335,27 @@ public class GroupHelper { throw new IOException(e); } - GroupChange signedGroupChange = groupsV2Api.patchGroup(change.build(), + GroupChange signedGroupChange = groupsV2Api.patchGroup(changeActions, groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams), Optional.absent()); return new Pair<>(decryptedGroupState, signedGroupChange); } + private GroupChange commitChange( + GroupSecretParams groupSecretParams, + int currentRevision, + GroupChange.Actions.Builder change, + GroupLinkPassword password + ) throws IOException { + final int nextRevision = currentRevision + 1; + final GroupChange.Actions changeActions = change.setRevision(nextRevision).build(); + + return groupsV2Api.patchGroup(changeActions, + groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams), + Optional.fromNullable(password).transform(GroupLinkPassword::serialize)); + } + public DecryptedGroup getUpdatedDecryptedGroup( DecryptedGroup group, byte[] signedGroupChange, GroupMasterKey groupMasterKey ) {