mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 18:40:39 +00:00
Refactor message sending
This commit is contained in:
parent
b77d820661
commit
893b7f7f9d
6 changed files with 404 additions and 295 deletions
|
@ -33,6 +33,7 @@ import org.asamk.signal.manager.groups.NotAGroupMemberException;
|
||||||
import org.asamk.signal.manager.helper.GroupV2Helper;
|
import org.asamk.signal.manager.helper.GroupV2Helper;
|
||||||
import org.asamk.signal.manager.helper.PinHelper;
|
import org.asamk.signal.manager.helper.PinHelper;
|
||||||
import org.asamk.signal.manager.helper.ProfileHelper;
|
import org.asamk.signal.manager.helper.ProfileHelper;
|
||||||
|
import org.asamk.signal.manager.helper.SendHelper;
|
||||||
import org.asamk.signal.manager.helper.UnidentifiedAccessHelper;
|
import org.asamk.signal.manager.helper.UnidentifiedAccessHelper;
|
||||||
import org.asamk.signal.manager.jobs.Context;
|
import org.asamk.signal.manager.jobs.Context;
|
||||||
import org.asamk.signal.manager.jobs.Job;
|
import org.asamk.signal.manager.jobs.Job;
|
||||||
|
@ -86,7 +87,6 @@ import org.whispersystems.libsignal.util.Pair;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.InvalidMessageStructureException;
|
import org.whispersystems.signalservice.api.InvalidMessageStructureException;
|
||||||
import org.whispersystems.signalservice.api.SignalSessionLock;
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString;
|
import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString;
|
||||||
|
@ -111,7 +111,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
|
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
||||||
|
@ -120,7 +119,6 @@ import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.ConflictException;
|
import org.whispersystems.signalservice.api.push.exceptions.ConflictException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
|
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
|
||||||
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
|
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||||
|
@ -179,10 +177,11 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
private final ExecutorService executor = Executors.newCachedThreadPool();
|
private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
private final UnidentifiedAccessHelper unidentifiedAccessHelper;
|
|
||||||
private final ProfileHelper profileHelper;
|
private final ProfileHelper profileHelper;
|
||||||
private final GroupV2Helper groupV2Helper;
|
private final GroupV2Helper groupV2Helper;
|
||||||
private final PinHelper pinHelper;
|
private final PinHelper pinHelper;
|
||||||
|
private final SendHelper sendHelper;
|
||||||
|
|
||||||
private final AvatarStore avatarStore;
|
private final AvatarStore avatarStore;
|
||||||
private final AttachmentStore attachmentStore;
|
private final AttachmentStore attachmentStore;
|
||||||
private final StickerPackStore stickerPackStore;
|
private final StickerPackStore stickerPackStore;
|
||||||
|
@ -218,7 +217,7 @@ public class Manager implements Closeable {
|
||||||
sessionLock);
|
sessionLock);
|
||||||
this.pinHelper = new PinHelper(dependencies.getKeyBackupService());
|
this.pinHelper = new PinHelper(dependencies.getKeyBackupService());
|
||||||
|
|
||||||
this.unidentifiedAccessHelper = new UnidentifiedAccessHelper(account::getProfileKey,
|
final var unidentifiedAccessHelper = new UnidentifiedAccessHelper(account::getProfileKey,
|
||||||
account.getProfileStore()::getProfileKey,
|
account.getProfileStore()::getProfileKey,
|
||||||
this::getRecipientProfile,
|
this::getRecipientProfile,
|
||||||
this::getSenderCertificate);
|
this::getSenderCertificate);
|
||||||
|
@ -237,6 +236,14 @@ public class Manager implements Closeable {
|
||||||
this.avatarStore = new AvatarStore(pathConfig.getAvatarsPath());
|
this.avatarStore = new AvatarStore(pathConfig.getAvatarsPath());
|
||||||
this.attachmentStore = new AttachmentStore(pathConfig.getAttachmentsPath());
|
this.attachmentStore = new AttachmentStore(pathConfig.getAttachmentsPath());
|
||||||
this.stickerPackStore = new StickerPackStore(pathConfig.getStickerPacksPath());
|
this.stickerPackStore = new StickerPackStore(pathConfig.getStickerPacksPath());
|
||||||
|
this.sendHelper = new SendHelper(account,
|
||||||
|
dependencies,
|
||||||
|
unidentifiedAccessHelper,
|
||||||
|
this::resolveSignalServiceAddress,
|
||||||
|
this::resolveRecipient,
|
||||||
|
this::handleIdentityFailure,
|
||||||
|
this::getGroup,
|
||||||
|
this::refreshRegisteredUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
|
@ -396,10 +403,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
account.getProfileStore().storeProfile(account.getSelfRecipientId(), newProfile);
|
account.getProfileStore().storeProfile(account.getSelfRecipientId(), newProfile);
|
||||||
|
|
||||||
try {
|
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE));
|
||||||
sendSyncMessage(SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE));
|
|
||||||
} catch (UntrustedIdentityException ignored) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregister() throws IOException {
|
public void unregister() throws IOException {
|
||||||
|
@ -653,17 +657,6 @@ public class Manager implements Closeable {
|
||||||
return Optional.of(AttachmentUtils.createAttachment(streamDetails, Optional.absent()));
|
return Optional.of(AttachmentUtils.createAttachment(streamDetails, Optional.absent()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException {
|
|
||||||
var g = getGroup(groupId);
|
|
||||||
if (g == null) {
|
|
||||||
throw new GroupNotFoundException(groupId);
|
|
||||||
}
|
|
||||||
if (!g.isMember(account.getSelfRecipientId())) {
|
|
||||||
throw new NotAGroupMemberException(groupId, g.getTitle());
|
|
||||||
}
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
|
|
||||||
private GroupInfo getGroupForUpdating(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException {
|
private GroupInfo getGroupForUpdating(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException {
|
||||||
var g = getGroup(groupId);
|
var g = getGroup(groupId);
|
||||||
if (g == null) {
|
if (g == null) {
|
||||||
|
@ -682,12 +675,12 @@ public class Manager implements Closeable {
|
||||||
public Pair<Long, List<SendMessageResult>> sendGroupMessage(
|
public Pair<Long, List<SendMessageResult>> sendGroupMessage(
|
||||||
String messageText, List<String> attachments, GroupId groupId
|
String messageText, List<String> attachments, GroupId groupId
|
||||||
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
|
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
|
||||||
final var messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText);
|
final var messageBuilder = createMessageBuilder().withBody(messageText);
|
||||||
if (attachments != null) {
|
if (attachments != null) {
|
||||||
messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments));
|
messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments));
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendGroupMessage(messageBuilder, groupId);
|
return sendHelper.sendAsGroupMessage(messageBuilder, groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendGroupMessageReaction(
|
public Pair<Long, List<SendMessageResult>> sendGroupMessageReaction(
|
||||||
|
@ -698,20 +691,9 @@ public class Manager implements Closeable {
|
||||||
remove,
|
remove,
|
||||||
resolveSignalServiceAddress(targetAuthorRecipientId),
|
resolveSignalServiceAddress(targetAuthorRecipientId),
|
||||||
targetSentTimestamp);
|
targetSentTimestamp);
|
||||||
final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction);
|
final var messageBuilder = createMessageBuilder().withReaction(reaction);
|
||||||
|
|
||||||
return sendGroupMessage(messageBuilder, groupId);
|
return sendHelper.sendAsGroupMessage(messageBuilder, groupId);
|
||||||
}
|
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendGroupMessage(
|
|
||||||
SignalServiceDataMessage.Builder messageBuilder, GroupId groupId
|
|
||||||
) throws IOException, GroupNotFoundException, NotAGroupMemberException {
|
|
||||||
final var g = getGroupForSending(groupId);
|
|
||||||
|
|
||||||
GroupUtils.setGroupContext(messageBuilder, g);
|
|
||||||
messageBuilder.withExpiration(g.getMessageExpirationTime());
|
|
||||||
|
|
||||||
return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfRecipientId()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendQuitGroupMessage(
|
public Pair<Long, List<SendMessageResult>> sendQuitGroupMessage(
|
||||||
|
@ -722,7 +704,7 @@ public class Manager implements Closeable {
|
||||||
return quitGroupV1((GroupInfoV1) group);
|
return quitGroupV1((GroupInfoV1) group);
|
||||||
}
|
}
|
||||||
|
|
||||||
final var newAdmins = getSignalServiceAddresses(groupAdmins);
|
final var newAdmins = getRecipientIds(groupAdmins);
|
||||||
try {
|
try {
|
||||||
return quitGroupV2((GroupInfoV2) group, newAdmins);
|
return quitGroupV2((GroupInfoV2) group, newAdmins);
|
||||||
} catch (ConflictException e) {
|
} catch (ConflictException e) {
|
||||||
|
@ -737,10 +719,11 @@ public class Manager implements Closeable {
|
||||||
.withId(groupInfoV1.getGroupId().serialize())
|
.withId(groupInfoV1.getGroupId().serialize())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
var messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group);
|
var messageBuilder = createMessageBuilder().asGroupMessage(group);
|
||||||
groupInfoV1.removeMember(account.getSelfRecipientId());
|
groupInfoV1.removeMember(account.getSelfRecipientId());
|
||||||
account.getGroupStore().updateGroup(groupInfoV1);
|
account.getGroupStore().updateGroup(groupInfoV1);
|
||||||
return sendMessage(messageBuilder, groupInfoV1.getMembersWithout(account.getSelfRecipientId()));
|
return sendHelper.sendGroupMessage(messageBuilder.build(),
|
||||||
|
groupInfoV1.getMembersIncludingPendingWithout(account.getSelfRecipientId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Long, List<SendMessageResult>> quitGroupV2(
|
private Pair<Long, List<SendMessageResult>> quitGroupV2(
|
||||||
|
@ -760,7 +743,8 @@ public class Manager implements Closeable {
|
||||||
groupInfoV2.setGroup(groupGroupChangePair.first(), this::resolveRecipient);
|
groupInfoV2.setGroup(groupGroupChangePair.first(), this::resolveRecipient);
|
||||||
var messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().toByteArray());
|
var messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().toByteArray());
|
||||||
account.getGroupStore().updateGroup(groupInfoV2);
|
account.getGroupStore().updateGroup(groupInfoV2);
|
||||||
return sendMessage(messageBuilder, groupInfoV2.getMembersWithout(account.getSelfRecipientId()));
|
return sendHelper.sendGroupMessage(messageBuilder.build(),
|
||||||
|
groupInfoV2.getMembersIncludingPendingWithout(account.getSelfRecipientId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteGroup(GroupId groupId) throws IOException {
|
public void deleteGroup(GroupId groupId) throws IOException {
|
||||||
|
@ -771,7 +755,7 @@ public class Manager implements Closeable {
|
||||||
public Pair<GroupId, List<SendMessageResult>> createGroup(
|
public Pair<GroupId, List<SendMessageResult>> createGroup(
|
||||||
String name, List<String> members, File avatarFile
|
String name, List<String> members, File avatarFile
|
||||||
) throws IOException, AttachmentInvalidException, InvalidNumberException {
|
) throws IOException, AttachmentInvalidException, InvalidNumberException {
|
||||||
return createGroup(name, members == null ? null : getSignalServiceAddresses(members), avatarFile);
|
return createGroup(name, members == null ? null : getRecipientIds(members), avatarFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<GroupId, List<SendMessageResult>> createGroup(
|
private Pair<GroupId, List<SendMessageResult>> createGroup(
|
||||||
|
@ -807,7 +791,8 @@ public class Manager implements Closeable {
|
||||||
messageBuilder = getGroupUpdateMessageBuilder(gv2, null);
|
messageBuilder = getGroupUpdateMessageBuilder(gv2, null);
|
||||||
account.getGroupStore().updateGroup(gv2);
|
account.getGroupStore().updateGroup(gv2);
|
||||||
|
|
||||||
final var result = sendMessage(messageBuilder, gv2.getMembersIncludingPendingWithout(selfRecipientId));
|
final var result = sendHelper.sendGroupMessage(messageBuilder.build(),
|
||||||
|
gv2.getMembersIncludingPendingWithout(selfRecipientId));
|
||||||
return new Pair<>(gv2.getGroupId(), result.second());
|
return new Pair<>(gv2.getGroupId(), result.second());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,10 +814,10 @@ public class Manager implements Closeable {
|
||||||
return updateGroup(groupId,
|
return updateGroup(groupId,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
members == null ? null : getSignalServiceAddresses(members),
|
members == null ? null : getRecipientIds(members),
|
||||||
removeMembers == null ? null : getSignalServiceAddresses(removeMembers),
|
removeMembers == null ? null : getRecipientIds(removeMembers),
|
||||||
admins == null ? null : getSignalServiceAddresses(admins),
|
admins == null ? null : getRecipientIds(admins),
|
||||||
removeAdmins == null ? null : getSignalServiceAddresses(removeAdmins),
|
removeAdmins == null ? null : getRecipientIds(removeAdmins),
|
||||||
resetGroupLink,
|
resetGroupLink,
|
||||||
groupLinkState,
|
groupLinkState,
|
||||||
addMemberPermission,
|
addMemberPermission,
|
||||||
|
@ -908,7 +893,8 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
account.getGroupStore().updateGroup(gv1);
|
account.getGroupStore().updateGroup(gv1);
|
||||||
|
|
||||||
return sendMessage(messageBuilder, gv1.getMembersIncludingPendingWithout(account.getSelfRecipientId()));
|
return sendHelper.sendGroupMessage(messageBuilder.build(),
|
||||||
|
gv1.getMembersIncludingPendingWithout(account.getSelfRecipientId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGroupV1Details(
|
private void updateGroupV1Details(
|
||||||
|
@ -1092,7 +1078,7 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
final var messageBuilder = getGroupUpdateMessageBuilder(group, groupChange.toByteArray());
|
final var messageBuilder = getGroupUpdateMessageBuilder(group, groupChange.toByteArray());
|
||||||
account.getGroupStore().updateGroup(group);
|
account.getGroupStore().updateGroup(group);
|
||||||
return sendMessage(messageBuilder, members);
|
return sendHelper.sendGroupMessage(messageBuilder.build(), members);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int currentTimeDays() {
|
private static int currentTimeDays() {
|
||||||
|
@ -1122,9 +1108,9 @@ public class Manager implements Closeable {
|
||||||
GroupIdV1 groupId, SignalServiceAddress recipient
|
GroupIdV1 groupId, SignalServiceAddress recipient
|
||||||
) throws IOException, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException {
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException {
|
||||||
GroupInfoV1 g;
|
GroupInfoV1 g;
|
||||||
var group = getGroupForSending(groupId);
|
var group = getGroupForUpdating(groupId);
|
||||||
if (!(group instanceof GroupInfoV1)) {
|
if (!(group instanceof GroupInfoV1)) {
|
||||||
throw new RuntimeException("Received an invalid group request for a v2 group!");
|
throw new IOException("Received an invalid group request for a v2 group!");
|
||||||
}
|
}
|
||||||
g = (GroupInfoV1) group;
|
g = (GroupInfoV1) group;
|
||||||
|
|
||||||
|
@ -1136,7 +1122,7 @@ public class Manager implements Closeable {
|
||||||
var messageBuilder = getGroupUpdateMessageBuilder(g);
|
var messageBuilder = getGroupUpdateMessageBuilder(g);
|
||||||
|
|
||||||
// Send group message only to the recipient who requested it
|
// Send group message only to the recipient who requested it
|
||||||
return sendMessage(messageBuilder, Set.of(recipientId));
|
return sendHelper.sendGroupMessage(messageBuilder.build(), Set.of(recipientId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV1 g) throws AttachmentInvalidException {
|
private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV1 g) throws AttachmentInvalidException {
|
||||||
|
@ -1157,18 +1143,14 @@ public class Manager implements Closeable {
|
||||||
throw new AttachmentInvalidException(g.getGroupId().toBase64(), e);
|
throw new AttachmentInvalidException(g.getGroupId().toBase64(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return SignalServiceDataMessage.newBuilder()
|
return createMessageBuilder().asGroupMessage(group.build()).withExpiration(g.getMessageExpirationTime());
|
||||||
.asGroupMessage(group.build())
|
|
||||||
.withExpiration(g.getMessageExpirationTime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV2 g, byte[] signedGroupChange) {
|
private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV2 g, byte[] signedGroupChange) {
|
||||||
var group = SignalServiceGroupV2.newBuilder(g.getMasterKey())
|
var group = SignalServiceGroupV2.newBuilder(g.getMasterKey())
|
||||||
.withRevision(g.getGroup().getRevision())
|
.withRevision(g.getGroup().getRevision())
|
||||||
.withSignedGroupChange(signedGroupChange);
|
.withSignedGroupChange(signedGroupChange);
|
||||||
return SignalServiceDataMessage.newBuilder()
|
return createMessageBuilder().asGroupMessage(group.build()).withExpiration(g.getMessageExpirationTime());
|
||||||
.asGroupMessage(group.build())
|
|
||||||
.withExpiration(g.getMessageExpirationTime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<Long, List<SendMessageResult>> sendGroupInfoRequest(
|
Pair<Long, List<SendMessageResult>> sendGroupInfoRequest(
|
||||||
|
@ -1176,10 +1158,10 @@ public class Manager implements Closeable {
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.REQUEST_INFO).withId(groupId.serialize());
|
var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.REQUEST_INFO).withId(groupId.serialize());
|
||||||
|
|
||||||
var messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group.build());
|
var messageBuilder = createMessageBuilder().asGroupMessage(group.build());
|
||||||
|
|
||||||
// Send group info request message to the recipient who sent us a message with this groupId
|
// Send group info request message to the recipient who sent us a message with this groupId
|
||||||
return sendMessage(messageBuilder, Set.of(resolveRecipient(recipient)));
|
return sendHelper.sendGroupMessage(messageBuilder.build(), Set.of(resolveRecipient(recipient)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendReceipt(
|
void sendReceipt(
|
||||||
|
@ -1189,16 +1171,13 @@ public class Manager implements Closeable {
|
||||||
List.of(messageId),
|
List.of(messageId),
|
||||||
System.currentTimeMillis());
|
System.currentTimeMillis());
|
||||||
|
|
||||||
dependencies.getMessageSender()
|
sendHelper.sendReceiptMessage(receiptMessage, resolveRecipient(remoteAddress));
|
||||||
.sendReceipt(remoteAddress,
|
|
||||||
unidentifiedAccessHelper.getAccessFor(resolveRecipient(remoteAddress)),
|
|
||||||
receiptMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendMessage(
|
public Pair<Long, List<SendMessageResult>> sendMessage(
|
||||||
String messageText, List<String> attachments, List<String> recipients
|
String messageText, List<String> attachments, List<String> recipients
|
||||||
) throws IOException, AttachmentInvalidException, InvalidNumberException {
|
) throws IOException, AttachmentInvalidException, InvalidNumberException {
|
||||||
final var messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText);
|
final var messageBuilder = createMessageBuilder().withBody(messageText);
|
||||||
if (attachments != null) {
|
if (attachments != null) {
|
||||||
var attachmentStreams = AttachmentUtils.getSignalServiceAttachments(attachments);
|
var attachmentStreams = AttachmentUtils.getSignalServiceAttachments(attachments);
|
||||||
|
|
||||||
|
@ -1215,33 +1194,33 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
messageBuilder.withAttachments(attachmentPointers);
|
messageBuilder.withAttachments(attachmentPointers);
|
||||||
}
|
}
|
||||||
return sendMessage(messageBuilder, getSignalServiceAddresses(recipients));
|
return sendHelper.sendMessage(messageBuilder, getRecipientIds(recipients));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, SendMessageResult> sendSelfMessage(
|
public Pair<Long, SendMessageResult> sendSelfMessage(
|
||||||
String messageText, List<String> attachments
|
String messageText, List<String> attachments
|
||||||
) throws IOException, AttachmentInvalidException {
|
) throws IOException, AttachmentInvalidException {
|
||||||
final var messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText);
|
final var messageBuilder = createMessageBuilder().withBody(messageText);
|
||||||
if (attachments != null) {
|
if (attachments != null) {
|
||||||
messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments));
|
messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments));
|
||||||
}
|
}
|
||||||
return sendSelfMessage(messageBuilder);
|
return sendHelper.sendSelfMessage(messageBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendRemoteDeleteMessage(
|
public Pair<Long, List<SendMessageResult>> sendRemoteDeleteMessage(
|
||||||
long targetSentTimestamp, List<String> recipients
|
long targetSentTimestamp, List<String> recipients
|
||||||
) throws IOException, InvalidNumberException {
|
) throws IOException, InvalidNumberException {
|
||||||
var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp);
|
var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp);
|
||||||
final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete);
|
final var messageBuilder = createMessageBuilder().withRemoteDelete(delete);
|
||||||
return sendMessage(messageBuilder, getSignalServiceAddresses(recipients));
|
return sendHelper.sendMessage(messageBuilder, getRecipientIds(recipients));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendGroupRemoteDeleteMessage(
|
public Pair<Long, List<SendMessageResult>> sendGroupRemoteDeleteMessage(
|
||||||
long targetSentTimestamp, GroupId groupId
|
long targetSentTimestamp, GroupId groupId
|
||||||
) throws IOException, NotAGroupMemberException, GroupNotFoundException {
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException {
|
||||||
var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp);
|
var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp);
|
||||||
final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete);
|
final var messageBuilder = createMessageBuilder().withRemoteDelete(delete);
|
||||||
return sendGroupMessage(messageBuilder, groupId);
|
return sendHelper.sendAsGroupMessage(messageBuilder, groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendMessageReaction(
|
public Pair<Long, List<SendMessageResult>> sendMessageReaction(
|
||||||
|
@ -1252,28 +1231,27 @@ public class Manager implements Closeable {
|
||||||
remove,
|
remove,
|
||||||
resolveSignalServiceAddress(targetAuthorRecipientId),
|
resolveSignalServiceAddress(targetAuthorRecipientId),
|
||||||
targetSentTimestamp);
|
targetSentTimestamp);
|
||||||
final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction);
|
final var messageBuilder = createMessageBuilder().withReaction(reaction);
|
||||||
return sendMessage(messageBuilder, getSignalServiceAddresses(recipients));
|
return sendHelper.sendMessage(messageBuilder, getRecipientIds(recipients));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendEndSessionMessage(List<String> recipients) throws IOException, InvalidNumberException {
|
public Pair<Long, List<SendMessageResult>> sendEndSessionMessage(List<String> recipients) throws IOException, InvalidNumberException {
|
||||||
var messageBuilder = SignalServiceDataMessage.newBuilder().asEndSessionMessage();
|
var messageBuilder = createMessageBuilder().asEndSessionMessage();
|
||||||
|
|
||||||
final var signalServiceAddresses = getSignalServiceAddresses(recipients);
|
final var recipientIds = getRecipientIds(recipients);
|
||||||
try {
|
try {
|
||||||
return sendMessage(messageBuilder, signalServiceAddresses);
|
return sendHelper.sendMessage(messageBuilder, recipientIds);
|
||||||
} catch (Exception e) {
|
} finally {
|
||||||
for (var address : signalServiceAddresses) {
|
for (var recipientId : recipientIds) {
|
||||||
handleEndSession(address);
|
handleEndSession(recipientId);
|
||||||
}
|
}
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void renewSession(RecipientId recipientId) throws IOException {
|
void renewSession(RecipientId recipientId) throws IOException {
|
||||||
account.getSessionStore().archiveSessions(recipientId);
|
account.getSessionStore().archiveSessions(recipientId);
|
||||||
if (!recipientId.equals(getSelfRecipientId())) {
|
if (!recipientId.equals(getSelfRecipientId())) {
|
||||||
sendNullMessage(recipientId);
|
sendHelper.sendNullMessage(recipientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1323,8 +1301,8 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendExpirationTimerUpdate(RecipientId recipientId) throws IOException {
|
private void sendExpirationTimerUpdate(RecipientId recipientId) throws IOException {
|
||||||
final var messageBuilder = SignalServiceDataMessage.newBuilder().asExpirationUpdate();
|
final var messageBuilder = createMessageBuilder().asExpirationUpdate();
|
||||||
sendMessage(messageBuilder, Set.of(recipientId));
|
sendHelper.sendMessage(messageBuilder, Set.of(recipientId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1350,8 +1328,8 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendExpirationTimerUpdate(GroupIdV1 groupId) throws IOException, NotAGroupMemberException, GroupNotFoundException {
|
private void sendExpirationTimerUpdate(GroupIdV1 groupId) throws IOException, NotAGroupMemberException, GroupNotFoundException {
|
||||||
final var messageBuilder = SignalServiceDataMessage.newBuilder().asExpirationUpdate();
|
final var messageBuilder = createMessageBuilder().asExpirationUpdate();
|
||||||
sendGroupMessage(messageBuilder, groupId);
|
sendHelper.sendAsGroupMessage(messageBuilder, groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1398,11 +1376,7 @@ public class Manager implements Closeable {
|
||||||
.setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS)
|
.setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS)
|
||||||
.build();
|
.build();
|
||||||
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
||||||
try {
|
sendHelper.sendSyncMessage(message);
|
||||||
sendSyncMessage(message);
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestSyncContacts() throws IOException {
|
private void requestSyncContacts() throws IOException {
|
||||||
|
@ -1410,11 +1384,7 @@ public class Manager implements Closeable {
|
||||||
.setType(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS)
|
.setType(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS)
|
||||||
.build();
|
.build();
|
||||||
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
||||||
try {
|
sendHelper.sendSyncMessage(message);
|
||||||
sendSyncMessage(message);
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestSyncBlocked() throws IOException {
|
private void requestSyncBlocked() throws IOException {
|
||||||
|
@ -1422,11 +1392,7 @@ public class Manager implements Closeable {
|
||||||
.setType(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED)
|
.setType(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED)
|
||||||
.build();
|
.build();
|
||||||
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
||||||
try {
|
sendHelper.sendSyncMessage(message);
|
||||||
sendSyncMessage(message);
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestSyncConfiguration() throws IOException {
|
private void requestSyncConfiguration() throws IOException {
|
||||||
|
@ -1434,11 +1400,7 @@ public class Manager implements Closeable {
|
||||||
.setType(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION)
|
.setType(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION)
|
||||||
.build();
|
.build();
|
||||||
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
||||||
try {
|
sendHelper.sendSyncMessage(message);
|
||||||
sendSyncMessage(message);
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestSyncKeys() throws IOException {
|
private void requestSyncKeys() throws IOException {
|
||||||
|
@ -1446,11 +1408,7 @@ public class Manager implements Closeable {
|
||||||
.setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS)
|
.setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS)
|
||||||
.build();
|
.build();
|
||||||
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
|
||||||
try {
|
sendHelper.sendSyncMessage(message);
|
||||||
sendSyncMessage(message);
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getSenderCertificate() {
|
private byte[] getSenderCertificate() {
|
||||||
|
@ -1469,12 +1427,7 @@ public class Manager implements Closeable {
|
||||||
return certificate;
|
return certificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendSyncMessage(SignalServiceSyncMessage message) throws IOException, UntrustedIdentityException {
|
private Set<RecipientId> getRecipientIds(Collection<String> numbers) throws InvalidNumberException {
|
||||||
var messageSender = dependencies.getMessageSender();
|
|
||||||
messageSender.sendSyncMessage(message, unidentifiedAccessHelper.getAccessForSync());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<RecipientId> getSignalServiceAddresses(Collection<String> numbers) throws InvalidNumberException {
|
|
||||||
final var signalServiceAddresses = new HashSet<SignalServiceAddress>(numbers.size());
|
final var signalServiceAddresses = new HashSet<SignalServiceAddress>(numbers.size());
|
||||||
final var addressesMissingUuid = new HashSet<SignalServiceAddress>();
|
final var addressesMissingUuid = new HashSet<SignalServiceAddress>();
|
||||||
|
|
||||||
|
@ -1537,188 +1490,27 @@ public class Manager implements Closeable {
|
||||||
public void sendTypingMessage(
|
public void sendTypingMessage(
|
||||||
TypingAction action, Set<String> recipients
|
TypingAction action, Set<String> recipients
|
||||||
) throws IOException, UntrustedIdentityException, InvalidNumberException {
|
) throws IOException, UntrustedIdentityException, InvalidNumberException {
|
||||||
sendTypingMessageInternal(action, getSignalServiceAddresses(recipients));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendTypingMessageInternal(
|
|
||||||
TypingAction action, Set<RecipientId> recipientIds
|
|
||||||
) throws IOException, UntrustedIdentityException {
|
|
||||||
final var timestamp = System.currentTimeMillis();
|
final var timestamp = System.currentTimeMillis();
|
||||||
var message = new SignalServiceTypingMessage(action.toSignalService(), timestamp, Optional.absent());
|
var message = new SignalServiceTypingMessage(action.toSignalService(), timestamp, Optional.absent());
|
||||||
var messageSender = dependencies.getMessageSender();
|
sendHelper.sendTypingMessage(message, getRecipientIds(recipients));
|
||||||
for (var recipientId : recipientIds) {
|
|
||||||
final var address = resolveSignalServiceAddress(recipientId);
|
|
||||||
messageSender.sendTyping(address, unidentifiedAccessHelper.getAccessFor(recipientId), message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendGroupTypingMessage(
|
public void sendGroupTypingMessage(
|
||||||
TypingAction action, GroupId groupId
|
TypingAction action, GroupId groupId
|
||||||
) throws IOException, NotAGroupMemberException, GroupNotFoundException {
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException {
|
||||||
final var timestamp = System.currentTimeMillis();
|
final var timestamp = System.currentTimeMillis();
|
||||||
final var g = getGroupForSending(groupId);
|
|
||||||
final var message = new SignalServiceTypingMessage(action.toSignalService(),
|
final var message = new SignalServiceTypingMessage(action.toSignalService(),
|
||||||
timestamp,
|
timestamp,
|
||||||
Optional.of(groupId.serialize()));
|
Optional.of(groupId.serialize()));
|
||||||
final var messageSender = dependencies.getMessageSender();
|
sendHelper.sendGroupTypingMessage(message, groupId);
|
||||||
final var recipientIdList = new ArrayList<>(g.getMembersWithout(account.getSelfRecipientId()));
|
|
||||||
final var addresses = recipientIdList.stream()
|
|
||||||
.map(this::resolveSignalServiceAddress)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
messageSender.sendTyping(addresses, unidentifiedAccessHelper.getAccessFor(recipientIdList), message, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Long, List<SendMessageResult>> sendMessage(
|
private SignalServiceDataMessage.Builder createMessageBuilder() {
|
||||||
SignalServiceDataMessage.Builder messageBuilder, Set<RecipientId> recipientIds
|
|
||||||
) throws IOException {
|
|
||||||
final var timestamp = System.currentTimeMillis();
|
final var timestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
|
var messageBuilder = SignalServiceDataMessage.newBuilder();
|
||||||
messageBuilder.withTimestamp(timestamp);
|
messageBuilder.withTimestamp(timestamp);
|
||||||
|
return messageBuilder;
|
||||||
SignalServiceDataMessage message = null;
|
|
||||||
try {
|
|
||||||
message = messageBuilder.build();
|
|
||||||
if (message.getGroupContext().isPresent()) {
|
|
||||||
try {
|
|
||||||
var messageSender = dependencies.getMessageSender();
|
|
||||||
final var isRecipientUpdate = false;
|
|
||||||
final var recipientIdList = new ArrayList<>(recipientIds);
|
|
||||||
final var addresses = recipientIdList.stream()
|
|
||||||
.map(this::resolveSignalServiceAddress)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
var result = messageSender.sendDataMessage(addresses,
|
|
||||||
unidentifiedAccessHelper.getAccessFor(recipientIdList),
|
|
||||||
isRecipientUpdate,
|
|
||||||
ContentHint.DEFAULT,
|
|
||||||
message,
|
|
||||||
sendResult -> logger.trace("Partial message send result: {}", sendResult.isSuccess()),
|
|
||||||
() -> false);
|
|
||||||
|
|
||||||
for (var r : result) {
|
|
||||||
handlePossibleIdentityFailure(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Pair<>(timestamp, result);
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
return new Pair<>(timestamp, List.of());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Send to all individually, so sync messages are sent correctly
|
|
||||||
messageBuilder.withProfileKey(account.getProfileKey().serialize());
|
|
||||||
var results = new ArrayList<SendMessageResult>(recipientIds.size());
|
|
||||||
for (var recipientId : recipientIds) {
|
|
||||||
final var contact = account.getContactStore().getContact(recipientId);
|
|
||||||
final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
|
|
||||||
messageBuilder.withExpiration(expirationTime);
|
|
||||||
message = messageBuilder.build();
|
|
||||||
final var result = sendMessage(recipientId, message);
|
|
||||||
handlePossibleIdentityFailure(result);
|
|
||||||
results.add(result);
|
|
||||||
}
|
|
||||||
return new Pair<>(timestamp, results);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (message != null && message.isEndSession()) {
|
|
||||||
for (var recipient : recipientIds) {
|
|
||||||
handleEndSession(recipient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handlePossibleIdentityFailure(final SendMessageResult r) {
|
|
||||||
if (r.getIdentityFailure() != null) {
|
|
||||||
final var recipientId = resolveRecipient(r.getAddress());
|
|
||||||
final var identityKey = r.getIdentityFailure().getIdentityKey();
|
|
||||||
if (identityKey != null) {
|
|
||||||
final var newIdentity = account.getIdentityKeyStore()
|
|
||||||
.saveIdentity(recipientId, identityKey, new Date());
|
|
||||||
if (newIdentity) {
|
|
||||||
account.getSessionStore().archiveSessions(recipientId);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Retrieve profile to get the current identity key from the server
|
|
||||||
retrieveEncryptedProfile(recipientId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Pair<Long, SendMessageResult> sendSelfMessage(
|
|
||||||
SignalServiceDataMessage.Builder messageBuilder
|
|
||||||
) throws IOException {
|
|
||||||
final var timestamp = System.currentTimeMillis();
|
|
||||||
messageBuilder.withTimestamp(timestamp);
|
|
||||||
final var recipientId = account.getSelfRecipientId();
|
|
||||||
|
|
||||||
final var contact = account.getContactStore().getContact(recipientId);
|
|
||||||
final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
|
|
||||||
messageBuilder.withExpiration(expirationTime);
|
|
||||||
|
|
||||||
var message = messageBuilder.build();
|
|
||||||
final var result = sendSelfMessage(message);
|
|
||||||
return new Pair<>(timestamp, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException {
|
|
||||||
var messageSender = dependencies.getMessageSender();
|
|
||||||
|
|
||||||
var recipientId = account.getSelfRecipientId();
|
|
||||||
|
|
||||||
final var unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(recipientId);
|
|
||||||
var recipient = resolveSignalServiceAddress(recipientId);
|
|
||||||
var transcript = new SentTranscriptMessage(Optional.of(recipient),
|
|
||||||
message.getTimestamp(),
|
|
||||||
message,
|
|
||||||
message.getExpiresInSeconds(),
|
|
||||||
Map.of(recipient, unidentifiedAccess.isPresent()),
|
|
||||||
false);
|
|
||||||
var syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript);
|
|
||||||
|
|
||||||
try {
|
|
||||||
return messageSender.sendSyncMessage(syncMessage, unidentifiedAccess);
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
return SendMessageResult.identityFailure(recipient, e.getIdentityKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SendMessageResult sendMessage(
|
|
||||||
RecipientId recipientId, SignalServiceDataMessage message
|
|
||||||
) throws IOException {
|
|
||||||
var messageSender = dependencies.getMessageSender();
|
|
||||||
|
|
||||||
final var address = resolveSignalServiceAddress(recipientId);
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
return messageSender.sendDataMessage(address,
|
|
||||||
unidentifiedAccessHelper.getAccessFor(recipientId),
|
|
||||||
ContentHint.DEFAULT,
|
|
||||||
message);
|
|
||||||
} catch (UnregisteredUserException e) {
|
|
||||||
final var newRecipientId = refreshRegisteredUser(recipientId);
|
|
||||||
return messageSender.sendDataMessage(resolveSignalServiceAddress(newRecipientId),
|
|
||||||
unidentifiedAccessHelper.getAccessFor(newRecipientId),
|
|
||||||
ContentHint.DEFAULT,
|
|
||||||
message);
|
|
||||||
}
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
return SendMessageResult.identityFailure(address, e.getIdentityKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException {
|
|
||||||
var messageSender = dependencies.getMessageSender();
|
|
||||||
|
|
||||||
final var address = resolveSignalServiceAddress(recipientId);
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
return messageSender.sendNullMessage(address, unidentifiedAccessHelper.getAccessFor(recipientId));
|
|
||||||
} catch (UnregisteredUserException e) {
|
|
||||||
final var newRecipientId = refreshRegisteredUser(recipientId);
|
|
||||||
final var newAddress = resolveSignalServiceAddress(newRecipientId);
|
|
||||||
return messageSender.sendNullMessage(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId));
|
|
||||||
}
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
return SendMessageResult.identityFailure(address, e.getIdentityKey());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, ProtocolUntrustedIdentityException, InvalidMessageStructureException {
|
private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, ProtocolUntrustedIdentityException, InvalidMessageStructureException {
|
||||||
|
@ -2611,7 +2403,7 @@ public class Manager implements Closeable {
|
||||||
.retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE);
|
.retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendGroups() throws IOException, UntrustedIdentityException {
|
void sendGroups() throws IOException {
|
||||||
var groupsFile = IOUtils.createTempFile();
|
var groupsFile = IOUtils.createTempFile();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -2645,7 +2437,7 @@ public class Manager implements Closeable {
|
||||||
.withLength(groupsFile.length())
|
.withLength(groupsFile.length())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
sendSyncMessage(SignalServiceSyncMessage.forGroups(attachmentStream));
|
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forGroups(attachmentStream));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -2657,7 +2449,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendContacts() throws IOException, UntrustedIdentityException {
|
public void sendContacts() throws IOException {
|
||||||
var contactsFile = IOUtils.createTempFile();
|
var contactsFile = IOUtils.createTempFile();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -2713,7 +2505,8 @@ public class Manager implements Closeable {
|
||||||
.withLength(contactsFile.length())
|
.withLength(contactsFile.length())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
sendSyncMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, true)));
|
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream,
|
||||||
|
true)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -2725,7 +2518,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendBlockedList() throws IOException, UntrustedIdentityException {
|
void sendBlockedList() throws IOException {
|
||||||
var addresses = new ArrayList<SignalServiceAddress>();
|
var addresses = new ArrayList<SignalServiceAddress>();
|
||||||
for (var record : account.getContactStore().getContacts()) {
|
for (var record : account.getContactStore().getContacts()) {
|
||||||
if (record.second().isBlocked()) {
|
if (record.second().isBlocked()) {
|
||||||
|
@ -2738,17 +2531,17 @@ public class Manager implements Closeable {
|
||||||
groupIds.add(record.getGroupId().serialize());
|
groupIds.add(record.getGroupId().serialize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendSyncMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(addresses, groupIds)));
|
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(addresses, groupIds)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendVerifiedMessage(
|
private void sendVerifiedMessage(
|
||||||
SignalServiceAddress destination, IdentityKey identityKey, TrustLevel trustLevel
|
SignalServiceAddress destination, IdentityKey identityKey, TrustLevel trustLevel
|
||||||
) throws IOException, UntrustedIdentityException {
|
) throws IOException {
|
||||||
var verifiedMessage = new VerifiedMessage(destination,
|
var verifiedMessage = new VerifiedMessage(destination,
|
||||||
identityKey,
|
identityKey,
|
||||||
trustLevel.toVerifiedState(),
|
trustLevel.toVerifiedState(),
|
||||||
System.currentTimeMillis());
|
System.currentTimeMillis());
|
||||||
sendSyncMessage(SignalServiceSyncMessage.forVerified(verifiedMessage));
|
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forVerified(verifiedMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Pair<RecipientId, Contact>> getContacts() {
|
public List<Pair<RecipientId, Contact>> getContacts() {
|
||||||
|
@ -2849,13 +2642,28 @@ public class Manager implements Closeable {
|
||||||
try {
|
try {
|
||||||
var address = account.getRecipientStore().resolveServiceAddress(recipientId);
|
var address = account.getRecipientStore().resolveServiceAddress(recipientId);
|
||||||
sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel);
|
sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel);
|
||||||
} catch (IOException | UntrustedIdentityException e) {
|
} catch (IOException e) {
|
||||||
logger.warn("Failed to send verification sync message: {}", e.getMessage());
|
logger.warn("Failed to send verification sync message: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleIdentityFailure(
|
||||||
|
final RecipientId recipientId, final SendMessageResult.IdentityFailure identityFailure
|
||||||
|
) {
|
||||||
|
final var identityKey = identityFailure.getIdentityKey();
|
||||||
|
if (identityKey != null) {
|
||||||
|
final var newIdentity = account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date());
|
||||||
|
if (newIdentity) {
|
||||||
|
account.getSessionStore().archiveSessions(recipientId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Retrieve profile to get the current identity key from the server
|
||||||
|
retrieveEncryptedProfile(recipientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String computeSafetyNumber(
|
public String computeSafetyNumber(
|
||||||
SignalServiceAddress theirAddress, IdentityKey theirIdentityKey
|
SignalServiceAddress theirAddress, IdentityKey theirIdentityKey
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.groups.GroupId;
|
||||||
|
import org.asamk.signal.manager.storage.groups.GroupInfo;
|
||||||
|
|
||||||
|
public interface GroupProvider {
|
||||||
|
|
||||||
|
GroupInfo getGroup(GroupId groupId);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
||||||
|
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||||
|
|
||||||
|
public interface IdentityFailureHandler {
|
||||||
|
|
||||||
|
void handleIdentityFailure(RecipientId recipientId, SendMessageResult.IdentityFailure identityFailure);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface RecipientRegistrationRefresher {
|
||||||
|
|
||||||
|
RecipientId refreshRecipientRegistration(RecipientId recipientId) throws IOException;
|
||||||
|
}
|
|
@ -0,0 +1,277 @@
|
||||||
|
package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.SignalDependencies;
|
||||||
|
import org.asamk.signal.manager.groups.GroupId;
|
||||||
|
import org.asamk.signal.manager.groups.GroupNotFoundException;
|
||||||
|
import org.asamk.signal.manager.groups.GroupUtils;
|
||||||
|
import org.asamk.signal.manager.groups.NotAGroupMemberException;
|
||||||
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
|
import org.asamk.signal.manager.storage.groups.GroupInfo;
|
||||||
|
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
||||||
|
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
||||||
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
|
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||||
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
|
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||||
|
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
|
||||||
|
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
|
||||||
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
|
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SendHelper {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(SendHelper.class);
|
||||||
|
|
||||||
|
private final SignalAccount account;
|
||||||
|
private final SignalDependencies dependencies;
|
||||||
|
private final UnidentifiedAccessHelper unidentifiedAccessHelper;
|
||||||
|
private final SignalServiceAddressResolver addressResolver;
|
||||||
|
private final RecipientResolver recipientResolver;
|
||||||
|
private final IdentityFailureHandler identityFailureHandler;
|
||||||
|
private final GroupProvider groupProvider;
|
||||||
|
private final RecipientRegistrationRefresher recipientRegistrationRefresher;
|
||||||
|
|
||||||
|
public SendHelper(
|
||||||
|
final SignalAccount account,
|
||||||
|
final SignalDependencies dependencies,
|
||||||
|
final UnidentifiedAccessHelper unidentifiedAccessHelper,
|
||||||
|
final SignalServiceAddressResolver addressResolver,
|
||||||
|
final RecipientResolver recipientResolver,
|
||||||
|
final IdentityFailureHandler identityFailureHandler,
|
||||||
|
final GroupProvider groupProvider,
|
||||||
|
final RecipientRegistrationRefresher recipientRegistrationRefresher
|
||||||
|
) {
|
||||||
|
this.account = account;
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
this.unidentifiedAccessHelper = unidentifiedAccessHelper;
|
||||||
|
this.addressResolver = addressResolver;
|
||||||
|
this.recipientResolver = recipientResolver;
|
||||||
|
this.identityFailureHandler = identityFailureHandler;
|
||||||
|
this.groupProvider = groupProvider;
|
||||||
|
this.recipientRegistrationRefresher = recipientRegistrationRefresher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a single message to one or multiple recipients.
|
||||||
|
* The message is extended with the current expiration timer for each recipient.
|
||||||
|
*/
|
||||||
|
public Pair<Long, List<SendMessageResult>> sendMessage(
|
||||||
|
SignalServiceDataMessage.Builder messageBuilder, Set<RecipientId> recipientIds
|
||||||
|
) throws IOException {
|
||||||
|
// Send to all individually, so sync messages are sent correctly
|
||||||
|
messageBuilder.withProfileKey(account.getProfileKey().serialize());
|
||||||
|
var results = new ArrayList<SendMessageResult>(recipientIds.size());
|
||||||
|
long timestamp = 0;
|
||||||
|
for (var recipientId : recipientIds) {
|
||||||
|
final var contact = account.getContactStore().getContact(recipientId);
|
||||||
|
final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
|
||||||
|
messageBuilder.withExpiration(expirationTime);
|
||||||
|
|
||||||
|
final var singleMessage = messageBuilder.build();
|
||||||
|
timestamp = singleMessage.getTimestamp();
|
||||||
|
final var result = sendMessage(singleMessage, recipientId);
|
||||||
|
handlePossibleIdentityFailure(result);
|
||||||
|
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
|
return new Pair<>(timestamp, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a group message to the given group
|
||||||
|
* The message is extended with the current expiration timer for the group and the group context.
|
||||||
|
*/
|
||||||
|
public Pair<Long, List<SendMessageResult>> sendAsGroupMessage(
|
||||||
|
SignalServiceDataMessage.Builder messageBuilder, GroupId groupId
|
||||||
|
) throws IOException, GroupNotFoundException, NotAGroupMemberException {
|
||||||
|
final var g = getGroupForSending(groupId);
|
||||||
|
GroupUtils.setGroupContext(messageBuilder, g);
|
||||||
|
messageBuilder.withExpiration(g.getMessageExpirationTime());
|
||||||
|
|
||||||
|
final var recipients = g.getMembersWithout(account.getSelfRecipientId());
|
||||||
|
return sendGroupMessage(messageBuilder.build(), recipients);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a complete group message to the given recipients (should be current/old/new members)
|
||||||
|
* This method should only be used for create/update/quit group messages.
|
||||||
|
*/
|
||||||
|
public Pair<Long, List<SendMessageResult>> sendGroupMessage(
|
||||||
|
final SignalServiceDataMessage message, final Set<RecipientId> recipientIds
|
||||||
|
) throws IOException {
|
||||||
|
List<SendMessageResult> result = sendGroupMessageInternal(message, recipientIds);
|
||||||
|
|
||||||
|
for (var r : result) {
|
||||||
|
handlePossibleIdentityFailure(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pair<>(message.getTimestamp(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendReceiptMessage(
|
||||||
|
final SignalServiceReceiptMessage receiptMessage, final RecipientId recipientId
|
||||||
|
) throws IOException, UntrustedIdentityException {
|
||||||
|
final var messageSender = dependencies.getMessageSender();
|
||||||
|
messageSender.sendReceipt(addressResolver.resolveSignalServiceAddress(recipientId),
|
||||||
|
unidentifiedAccessHelper.getAccessFor(recipientId),
|
||||||
|
receiptMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException {
|
||||||
|
var messageSender = dependencies.getMessageSender();
|
||||||
|
|
||||||
|
final var address = addressResolver.resolveSignalServiceAddress(recipientId);
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
return messageSender.sendNullMessage(address, unidentifiedAccessHelper.getAccessFor(recipientId));
|
||||||
|
} catch (UnregisteredUserException e) {
|
||||||
|
final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId);
|
||||||
|
final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId);
|
||||||
|
return messageSender.sendNullMessage(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId));
|
||||||
|
}
|
||||||
|
} catch (UntrustedIdentityException e) {
|
||||||
|
return SendMessageResult.identityFailure(address, e.getIdentityKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pair<Long, SendMessageResult> sendSelfMessage(
|
||||||
|
SignalServiceDataMessage.Builder messageBuilder
|
||||||
|
) throws IOException {
|
||||||
|
final var recipientId = account.getSelfRecipientId();
|
||||||
|
final var contact = account.getContactStore().getContact(recipientId);
|
||||||
|
final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
|
||||||
|
messageBuilder.withExpiration(expirationTime);
|
||||||
|
|
||||||
|
var message = messageBuilder.build();
|
||||||
|
final var result = sendSelfMessage(message);
|
||||||
|
return new Pair<>(message.getTimestamp(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendMessageResult sendSyncMessage(SignalServiceSyncMessage message) throws IOException {
|
||||||
|
var messageSender = dependencies.getMessageSender();
|
||||||
|
try {
|
||||||
|
return messageSender.sendSyncMessage(message, unidentifiedAccessHelper.getAccessForSync());
|
||||||
|
} catch (UntrustedIdentityException e) {
|
||||||
|
var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId());
|
||||||
|
return SendMessageResult.identityFailure(address, e.getIdentityKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendTypingMessage(
|
||||||
|
SignalServiceTypingMessage message, Set<RecipientId> recipientIds
|
||||||
|
) throws IOException, UntrustedIdentityException {
|
||||||
|
var messageSender = dependencies.getMessageSender();
|
||||||
|
for (var recipientId : recipientIds) {
|
||||||
|
final var address = addressResolver.resolveSignalServiceAddress(recipientId);
|
||||||
|
try {
|
||||||
|
messageSender.sendTyping(address, unidentifiedAccessHelper.getAccessFor(recipientId), message);
|
||||||
|
} catch (UnregisteredUserException e) {
|
||||||
|
final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId);
|
||||||
|
final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId);
|
||||||
|
messageSender.sendTyping(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId), message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendGroupTypingMessage(
|
||||||
|
SignalServiceTypingMessage message, GroupId groupId
|
||||||
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException {
|
||||||
|
final var g = getGroupForSending(groupId);
|
||||||
|
final var messageSender = dependencies.getMessageSender();
|
||||||
|
final var recipientIdList = new ArrayList<>(g.getMembersWithout(account.getSelfRecipientId()));
|
||||||
|
final var addresses = recipientIdList.stream()
|
||||||
|
.map(addressResolver::resolveSignalServiceAddress)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
messageSender.sendTyping(addresses, unidentifiedAccessHelper.getAccessFor(recipientIdList), message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException {
|
||||||
|
var g = groupProvider.getGroup(groupId);
|
||||||
|
if (g == null) {
|
||||||
|
throw new GroupNotFoundException(groupId);
|
||||||
|
}
|
||||||
|
if (!g.isMember(account.getSelfRecipientId())) {
|
||||||
|
throw new NotAGroupMemberException(groupId, g.getTitle());
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SendMessageResult> sendGroupMessageInternal(
|
||||||
|
final SignalServiceDataMessage message, final Set<RecipientId> recipientIds
|
||||||
|
) throws IOException {
|
||||||
|
try {
|
||||||
|
var messageSender = dependencies.getMessageSender();
|
||||||
|
// isRecipientUpdate is true if we've already sent this message to some recipients in the past, otherwise false.
|
||||||
|
final var isRecipientUpdate = false;
|
||||||
|
final var recipientIdList = new ArrayList<>(recipientIds);
|
||||||
|
final var addresses = recipientIdList.stream()
|
||||||
|
.map(addressResolver::resolveSignalServiceAddress)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return messageSender.sendDataMessage(addresses,
|
||||||
|
unidentifiedAccessHelper.getAccessFor(recipientIdList),
|
||||||
|
isRecipientUpdate,
|
||||||
|
ContentHint.DEFAULT,
|
||||||
|
message,
|
||||||
|
sendResult -> logger.trace("Partial message send result: {}", sendResult.isSuccess()),
|
||||||
|
() -> false);
|
||||||
|
} catch (UntrustedIdentityException e) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SendMessageResult sendMessage(
|
||||||
|
SignalServiceDataMessage message, RecipientId recipientId
|
||||||
|
) throws IOException {
|
||||||
|
var messageSender = dependencies.getMessageSender();
|
||||||
|
|
||||||
|
final var address = addressResolver.resolveSignalServiceAddress(recipientId);
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
return messageSender.sendDataMessage(address,
|
||||||
|
unidentifiedAccessHelper.getAccessFor(recipientId),
|
||||||
|
ContentHint.DEFAULT,
|
||||||
|
message);
|
||||||
|
} catch (UnregisteredUserException e) {
|
||||||
|
final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId);
|
||||||
|
return messageSender.sendDataMessage(addressResolver.resolveSignalServiceAddress(newRecipientId),
|
||||||
|
unidentifiedAccessHelper.getAccessFor(newRecipientId),
|
||||||
|
ContentHint.DEFAULT,
|
||||||
|
message);
|
||||||
|
}
|
||||||
|
} catch (UntrustedIdentityException e) {
|
||||||
|
return SendMessageResult.identityFailure(address, e.getIdentityKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException {
|
||||||
|
var address = account.getSelfAddress();
|
||||||
|
var transcript = new SentTranscriptMessage(Optional.of(address),
|
||||||
|
message.getTimestamp(),
|
||||||
|
message,
|
||||||
|
message.getExpiresInSeconds(),
|
||||||
|
Map.of(address, true),
|
||||||
|
false);
|
||||||
|
var syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript);
|
||||||
|
|
||||||
|
return sendSyncMessage(syncMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePossibleIdentityFailure(final SendMessageResult r) {
|
||||||
|
if (r.getIdentityFailure() != null) {
|
||||||
|
final var recipientId = recipientResolver.resolveRecipient(r.getAddress());
|
||||||
|
identityFailureHandler.handleIdentityFailure(recipientId, r.getIdentityFailure());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,9 +6,7 @@ import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
import org.asamk.signal.OutputWriter;
|
import org.asamk.signal.OutputWriter;
|
||||||
import org.asamk.signal.commands.exceptions.CommandException;
|
import org.asamk.signal.commands.exceptions.CommandException;
|
||||||
import org.asamk.signal.commands.exceptions.IOErrorException;
|
import org.asamk.signal.commands.exceptions.IOErrorException;
|
||||||
import org.asamk.signal.commands.exceptions.UntrustedKeyErrorException;
|
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -30,8 +28,6 @@ public class SendContactsCommand implements JsonRpcLocalCommand {
|
||||||
) throws CommandException {
|
) throws CommandException {
|
||||||
try {
|
try {
|
||||||
m.sendContacts();
|
m.sendContacts();
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
throw new UntrustedKeyErrorException("SendContacts error: " + e.getMessage());
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IOErrorException("SendContacts error: " + e.getMessage());
|
throw new IOErrorException("SendContacts error: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue