mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 02:20:39 +00:00
Update libsignal-service
This commit is contained in:
parent
5ff66728e3
commit
e51b1ee23a
10 changed files with 201 additions and 137 deletions
|
@ -1,7 +1,13 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"name":"[B"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name":"[Z"
|
"name":"[Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name":"[[B"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name":"com.sun.security.auth.module.UnixSystem",
|
"name":"com.sun.security.auth.module.UnixSystem",
|
||||||
"fields":[{"name":"gid"}, {"name":"groups"}, {"name":"uid"}, {"name":"username"}]
|
"fields":[{"name":"gid"}, {"name":"groups"}, {"name":"uid"}, {"name":"username"}]
|
||||||
|
|
|
@ -36,7 +36,6 @@ public class ProofRequiredException extends Exception {
|
||||||
|
|
||||||
static Option from(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException.Option option) {
|
static Option from(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException.Option option) {
|
||||||
return switch (option) {
|
return switch (option) {
|
||||||
case RECAPTCHA -> CAPTCHA;
|
|
||||||
case CAPTCHA -> CAPTCHA;
|
case CAPTCHA -> CAPTCHA;
|
||||||
case PUSH_CHALLENGE -> PUSH_CHALLENGE;
|
case PUSH_CHALLENGE -> PUSH_CHALLENGE;
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,19 +27,8 @@ public class ServiceConfig {
|
||||||
public static final long UNREGISTERED_LIFESPAN = TimeUnit.DAYS.toMillis(30);
|
public static final long UNREGISTERED_LIFESPAN = TimeUnit.DAYS.toMillis(30);
|
||||||
|
|
||||||
public static AccountAttributes.Capabilities getCapabilities(boolean isPrimaryDevice) {
|
public static AccountAttributes.Capabilities getCapabilities(boolean isPrimaryDevice) {
|
||||||
final var giftBadges = !isPrimaryDevice;
|
|
||||||
final var pni = !isPrimaryDevice;
|
|
||||||
final var paymentActivation = !isPrimaryDevice;
|
|
||||||
final var deleteSync = !isPrimaryDevice;
|
final var deleteSync = !isPrimaryDevice;
|
||||||
return new AccountAttributes.Capabilities(true,
|
return new AccountAttributes.Capabilities(true, deleteSync);
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
giftBadges,
|
|
||||||
pni,
|
|
||||||
paymentActivation,
|
|
||||||
deleteSync);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ServiceEnvironmentConfig getServiceEnvironmentConfig(
|
public static ServiceEnvironmentConfig getServiceEnvironmentConfig(
|
||||||
|
|
|
@ -33,13 +33,16 @@ import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
|
||||||
import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
|
import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
|
||||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||||
import org.signal.storageservice.protos.groups.GroupChange;
|
import org.signal.storageservice.protos.groups.GroupChange;
|
||||||
|
import org.signal.storageservice.protos.groups.GroupChangeResponse;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupChangeLog;
|
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupChangeLog;
|
||||||
|
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupResponse;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
||||||
|
import org.whispersystems.signalservice.api.groupsv2.ReceivedGroupSendEndorsements;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
|
@ -133,9 +136,10 @@ public class GroupHelper {
|
||||||
}
|
}
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
try {
|
try {
|
||||||
group = context.getGroupV2Helper().getDecryptedGroup(groupSecretParams);
|
final var response = context.getGroupV2Helper().getDecryptedGroup(groupSecretParams);
|
||||||
|
|
||||||
if (group != null) {
|
if (response != null) {
|
||||||
|
group = handleDecryptedGroupResponse(groupInfoV2, response);
|
||||||
storeProfileKeysFromHistory(groupSecretParams, groupInfoV2, group);
|
storeProfileKeysFromHistory(groupSecretParams, groupInfoV2, group);
|
||||||
}
|
}
|
||||||
} catch (NotAGroupMemberException ignored) {
|
} catch (NotAGroupMemberException ignored) {
|
||||||
|
@ -156,6 +160,35 @@ public class GroupHelper {
|
||||||
return groupInfoV2;
|
return groupInfoV2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DecryptedGroup handleDecryptedGroupResponse(
|
||||||
|
GroupInfoV2 groupInfoV2, final DecryptedGroupResponse decryptedGroupResponse
|
||||||
|
) {
|
||||||
|
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
|
||||||
|
ReceivedGroupSendEndorsements groupSendEndorsements = dependencies.getGroupsV2Operations()
|
||||||
|
.forGroup(groupSecretParams)
|
||||||
|
.receiveGroupSendEndorsements(account.getAci(),
|
||||||
|
decryptedGroupResponse.getGroup(),
|
||||||
|
decryptedGroupResponse.getGroupSendEndorsementsResponse());
|
||||||
|
|
||||||
|
// TODO save group endorsements
|
||||||
|
|
||||||
|
return decryptedGroupResponse.getGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private GroupChange handleGroupChangeResponse(
|
||||||
|
final GroupInfoV2 groupInfoV2, final GroupChangeResponse groupChangeResponse
|
||||||
|
) {
|
||||||
|
ReceivedGroupSendEndorsements groupSendEndorsements = dependencies.getGroupsV2Operations()
|
||||||
|
.forGroup(GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()))
|
||||||
|
.receiveGroupSendEndorsements(account.getAci(),
|
||||||
|
groupInfoV2.getGroup(),
|
||||||
|
groupChangeResponse.groupSendEndorsementsResponse);
|
||||||
|
|
||||||
|
// TODO save group endorsements
|
||||||
|
|
||||||
|
return groupChangeResponse.groupChange;
|
||||||
|
}
|
||||||
|
|
||||||
public Pair<GroupId, SendGroupMessageResults> createGroup(
|
public Pair<GroupId, SendGroupMessageResults> createGroup(
|
||||||
String name, Set<RecipientId> members, String avatarFile
|
String name, Set<RecipientId> members, String avatarFile
|
||||||
) throws IOException, AttachmentInvalidException {
|
) throws IOException, AttachmentInvalidException {
|
||||||
|
@ -181,7 +214,7 @@ public class GroupHelper {
|
||||||
final var gv2 = gv2Pair.first();
|
final var gv2 = gv2Pair.first();
|
||||||
final var decryptedGroup = gv2Pair.second();
|
final var decryptedGroup = gv2Pair.second();
|
||||||
|
|
||||||
gv2.setGroup(decryptedGroup);
|
gv2.setGroup(handleDecryptedGroupResponse(gv2, decryptedGroup));
|
||||||
gv2.setProfileSharingEnabled(true);
|
gv2.setProfileSharingEnabled(true);
|
||||||
if (avatarBytes != null) {
|
if (avatarBytes != null) {
|
||||||
context.getAvatarStore()
|
context.getAvatarStore()
|
||||||
|
@ -277,7 +310,7 @@ public class GroupHelper {
|
||||||
var group = getGroupForUpdating(groupId);
|
var group = getGroupForUpdating(groupId);
|
||||||
|
|
||||||
if (group instanceof GroupInfoV2 groupInfoV2) {
|
if (group instanceof GroupInfoV2 groupInfoV2) {
|
||||||
Pair<DecryptedGroup, GroupChange> groupChangePair;
|
Pair<DecryptedGroup, GroupChangeResponse> groupChangePair;
|
||||||
try {
|
try {
|
||||||
groupChangePair = context.getGroupV2Helper().updateSelfProfileKey(groupInfoV2);
|
groupChangePair = context.getGroupV2Helper().updateSelfProfileKey(groupInfoV2);
|
||||||
} catch (ConflictException e) {
|
} catch (ConflictException e) {
|
||||||
|
@ -286,7 +319,9 @@ public class GroupHelper {
|
||||||
groupChangePair = context.getGroupV2Helper().updateSelfProfileKey(groupInfoV2);
|
groupChangePair = context.getGroupV2Helper().updateSelfProfileKey(groupInfoV2);
|
||||||
}
|
}
|
||||||
if (groupChangePair != null) {
|
if (groupChangePair != null) {
|
||||||
sendUpdateGroupV2Message(groupInfoV2, groupChangePair.first(), groupChangePair.second());
|
sendUpdateGroupV2Message(groupInfoV2,
|
||||||
|
groupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(groupInfoV2, groupChangePair.second()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,11 +339,12 @@ public class GroupHelper {
|
||||||
if (groupJoinInfo.pendingAdminApproval) {
|
if (groupJoinInfo.pendingAdminApproval) {
|
||||||
throw new PendingAdminApprovalException("You have already requested to join the group.");
|
throw new PendingAdminApprovalException("You have already requested to join the group.");
|
||||||
}
|
}
|
||||||
final var groupChange = context.getGroupV2Helper()
|
final var changeResponse = context.getGroupV2Helper()
|
||||||
.joinGroup(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword(), groupJoinInfo);
|
.joinGroup(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword(), groupJoinInfo);
|
||||||
final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(),
|
final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(),
|
||||||
groupJoinInfo.revision + 1,
|
groupJoinInfo.revision + 1,
|
||||||
groupChange.encode());
|
changeResponse.groupChange == null ? null : changeResponse.groupChange.encode());
|
||||||
|
final var groupChange = handleGroupChangeResponse(group, changeResponse);
|
||||||
|
|
||||||
if (group.getGroup() == null) {
|
if (group.getGroup() == null) {
|
||||||
// Only requested member, can't send update to group members
|
// Only requested member, can't send update to group members
|
||||||
|
@ -404,17 +440,17 @@ public class GroupHelper {
|
||||||
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
|
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
|
||||||
DecryptedGroup decryptedGroup;
|
DecryptedGroup decryptedGroup;
|
||||||
try {
|
try {
|
||||||
decryptedGroup = context.getGroupV2Helper().getDecryptedGroup(groupSecretParams);
|
final var response = context.getGroupV2Helper().getDecryptedGroup(groupSecretParams);
|
||||||
|
if (response == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
decryptedGroup = handleDecryptedGroupResponse(groupInfoV2, response);
|
||||||
} catch (NotAGroupMemberException e) {
|
} catch (NotAGroupMemberException e) {
|
||||||
groupInfoV2.setPermissionDenied(true);
|
groupInfoV2.setPermissionDenied(true);
|
||||||
account.getGroupStore().updateGroup(group);
|
account.getGroupStore().updateGroup(group);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decryptedGroup == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
storeProfileKeysFromHistory(groupSecretParams, groupInfoV2, decryptedGroup);
|
storeProfileKeysFromHistory(groupSecretParams, groupInfoV2, decryptedGroup);
|
||||||
} catch (NotAGroupMemberException ignored) {
|
} catch (NotAGroupMemberException ignored) {
|
||||||
|
@ -496,10 +532,12 @@ public class GroupHelper {
|
||||||
) throws NotAGroupMemberException {
|
) throws NotAGroupMemberException {
|
||||||
final var revisionWeWereAdded = context.getGroupV2Helper().findRevisionWeWereAdded(newDecryptedGroup);
|
final var revisionWeWereAdded = context.getGroupV2Helper().findRevisionWeWereAdded(newDecryptedGroup);
|
||||||
final var localRevision = localGroup.getGroup() == null ? 0 : localGroup.getGroup().revision;
|
final var localRevision = localGroup.getGroup() == null ? 0 : localGroup.getGroup().revision;
|
||||||
|
final var sendEndorsementsExpirationMs = 0L;// TODO store expiration localGroup.getGroup() == null ? 0 : localGroup.getGroup().revision;
|
||||||
var fromRevision = Math.max(revisionWeWereAdded, localRevision);
|
var fromRevision = Math.max(revisionWeWereAdded, localRevision);
|
||||||
final var newProfileKeys = new HashMap<RecipientId, ProfileKey>();
|
final var newProfileKeys = new HashMap<RecipientId, ProfileKey>();
|
||||||
while (true) {
|
while (true) {
|
||||||
final var page = context.getGroupV2Helper().getDecryptedGroupHistoryPage(groupSecretParams, fromRevision);
|
final var page = context.getGroupV2Helper()
|
||||||
|
.getDecryptedGroupHistoryPage(groupSecretParams, fromRevision, sendEndorsementsExpirationMs);
|
||||||
page.getChangeLogs()
|
page.getChangeLogs()
|
||||||
.stream()
|
.stream()
|
||||||
.map(DecryptedGroupChangeLog::getChange)
|
.map(DecryptedGroupChangeLog::getChange)
|
||||||
|
@ -606,7 +644,9 @@ public class GroupHelper {
|
||||||
final var groupV2Helper = context.getGroupV2Helper();
|
final var groupV2Helper = context.getGroupV2Helper();
|
||||||
if (group.isPendingMember(account.getSelfRecipientId())) {
|
if (group.isPendingMember(account.getSelfRecipientId())) {
|
||||||
var groupGroupChangePair = groupV2Helper.acceptInvite(group);
|
var groupGroupChangePair = groupV2Helper.acceptInvite(group);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (members != null) {
|
if (members != null) {
|
||||||
|
@ -614,14 +654,18 @@ public class GroupHelper {
|
||||||
requestingMembers.retainAll(group.getRequestingMembers());
|
requestingMembers.retainAll(group.getRequestingMembers());
|
||||||
if (!requestingMembers.isEmpty()) {
|
if (!requestingMembers.isEmpty()) {
|
||||||
var groupGroupChangePair = groupV2Helper.approveJoinRequestMembers(group, requestingMembers);
|
var groupGroupChangePair = groupV2Helper.approveJoinRequestMembers(group, requestingMembers);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
final var newMembers = new HashSet<>(members);
|
final var newMembers = new HashSet<>(members);
|
||||||
newMembers.removeAll(group.getMembers());
|
newMembers.removeAll(group.getMembers());
|
||||||
newMembers.removeAll(group.getRequestingMembers());
|
newMembers.removeAll(group.getRequestingMembers());
|
||||||
if (!newMembers.isEmpty()) {
|
if (!newMembers.isEmpty()) {
|
||||||
var groupGroupChangePair = groupV2Helper.addMembers(group, newMembers);
|
var groupGroupChangePair = groupV2Helper.addMembers(group, newMembers);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,20 +681,26 @@ public class GroupHelper {
|
||||||
existingRemoveMembers.remove(account.getSelfRecipientId());// self can be removed with sendQuitGroupMessage
|
existingRemoveMembers.remove(account.getSelfRecipientId());// self can be removed with sendQuitGroupMessage
|
||||||
if (!existingRemoveMembers.isEmpty()) {
|
if (!existingRemoveMembers.isEmpty()) {
|
||||||
var groupGroupChangePair = groupV2Helper.removeMembers(group, existingRemoveMembers);
|
var groupGroupChangePair = groupV2Helper.removeMembers(group, existingRemoveMembers);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var pendingRemoveMembers = new HashSet<>(removeMembers);
|
var pendingRemoveMembers = new HashSet<>(removeMembers);
|
||||||
pendingRemoveMembers.retainAll(group.getPendingMembers());
|
pendingRemoveMembers.retainAll(group.getPendingMembers());
|
||||||
if (!pendingRemoveMembers.isEmpty()) {
|
if (!pendingRemoveMembers.isEmpty()) {
|
||||||
var groupGroupChangePair = groupV2Helper.revokeInvitedMembers(group, pendingRemoveMembers);
|
var groupGroupChangePair = groupV2Helper.revokeInvitedMembers(group, pendingRemoveMembers);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
var requestingRemoveMembers = new HashSet<>(removeMembers);
|
var requestingRemoveMembers = new HashSet<>(removeMembers);
|
||||||
requestingRemoveMembers.retainAll(group.getRequestingMembers());
|
requestingRemoveMembers.retainAll(group.getRequestingMembers());
|
||||||
if (!requestingRemoveMembers.isEmpty()) {
|
if (!requestingRemoveMembers.isEmpty()) {
|
||||||
var groupGroupChangePair = groupV2Helper.refuseJoinRequestMembers(group, requestingRemoveMembers);
|
var groupGroupChangePair = groupV2Helper.refuseJoinRequestMembers(group, requestingRemoveMembers);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,7 +713,7 @@ public class GroupHelper {
|
||||||
var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, true);
|
var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, true);
|
||||||
result = sendUpdateGroupV2Message(group,
|
result = sendUpdateGroupV2Message(group,
|
||||||
groupGroupChangePair.first(),
|
groupGroupChangePair.first(),
|
||||||
groupGroupChangePair.second());
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -676,7 +726,7 @@ public class GroupHelper {
|
||||||
var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, false);
|
var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, false);
|
||||||
result = sendUpdateGroupV2Message(group,
|
result = sendUpdateGroupV2Message(group,
|
||||||
groupGroupChangePair.first(),
|
groupGroupChangePair.first(),
|
||||||
groupGroupChangePair.second());
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -686,7 +736,9 @@ public class GroupHelper {
|
||||||
newlyBannedMembers.removeAll(group.getBannedMembers());
|
newlyBannedMembers.removeAll(group.getBannedMembers());
|
||||||
if (!newlyBannedMembers.isEmpty()) {
|
if (!newlyBannedMembers.isEmpty()) {
|
||||||
var groupGroupChangePair = groupV2Helper.banMembers(group, newlyBannedMembers);
|
var groupGroupChangePair = groupV2Helper.banMembers(group, newlyBannedMembers);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,38 +747,52 @@ public class GroupHelper {
|
||||||
existingUnbanMembers.retainAll(group.getBannedMembers());
|
existingUnbanMembers.retainAll(group.getBannedMembers());
|
||||||
if (!existingUnbanMembers.isEmpty()) {
|
if (!existingUnbanMembers.isEmpty()) {
|
||||||
var groupGroupChangePair = groupV2Helper.unbanMembers(group, existingUnbanMembers);
|
var groupGroupChangePair = groupV2Helper.unbanMembers(group, existingUnbanMembers);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resetGroupLink) {
|
if (resetGroupLink) {
|
||||||
var groupGroupChangePair = groupV2Helper.resetGroupLinkPassword(group);
|
var groupGroupChangePair = groupV2Helper.resetGroupLinkPassword(group);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupLinkState != null) {
|
if (groupLinkState != null) {
|
||||||
var groupGroupChangePair = groupV2Helper.setGroupLinkState(group, groupLinkState);
|
var groupGroupChangePair = groupV2Helper.setGroupLinkState(group, groupLinkState);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addMemberPermission != null) {
|
if (addMemberPermission != null) {
|
||||||
var groupGroupChangePair = groupV2Helper.setAddMemberPermission(group, addMemberPermission);
|
var groupGroupChangePair = groupV2Helper.setAddMemberPermission(group, addMemberPermission);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editDetailsPermission != null) {
|
if (editDetailsPermission != null) {
|
||||||
var groupGroupChangePair = groupV2Helper.setEditDetailsPermission(group, editDetailsPermission);
|
var groupGroupChangePair = groupV2Helper.setEditDetailsPermission(group, editDetailsPermission);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expirationTimer != null) {
|
if (expirationTimer != null) {
|
||||||
var groupGroupChangePair = groupV2Helper.setMessageExpirationTimer(group, expirationTimer);
|
var groupGroupChangePair = groupV2Helper.setMessageExpirationTimer(group, expirationTimer);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAnnouncementGroup != null) {
|
if (isAnnouncementGroup != null) {
|
||||||
var groupGroupChangePair = groupV2Helper.setIsAnnouncementGroup(group, isAnnouncementGroup);
|
var groupGroupChangePair = groupV2Helper.setIsAnnouncementGroup(group, isAnnouncementGroup);
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name != null || description != null || avatarFile != null) {
|
if (name != null || description != null || avatarFile != null) {
|
||||||
|
@ -735,7 +801,9 @@ public class GroupHelper {
|
||||||
context.getAvatarStore()
|
context.getAvatarStore()
|
||||||
.storeGroupAvatar(group.getGroupId(), outputStream -> outputStream.write(avatarFile));
|
.storeGroupAvatar(group.getGroupId(), outputStream -> outputStream.write(avatarFile));
|
||||||
}
|
}
|
||||||
result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
|
result = sendUpdateGroupV2Message(group,
|
||||||
|
groupGroupChangePair.first(),
|
||||||
|
handleGroupChangeResponse(group, groupGroupChangePair.second()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -771,7 +839,8 @@ public class GroupHelper {
|
||||||
groupInfoV2.setGroup(groupGroupChangePair.first());
|
groupInfoV2.setGroup(groupGroupChangePair.first());
|
||||||
account.getGroupStore().updateGroup(groupInfoV2);
|
account.getGroupStore().updateGroup(groupInfoV2);
|
||||||
|
|
||||||
var messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().encode());
|
var messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2,
|
||||||
|
handleGroupChangeResponse(groupInfoV2, groupGroupChangePair.second()).encode());
|
||||||
return sendGroupMessage(messageBuilder,
|
return sendGroupMessage(messageBuilder,
|
||||||
groupInfoV2.getMembersIncludingPendingWithout(account.getSelfRecipientId()),
|
groupInfoV2.getMembersIncludingPendingWithout(account.getSelfRecipientId()),
|
||||||
groupInfoV2.getDistributionId());
|
groupInfoV2.getDistributionId());
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.signal.libsignal.zkgroup.groups.UuidCiphertext;
|
||||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||||
import org.signal.storageservice.protos.groups.AccessControl;
|
import org.signal.storageservice.protos.groups.AccessControl;
|
||||||
import org.signal.storageservice.protos.groups.GroupChange;
|
import org.signal.storageservice.protos.groups.GroupChange;
|
||||||
|
import org.signal.storageservice.protos.groups.GroupChangeResponse;
|
||||||
import org.signal.storageservice.protos.groups.Member;
|
import org.signal.storageservice.protos.groups.Member;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||||
|
@ -27,6 +28,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
|
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupResponse;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupCandidate;
|
import org.whispersystems.signalservice.api.groupsv2.GroupCandidate;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupHistoryPage;
|
import org.whispersystems.signalservice.api.groupsv2.GroupHistoryPage;
|
||||||
|
@ -75,7 +77,7 @@ class GroupV2Helper {
|
||||||
groupApiCredentials = null;
|
groupApiCredentials = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecryptedGroup getDecryptedGroup(final GroupSecretParams groupSecretParams) throws NotAGroupMemberException {
|
DecryptedGroupResponse getDecryptedGroup(final GroupSecretParams groupSecretParams) throws NotAGroupMemberException {
|
||||||
try {
|
try {
|
||||||
final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams);
|
final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams);
|
||||||
return dependencies.getGroupsV2Api().getGroup(groupSecretParams, groupsV2AuthorizationString);
|
return dependencies.getGroupsV2Api().getGroup(groupSecretParams, groupsV2AuthorizationString);
|
||||||
|
@ -85,7 +87,7 @@ class GroupV2Helper {
|
||||||
}
|
}
|
||||||
logger.warn("Failed to retrieve Group V2 info, ignoring: {}", e.getMessage());
|
logger.warn("Failed to retrieve Group V2 info, ignoring: {}", e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
} catch (IOException | VerificationFailedException | InvalidGroupStateException e) {
|
} catch (IOException | VerificationFailedException | InvalidGroupStateException | InvalidInputException e) {
|
||||||
logger.warn("Failed to retrieve Group V2 info, ignoring: {}", e.getMessage());
|
logger.warn("Failed to retrieve Group V2 info, ignoring: {}", e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -103,19 +105,23 @@ class GroupV2Helper {
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupHistoryPage getDecryptedGroupHistoryPage(
|
GroupHistoryPage getDecryptedGroupHistoryPage(
|
||||||
final GroupSecretParams groupSecretParams, int fromRevision
|
final GroupSecretParams groupSecretParams, int fromRevision, long sendEndorsementsExpirationMs
|
||||||
) throws NotAGroupMemberException {
|
) throws NotAGroupMemberException {
|
||||||
try {
|
try {
|
||||||
final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams);
|
final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams);
|
||||||
return dependencies.getGroupsV2Api()
|
return dependencies.getGroupsV2Api()
|
||||||
.getGroupHistoryPage(groupSecretParams, fromRevision, groupsV2AuthorizationString, false);
|
.getGroupHistoryPage(groupSecretParams,
|
||||||
|
fromRevision,
|
||||||
|
groupsV2AuthorizationString,
|
||||||
|
false,
|
||||||
|
sendEndorsementsExpirationMs);
|
||||||
} catch (NonSuccessfulResponseCodeException e) {
|
} catch (NonSuccessfulResponseCodeException e) {
|
||||||
if (e.getCode() == 403) {
|
if (e.getCode() == 403) {
|
||||||
throw new NotAGroupMemberException(GroupUtils.getGroupIdV2(groupSecretParams), null);
|
throw new NotAGroupMemberException(GroupUtils.getGroupIdV2(groupSecretParams), null);
|
||||||
}
|
}
|
||||||
logger.warn("Failed to retrieve Group V2 history, ignoring: {}", e.getMessage());
|
logger.warn("Failed to retrieve Group V2 history, ignoring: {}", e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
} catch (IOException | VerificationFailedException | InvalidGroupStateException e) {
|
} catch (IOException | VerificationFailedException | InvalidGroupStateException | InvalidInputException e) {
|
||||||
logger.warn("Failed to retrieve Group V2 history, ignoring: {}", e.getMessage());
|
logger.warn("Failed to retrieve Group V2 history, ignoring: {}", e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -132,7 +138,7 @@ class GroupV2Helper {
|
||||||
return partialDecryptedGroup.revision;
|
return partialDecryptedGroup.revision;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<GroupInfoV2, DecryptedGroup> createGroup(
|
Pair<GroupInfoV2, DecryptedGroupResponse> createGroup(
|
||||||
String name, Set<RecipientId> members, byte[] avatarFile
|
String name, Set<RecipientId> members, byte[] avatarFile
|
||||||
) {
|
) {
|
||||||
final var newGroup = buildNewGroup(name, members, avatarFile);
|
final var newGroup = buildNewGroup(name, members, avatarFile);
|
||||||
|
@ -143,16 +149,16 @@ class GroupV2Helper {
|
||||||
final var groupSecretParams = newGroup.getGroupSecretParams();
|
final var groupSecretParams = newGroup.getGroupSecretParams();
|
||||||
|
|
||||||
final GroupsV2AuthorizationString groupAuthForToday;
|
final GroupsV2AuthorizationString groupAuthForToday;
|
||||||
final DecryptedGroup decryptedGroup;
|
final DecryptedGroupResponse response;
|
||||||
try {
|
try {
|
||||||
groupAuthForToday = getGroupAuthForToday(groupSecretParams);
|
groupAuthForToday = getGroupAuthForToday(groupSecretParams);
|
||||||
dependencies.getGroupsV2Api().putNewGroup(newGroup, groupAuthForToday);
|
dependencies.getGroupsV2Api().putNewGroup(newGroup, groupAuthForToday);
|
||||||
decryptedGroup = dependencies.getGroupsV2Api().getGroup(groupSecretParams, groupAuthForToday);
|
response = dependencies.getGroupsV2Api().getGroup(groupSecretParams, groupAuthForToday);
|
||||||
} catch (IOException | VerificationFailedException | InvalidGroupStateException e) {
|
} catch (IOException | VerificationFailedException | InvalidGroupStateException | InvalidInputException e) {
|
||||||
logger.warn("Failed to create V2 group: {}", e.getMessage());
|
logger.warn("Failed to create V2 group: {}", e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (decryptedGroup == null) {
|
if (response == null) {
|
||||||
logger.warn("Failed to create V2 group, unknown error!");
|
logger.warn("Failed to create V2 group, unknown error!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -161,7 +167,7 @@ class GroupV2Helper {
|
||||||
final var masterKey = groupSecretParams.getMasterKey();
|
final var masterKey = groupSecretParams.getMasterKey();
|
||||||
var g = new GroupInfoV2(groupId, masterKey, context.getAccount().getRecipientResolver());
|
var g = new GroupInfoV2(groupId, masterKey, context.getAccount().getRecipientResolver());
|
||||||
|
|
||||||
return new Pair<>(g, decryptedGroup);
|
return new Pair<>(g, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupsV2Operations.NewGroup buildNewGroup(
|
private GroupsV2Operations.NewGroup buildNewGroup(
|
||||||
|
@ -195,7 +201,7 @@ class GroupV2Helper {
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> updateGroup(
|
Pair<DecryptedGroup, GroupChangeResponse> updateGroup(
|
||||||
GroupInfoV2 groupInfoV2, String name, String description, byte[] avatarFile
|
GroupInfoV2 groupInfoV2, String name, String description, byte[] avatarFile
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
|
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
|
||||||
|
@ -218,7 +224,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> addMembers(
|
Pair<DecryptedGroup, GroupChangeResponse> addMembers(
|
||||||
GroupInfoV2 groupInfoV2, Set<RecipientId> newMembers
|
GroupInfoV2 groupInfoV2, Set<RecipientId> newMembers
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
@ -244,7 +250,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> leaveGroup(
|
Pair<DecryptedGroup, GroupChangeResponse> leaveGroup(
|
||||||
GroupInfoV2 groupInfoV2, Set<RecipientId> membersToMakeAdmin
|
GroupInfoV2 groupInfoV2, Set<RecipientId> membersToMakeAdmin
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
var pendingMembersList = groupInfoV2.getGroup().pendingMembers;
|
var pendingMembersList = groupInfoV2.getGroup().pendingMembers;
|
||||||
|
@ -264,7 +270,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, groupOperations.createLeaveAndPromoteMembersToAdmin(selfAci, adminUuids));
|
return commitChange(groupInfoV2, groupOperations.createLeaveAndPromoteMembersToAdmin(selfAci, adminUuids));
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> removeMembers(
|
Pair<DecryptedGroup, GroupChangeResponse> removeMembers(
|
||||||
GroupInfoV2 groupInfoV2, Set<RecipientId> members
|
GroupInfoV2 groupInfoV2, Set<RecipientId> members
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final var memberUuids = members.stream()
|
final var memberUuids = members.stream()
|
||||||
|
@ -276,7 +282,7 @@ class GroupV2Helper {
|
||||||
return ejectMembers(groupInfoV2, memberUuids);
|
return ejectMembers(groupInfoV2, memberUuids);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> approveJoinRequestMembers(
|
Pair<DecryptedGroup, GroupChangeResponse> approveJoinRequestMembers(
|
||||||
GroupInfoV2 groupInfoV2, Set<RecipientId> members
|
GroupInfoV2 groupInfoV2, Set<RecipientId> members
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final var memberUuids = members.stream()
|
final var memberUuids = members.stream()
|
||||||
|
@ -287,7 +293,7 @@ class GroupV2Helper {
|
||||||
return approveJoinRequest(groupInfoV2, memberUuids);
|
return approveJoinRequest(groupInfoV2, memberUuids);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> refuseJoinRequestMembers(
|
Pair<DecryptedGroup, GroupChangeResponse> refuseJoinRequestMembers(
|
||||||
GroupInfoV2 groupInfoV2, Set<RecipientId> members
|
GroupInfoV2 groupInfoV2, Set<RecipientId> members
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final var memberUuids = members.stream()
|
final var memberUuids = members.stream()
|
||||||
|
@ -297,7 +303,7 @@ class GroupV2Helper {
|
||||||
return refuseJoinRequest(groupInfoV2, memberUuids);
|
return refuseJoinRequest(groupInfoV2, memberUuids);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> revokeInvitedMembers(
|
Pair<DecryptedGroup, GroupChangeResponse> revokeInvitedMembers(
|
||||||
GroupInfoV2 groupInfoV2, Set<RecipientId> members
|
GroupInfoV2 groupInfoV2, Set<RecipientId> members
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
var pendingMembersList = groupInfoV2.getGroup().pendingMembers;
|
var pendingMembersList = groupInfoV2.getGroup().pendingMembers;
|
||||||
|
@ -311,7 +317,7 @@ class GroupV2Helper {
|
||||||
return revokeInvites(groupInfoV2, memberUuids);
|
return revokeInvites(groupInfoV2, memberUuids);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> banMembers(
|
Pair<DecryptedGroup, GroupChangeResponse> banMembers(
|
||||||
GroupInfoV2 groupInfoV2, Set<RecipientId> block
|
GroupInfoV2 groupInfoV2, Set<RecipientId> block
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
@ -329,7 +335,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> unbanMembers(
|
Pair<DecryptedGroup, GroupChangeResponse> unbanMembers(
|
||||||
GroupInfoV2 groupInfoV2, Set<RecipientId> block
|
GroupInfoV2 groupInfoV2, Set<RecipientId> block
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
@ -345,14 +351,14 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> resetGroupLinkPassword(GroupInfoV2 groupInfoV2) throws IOException {
|
Pair<DecryptedGroup, GroupChangeResponse> resetGroupLinkPassword(GroupInfoV2 groupInfoV2) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
final var newGroupLinkPassword = GroupLinkPassword.createNew().serialize();
|
final var newGroupLinkPassword = GroupLinkPassword.createNew().serialize();
|
||||||
final var change = groupOperations.createModifyGroupLinkPasswordChange(newGroupLinkPassword);
|
final var change = groupOperations.createModifyGroupLinkPasswordChange(newGroupLinkPassword);
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> setGroupLinkState(
|
Pair<DecryptedGroup, GroupChangeResponse> setGroupLinkState(
|
||||||
GroupInfoV2 groupInfoV2, GroupLinkState state
|
GroupInfoV2 groupInfoV2, GroupLinkState state
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
@ -367,7 +373,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> setEditDetailsPermission(
|
Pair<DecryptedGroup, GroupChangeResponse> setEditDetailsPermission(
|
||||||
GroupInfoV2 groupInfoV2, GroupPermission permission
|
GroupInfoV2 groupInfoV2, GroupPermission permission
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
@ -377,7 +383,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> setAddMemberPermission(
|
Pair<DecryptedGroup, GroupChangeResponse> setAddMemberPermission(
|
||||||
GroupInfoV2 groupInfoV2, GroupPermission permission
|
GroupInfoV2 groupInfoV2, GroupPermission permission
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
@ -387,7 +393,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> updateSelfProfileKey(GroupInfoV2 groupInfoV2) throws IOException {
|
Pair<DecryptedGroup, GroupChangeResponse> updateSelfProfileKey(GroupInfoV2 groupInfoV2) throws IOException {
|
||||||
Optional<DecryptedMember> selfInGroup = groupInfoV2.getGroup() == null
|
Optional<DecryptedMember> selfInGroup = groupInfoV2.getGroup() == null
|
||||||
? Optional.empty()
|
? Optional.empty()
|
||||||
: DecryptedGroupUtil.findMemberByAci(groupInfoV2.getGroup().members, getSelfAci());
|
: DecryptedGroupUtil.findMemberByAci(groupInfoV2.getGroup().members, getSelfAci());
|
||||||
|
@ -417,7 +423,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupChange joinGroup(
|
GroupChangeResponse joinGroup(
|
||||||
GroupMasterKey groupMasterKey,
|
GroupMasterKey groupMasterKey,
|
||||||
GroupLinkPassword groupLinkPassword,
|
GroupLinkPassword groupLinkPassword,
|
||||||
DecryptedGroupJoinInfo decryptedGroupJoinInfo
|
DecryptedGroupJoinInfo decryptedGroupJoinInfo
|
||||||
|
@ -444,7 +450,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupSecretParams, decryptedGroupJoinInfo.revision, change, groupLinkPassword);
|
return commitChange(groupSecretParams, decryptedGroupJoinInfo.revision, change, groupLinkPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> acceptInvite(GroupInfoV2 groupInfoV2) throws IOException {
|
Pair<DecryptedGroup, GroupChangeResponse> acceptInvite(GroupInfoV2 groupInfoV2) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
|
||||||
final var selfRecipientId = context.getAccount().getSelfRecipientId();
|
final var selfRecipientId = context.getAccount().getSelfRecipientId();
|
||||||
|
@ -461,7 +467,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> setMemberAdmin(
|
Pair<DecryptedGroup, GroupChangeResponse> setMemberAdmin(
|
||||||
GroupInfoV2 groupInfoV2, RecipientId recipientId, boolean admin
|
GroupInfoV2 groupInfoV2, RecipientId recipientId, boolean admin
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
@ -475,7 +481,7 @@ class GroupV2Helper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> setMessageExpirationTimer(
|
Pair<DecryptedGroup, GroupChangeResponse> setMessageExpirationTimer(
|
||||||
GroupInfoV2 groupInfoV2, int messageExpirationTimer
|
GroupInfoV2 groupInfoV2, int messageExpirationTimer
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
@ -483,7 +489,7 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, change);
|
return commitChange(groupInfoV2, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<DecryptedGroup, GroupChange> setIsAnnouncementGroup(
|
Pair<DecryptedGroup, GroupChangeResponse> setIsAnnouncementGroup(
|
||||||
GroupInfoV2 groupInfoV2, boolean isAnnouncementGroup
|
GroupInfoV2 groupInfoV2, boolean isAnnouncementGroup
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
@ -511,7 +517,7 @@ class GroupV2Helper {
|
||||||
return dependencies.getGroupsV2Operations().forGroup(groupSecretParams);
|
return dependencies.getGroupsV2Operations().forGroup(groupSecretParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<DecryptedGroup, GroupChange> revokeInvites(
|
private Pair<DecryptedGroup, GroupChangeResponse> revokeInvites(
|
||||||
GroupInfoV2 groupInfoV2, Set<DecryptedPendingMember> pendingMembers
|
GroupInfoV2 groupInfoV2, Set<DecryptedPendingMember> pendingMembers
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
|
@ -525,28 +531,28 @@ class GroupV2Helper {
|
||||||
return commitChange(groupInfoV2, groupOperations.createRemoveInvitationChange(uuidCipherTexts));
|
return commitChange(groupInfoV2, groupOperations.createRemoveInvitationChange(uuidCipherTexts));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<DecryptedGroup, GroupChange> approveJoinRequest(
|
private Pair<DecryptedGroup, GroupChangeResponse> approveJoinRequest(
|
||||||
GroupInfoV2 groupInfoV2, Set<UUID> uuids
|
GroupInfoV2 groupInfoV2, Set<UUID> uuids
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
return commitChange(groupInfoV2, groupOperations.createApproveGroupJoinRequest(uuids));
|
return commitChange(groupInfoV2, groupOperations.createApproveGroupJoinRequest(uuids));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<DecryptedGroup, GroupChange> refuseJoinRequest(
|
private Pair<DecryptedGroup, GroupChangeResponse> refuseJoinRequest(
|
||||||
GroupInfoV2 groupInfoV2, Set<ServiceId> serviceIds
|
GroupInfoV2 groupInfoV2, Set<ServiceId> serviceIds
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
return commitChange(groupInfoV2, groupOperations.createRefuseGroupJoinRequest(serviceIds, false, List.of()));
|
return commitChange(groupInfoV2, groupOperations.createRefuseGroupJoinRequest(serviceIds, false, List.of()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<DecryptedGroup, GroupChange> ejectMembers(
|
private Pair<DecryptedGroup, GroupChangeResponse> ejectMembers(
|
||||||
GroupInfoV2 groupInfoV2, Set<ACI> members
|
GroupInfoV2 groupInfoV2, Set<ACI> members
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
|
||||||
return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(members, false, List.of()));
|
return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(members, false, List.of()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<DecryptedGroup, GroupChange> commitChange(
|
private Pair<DecryptedGroup, GroupChangeResponse> commitChange(
|
||||||
GroupInfoV2 groupInfoV2, GroupChange.Actions.Builder change
|
GroupInfoV2 groupInfoV2, GroupChange.Actions.Builder change
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
|
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
|
||||||
|
@ -567,10 +573,12 @@ class GroupV2Helper {
|
||||||
var signedGroupChange = dependencies.getGroupsV2Api()
|
var signedGroupChange = dependencies.getGroupsV2Api()
|
||||||
.patchGroup(changeActions, getGroupAuthForToday(groupSecretParams), Optional.empty());
|
.patchGroup(changeActions, getGroupAuthForToday(groupSecretParams), Optional.empty());
|
||||||
|
|
||||||
|
groupInfoV2.setGroup(decryptedGroupState);
|
||||||
|
|
||||||
return new Pair<>(decryptedGroupState, signedGroupChange);
|
return new Pair<>(decryptedGroupState, signedGroupChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupChange commitChange(
|
private GroupChangeResponse commitChange(
|
||||||
GroupSecretParams groupSecretParams,
|
GroupSecretParams groupSecretParams,
|
||||||
int currentRevision,
|
int currentRevision,
|
||||||
GroupChange.Actions.Builder change,
|
GroupChange.Actions.Builder change,
|
||||||
|
|
|
@ -16,13 +16,14 @@ import org.asamk.signal.manager.util.KeyUtils;
|
||||||
import org.asamk.signal.manager.util.PaymentUtils;
|
import org.asamk.signal.manager.util.PaymentUtils;
|
||||||
import org.asamk.signal.manager.util.ProfileUtils;
|
import org.asamk.signal.manager.util.ProfileUtils;
|
||||||
import org.asamk.signal.manager.util.Utils;
|
import org.asamk.signal.manager.util.Utils;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.signal.libsignal.protocol.IdentityKey;
|
import org.signal.libsignal.protocol.IdentityKey;
|
||||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||||
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential;
|
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential;
|
||||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
import org.whispersystems.signalservice.api.crypto.SealedSenderAccess;
|
||||||
import org.whispersystems.signalservice.api.profiles.AvatarUploadParams;
|
import org.whispersystems.signalservice.api.profiles.AvatarUploadParams;
|
||||||
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
|
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
|
||||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||||
|
@ -387,7 +388,7 @@ public final class ProfileHelper {
|
||||||
private Single<ProfileAndCredential> retrieveProfile(
|
private Single<ProfileAndCredential> retrieveProfile(
|
||||||
SignalServiceAddress address,
|
SignalServiceAddress address,
|
||||||
Optional<ProfileKey> profileKey,
|
Optional<ProfileKey> profileKey,
|
||||||
Optional<UnidentifiedAccess> unidentifiedAccess,
|
@Nullable SealedSenderAccess unidentifiedAccess,
|
||||||
SignalServiceProfile.RequestType requestType
|
SignalServiceProfile.RequestType requestType
|
||||||
) {
|
) {
|
||||||
final var profileService = dependencies.getProfileService();
|
final var profileService = dependencies.getProfileService();
|
||||||
|
@ -450,13 +451,7 @@ public final class ProfileHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<UnidentifiedAccess> getUnidentifiedAccess(RecipientId recipientId) {
|
private @Nullable SealedSenderAccess getUnidentifiedAccess(RecipientId recipientId) {
|
||||||
var unidentifiedAccess = context.getUnidentifiedAccessHelper().getAccessFor(recipientId, true);
|
return context.getUnidentifiedAccessHelper().getSealedSenderAccessFor(recipientId, true);
|
||||||
|
|
||||||
if (unidentifiedAccess.isPresent()) {
|
|
||||||
return unidentifiedAccess.get().getTargetUnidentifiedAccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
import org.asamk.signal.manager.storage.groups.GroupInfo;
|
import org.asamk.signal.manager.storage.groups.GroupInfo;
|
||||||
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
||||||
import org.asamk.signal.manager.storage.sendLog.MessageSendLogEntry;
|
import org.asamk.signal.manager.storage.sendLog.MessageSendLogEntry;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||||
import org.signal.libsignal.protocol.InvalidRegistrationIdException;
|
import org.signal.libsignal.protocol.InvalidRegistrationIdException;
|
||||||
import org.signal.libsignal.protocol.NoSessionException;
|
import org.signal.libsignal.protocol.NoSessionException;
|
||||||
|
@ -22,9 +23,10 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
||||||
|
import org.whispersystems.signalservice.api.crypto.SealedSenderAccess;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
|
import org.whispersystems.signalservice.api.groupsv2.GroupSendEndorsements;
|
||||||
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceEditMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceEditMessage;
|
||||||
|
@ -199,7 +201,7 @@ public class SendHelper {
|
||||||
return SendMessageResult.success(account.getSelfAddress(), List.of(), false, false, 0, Optional.empty());
|
return SendMessageResult.success(account.getSelfAddress(), List.of(), false, false, 0, Optional.empty());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return messageSender.sendSyncMessage(message, context.getUnidentifiedAccessHelper().getAccessForSync());
|
return messageSender.sendSyncMessage(message);
|
||||||
} catch (UnregisteredUserException e) {
|
} catch (UnregisteredUserException e) {
|
||||||
var address = context.getRecipientHelper().resolveSignalServiceAddress(account.getSelfRecipientId());
|
var address = context.getRecipientHelper().resolveSignalServiceAddress(account.getSelfRecipientId());
|
||||||
return SendMessageResult.unregisteredFailure(address);
|
return SendMessageResult.unregisteredFailure(address);
|
||||||
|
@ -380,10 +382,11 @@ public class SendHelper {
|
||||||
() -> false,
|
() -> false,
|
||||||
urgent,
|
urgent,
|
||||||
editTargetTimestamp.get());
|
editTargetTimestamp.get());
|
||||||
final SenderKeySenderHandler senderKeySender = (distId, recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendGroupDataMessage(
|
final SenderKeySenderHandler senderKeySender = (distId, recipients, unidentifiedAccess, groupSendEndorsements, isRecipientUpdate) -> messageSender.sendGroupDataMessage(
|
||||||
distId,
|
distId,
|
||||||
recipients,
|
recipients,
|
||||||
unidentifiedAccess,
|
unidentifiedAccess,
|
||||||
|
groupSendEndorsements,
|
||||||
isRecipientUpdate,
|
isRecipientUpdate,
|
||||||
contentHint,
|
contentHint,
|
||||||
message,
|
message,
|
||||||
|
@ -436,9 +439,11 @@ public class SendHelper {
|
||||||
unidentifiedAccess,
|
unidentifiedAccess,
|
||||||
message,
|
message,
|
||||||
() -> false),
|
() -> false),
|
||||||
(distId, recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendGroupTyping(distId,
|
(distId, recipients, unidentifiedAccess, groupSendEndorsements, isRecipientUpdate) -> messageSender.sendGroupTyping(
|
||||||
|
distId,
|
||||||
recipients,
|
recipients,
|
||||||
unidentifiedAccess,
|
unidentifiedAccess,
|
||||||
|
groupSendEndorsements,
|
||||||
message),
|
message),
|
||||||
recipientIds,
|
recipientIds,
|
||||||
distributionId);
|
distributionId);
|
||||||
|
@ -526,8 +531,8 @@ public class SendHelper {
|
||||||
final var senderKeyTargets = new HashSet<RecipientId>();
|
final var senderKeyTargets = new HashSet<RecipientId>();
|
||||||
final var recipientList = new ArrayList<>(recipientIds);
|
final var recipientList = new ArrayList<>(recipientIds);
|
||||||
for (final var recipientId : recipientList) {
|
for (final var recipientId : recipientList) {
|
||||||
final var access = context.getUnidentifiedAccessHelper().getAccessFor(recipientId);
|
final var access = context.getUnidentifiedAccessHelper().getSealedSenderAccessFor(recipientId);
|
||||||
if (access.isEmpty() || access.get().getTargetUnidentifiedAccess().isEmpty()) {
|
if (access != null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +567,8 @@ public class SendHelper {
|
||||||
final var addresses = recipientIdList.stream()
|
final var addresses = recipientIdList.stream()
|
||||||
.map(context.getRecipientHelper()::resolveSignalServiceAddress)
|
.map(context.getRecipientHelper()::resolveSignalServiceAddress)
|
||||||
.toList();
|
.toList();
|
||||||
final var unidentifiedAccesses = context.getUnidentifiedAccessHelper().getAccessFor(recipientIdList);
|
final var unidentifiedAccesses = context.getUnidentifiedAccessHelper()
|
||||||
|
.getSealedSenderAccessFor(recipientIdList);
|
||||||
try {
|
try {
|
||||||
final var results = sender.send(addresses, unidentifiedAccesses, isRecipientUpdate);
|
final var results = sender.send(addresses, unidentifiedAccesses, isRecipientUpdate);
|
||||||
|
|
||||||
|
@ -601,15 +607,14 @@ public class SendHelper {
|
||||||
List<UnidentifiedAccess> unidentifiedAccesses = context.getUnidentifiedAccessHelper()
|
List<UnidentifiedAccess> unidentifiedAccesses = context.getUnidentifiedAccessHelper()
|
||||||
.getAccessFor(recipientIdList)
|
.getAccessFor(recipientIdList)
|
||||||
.stream()
|
.stream()
|
||||||
.map(Optional::get)
|
|
||||||
.map(UnidentifiedAccessPair::getTargetUnidentifiedAccess)
|
|
||||||
.map(Optional::get)
|
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
final GroupSendEndorsements groupSendEndorsements = null;//TODO
|
||||||
try {
|
try {
|
||||||
List<SendMessageResult> results = sender.send(distributionId,
|
List<SendMessageResult> results = sender.send(distributionId,
|
||||||
addresses,
|
addresses,
|
||||||
unidentifiedAccesses,
|
unidentifiedAccesses,
|
||||||
|
groupSendEndorsements,
|
||||||
isRecipientUpdate);
|
isRecipientUpdate);
|
||||||
|
|
||||||
final var successCount = results.stream().filter(SendMessageResult::isSuccess).count();
|
final var successCount = results.stream().filter(SendMessageResult::isSuccess).count();
|
||||||
|
@ -684,7 +689,7 @@ public class SendHelper {
|
||||||
try {
|
try {
|
||||||
return s.send(messageSender,
|
return s.send(messageSender,
|
||||||
address,
|
address,
|
||||||
context.getUnidentifiedAccessHelper().getAccessFor(recipientId),
|
context.getUnidentifiedAccessHelper().getSealedSenderAccessFor(recipientId),
|
||||||
includePniSignature);
|
includePniSignature);
|
||||||
} catch (UnregisteredUserException e) {
|
} catch (UnregisteredUserException e) {
|
||||||
final RecipientId newRecipientId;
|
final RecipientId newRecipientId;
|
||||||
|
@ -696,7 +701,7 @@ public class SendHelper {
|
||||||
address = context.getRecipientHelper().resolveSignalServiceAddress(newRecipientId);
|
address = context.getRecipientHelper().resolveSignalServiceAddress(newRecipientId);
|
||||||
return s.send(messageSender,
|
return s.send(messageSender,
|
||||||
address,
|
address,
|
||||||
context.getUnidentifiedAccessHelper().getAccessFor(newRecipientId),
|
context.getUnidentifiedAccessHelper().getSealedSenderAccessFor(newRecipientId),
|
||||||
includePniSignature);
|
includePniSignature);
|
||||||
}
|
}
|
||||||
} catch (UnregisteredUserException e) {
|
} catch (UnregisteredUserException e) {
|
||||||
|
@ -772,7 +777,7 @@ public class SendHelper {
|
||||||
SendMessageResult send(
|
SendMessageResult send(
|
||||||
SignalServiceMessageSender messageSender,
|
SignalServiceMessageSender messageSender,
|
||||||
SignalServiceAddress address,
|
SignalServiceAddress address,
|
||||||
Optional<UnidentifiedAccessPair> unidentifiedAccess,
|
@Nullable SealedSenderAccess unidentifiedAccess,
|
||||||
boolean includePniSignature
|
boolean includePniSignature
|
||||||
) throws IOException, UnregisteredUserException, ProofRequiredException, RateLimitException, org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
) throws IOException, UnregisteredUserException, ProofRequiredException, RateLimitException, org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
}
|
}
|
||||||
|
@ -783,6 +788,7 @@ public class SendHelper {
|
||||||
DistributionId distributionId,
|
DistributionId distributionId,
|
||||||
List<SignalServiceAddress> recipients,
|
List<SignalServiceAddress> recipients,
|
||||||
List<UnidentifiedAccess> unidentifiedAccess,
|
List<UnidentifiedAccess> unidentifiedAccess,
|
||||||
|
GroupSendEndorsements groupSendEndorsements,
|
||||||
boolean isRecipientUpdate
|
boolean isRecipientUpdate
|
||||||
) throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException;
|
) throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException;
|
||||||
}
|
}
|
||||||
|
@ -791,7 +797,7 @@ public class SendHelper {
|
||||||
|
|
||||||
List<SendMessageResult> send(
|
List<SendMessageResult> send(
|
||||||
List<SignalServiceAddress> recipients,
|
List<SignalServiceAddress> recipients,
|
||||||
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess,
|
List<SealedSenderAccess> unidentifiedAccess,
|
||||||
boolean isRecipientUpdate
|
boolean isRecipientUpdate
|
||||||
) throws IOException, UntrustedIdentityException;
|
) throws IOException, UntrustedIdentityException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,17 @@ import org.asamk.signal.manager.api.Profile;
|
||||||
import org.asamk.signal.manager.internal.SignalDependencies;
|
import org.asamk.signal.manager.internal.SignalDependencies;
|
||||||
import org.asamk.signal.manager.storage.SignalAccount;
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
|
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
|
||||||
import org.signal.libsignal.metadata.certificate.SenderCertificate;
|
import org.signal.libsignal.metadata.certificate.SenderCertificate;
|
||||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.whispersystems.signalservice.api.crypto.SealedSenderAccess;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class UnidentifiedAccessHelper {
|
public class UnidentifiedAccessHelper {
|
||||||
|
@ -42,57 +42,49 @@ public class UnidentifiedAccessHelper {
|
||||||
senderCertificate = null;
|
senderCertificate = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Optional<UnidentifiedAccessPair>> getAccessFor(List<RecipientId> recipients) {
|
public List<SealedSenderAccess> getSealedSenderAccessFor(List<RecipientId> recipients) {
|
||||||
|
return recipients.stream().map(this::getAccessFor).map(SealedSenderAccess::forIndividual).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable SealedSenderAccess getSealedSenderAccessFor(RecipientId recipient) {
|
||||||
|
return getSealedSenderAccessFor(recipient, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable SealedSenderAccess getSealedSenderAccessFor(RecipientId recipient, boolean noRefresh) {
|
||||||
|
return SealedSenderAccess.forIndividual(getAccessFor(recipient, noRefresh));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UnidentifiedAccess> getAccessFor(List<RecipientId> recipients) {
|
||||||
return recipients.stream().map(this::getAccessFor).toList();
|
return recipients.stream().map(this::getAccessFor).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<UnidentifiedAccessPair> getAccessFor(RecipientId recipient) {
|
private @Nullable UnidentifiedAccess getAccessFor(RecipientId recipient) {
|
||||||
return getAccessFor(recipient, false);
|
return getAccessFor(recipient, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<UnidentifiedAccessPair> getAccessFor(RecipientId recipientId, boolean noRefresh) {
|
private @Nullable UnidentifiedAccess getAccessFor(RecipientId recipientId, boolean noRefresh) {
|
||||||
var recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipientId, noRefresh);
|
var recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipientId, noRefresh);
|
||||||
if (recipientUnidentifiedAccessKey == null) {
|
if (recipientUnidentifiedAccessKey == null) {
|
||||||
logger.trace("Unidentified access not available for {}", recipientId);
|
logger.trace("Unidentified access not available for {}", recipientId);
|
||||||
return Optional.empty();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(noRefresh);
|
var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(noRefresh);
|
||||||
if (selfUnidentifiedAccessKey == null) {
|
if (selfUnidentifiedAccessKey == null) {
|
||||||
logger.trace("Unidentified access not available for self");
|
logger.trace("Unidentified access not available for self");
|
||||||
return Optional.empty();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var senderCertificate = getSenderCertificateFor(recipientId);
|
var senderCertificate = getSenderCertificateFor(recipientId);
|
||||||
if (senderCertificate == null) {
|
if (senderCertificate == null) {
|
||||||
logger.trace("Unidentified access not available due to missing sender certificate");
|
logger.trace("Unidentified access not available due to missing sender certificate");
|
||||||
return Optional.empty();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(recipientUnidentifiedAccessKey,
|
return new UnidentifiedAccess(recipientUnidentifiedAccessKey, senderCertificate, false);
|
||||||
senderCertificate,
|
|
||||||
false), new UnidentifiedAccess(selfUnidentifiedAccessKey, senderCertificate, false)));
|
|
||||||
} catch (InvalidCertificateException e) {
|
} catch (InvalidCertificateException e) {
|
||||||
return Optional.empty();
|
return null;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<UnidentifiedAccessPair> getAccessForSync() {
|
|
||||||
var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(false);
|
|
||||||
var selfUnidentifiedAccessCertificate = getSenderCertificate();
|
|
||||||
|
|
||||||
if (selfUnidentifiedAccessKey == null || selfUnidentifiedAccessCertificate == null) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(selfUnidentifiedAccessKey,
|
|
||||||
selfUnidentifiedAccessCertificate,
|
|
||||||
false),
|
|
||||||
new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate, false)));
|
|
||||||
} catch (InvalidCertificateException e) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +113,7 @@ public class UnidentifiedAccessHelper {
|
||||||
privacySenderCertificate = new SenderCertificate(certificate);
|
privacySenderCertificate = new SenderCertificate(certificate);
|
||||||
return certificate;
|
return certificate;
|
||||||
} catch (IOException | InvalidCertificateException e) {
|
} catch (IOException | InvalidCertificateException e) {
|
||||||
logger.warn("Failed to get sender certificate, ignoring: {}", e.getMessage());
|
logger.warn("Failed to get sender certificate (pnp), ignoring: {}", e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ dependencyResolutionManagement {
|
||||||
library("slf4j.jul", "org.slf4j", "jul-to-slf4j").versionRef("slf4j")
|
library("slf4j.jul", "org.slf4j", "jul-to-slf4j").versionRef("slf4j")
|
||||||
library("logback", "ch.qos.logback", "logback-classic").version("1.5.6")
|
library("logback", "ch.qos.logback", "logback-classic").version("1.5.6")
|
||||||
|
|
||||||
library("signalservice", "com.github.turasa", "signal-service-java").version("2.15.3_unofficial_104")
|
library("signalservice", "com.github.turasa", "signal-service-java").version("2.15.3_unofficial_105")
|
||||||
library("sqlite", "org.xerial", "sqlite-jdbc").version("3.46.0.0")
|
library("sqlite", "org.xerial", "sqlite-jdbc").version("3.46.0.0")
|
||||||
library("hikari", "com.zaxxer", "HikariCP").version("5.1.0")
|
library("hikari", "com.zaxxer", "HikariCP").version("5.1.0")
|
||||||
library("junit.jupiter", "org.junit.jupiter", "junit-jupiter").version("5.10.2")
|
library("junit.jupiter", "org.junit.jupiter", "junit-jupiter").version("5.10.2")
|
||||||
|
|
|
@ -8,7 +8,7 @@ public class BaseConfig {
|
||||||
public static final String PROJECT_VERSION = BaseConfig.class.getPackage().getImplementationVersion();
|
public static final String PROJECT_VERSION = BaseConfig.class.getPackage().getImplementationVersion();
|
||||||
|
|
||||||
static final String USER_AGENT_SIGNAL_ANDROID = Optional.ofNullable(System.getenv("SIGNAL_CLI_USER_AGENT"))
|
static final String USER_AGENT_SIGNAL_ANDROID = Optional.ofNullable(System.getenv("SIGNAL_CLI_USER_AGENT"))
|
||||||
.orElse("Signal-Android/7.9.0");
|
.orElse("Signal-Android/7.12.1");
|
||||||
static final String USER_AGENT_SIGNAL_CLI = PROJECT_NAME == null
|
static final String USER_AGENT_SIGNAL_CLI = PROJECT_NAME == null
|
||||||
? "signal-cli"
|
? "signal-cli"
|
||||||
: PROJECT_NAME + "/" + PROJECT_VERSION;
|
: PROJECT_NAME + "/" + PROJECT_VERSION;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue