Update libsignal-service-java

This commit is contained in:
AsamK 2021-08-26 21:23:30 +02:00
parent 85c5caeaca
commit 8bcd8d87d2
52 changed files with 692 additions and 551 deletions

View file

@ -14,7 +14,7 @@ repositories {
}
dependencies {
api("com.github.turasa:signal-service-java:2.15.3_unofficial_25")
api("com.github.turasa:signal-service-java:2.15.3_unofficial_26")
implementation("com.google.protobuf:protobuf-javalite:3.10.0")
implementation("org.bouncycastle:bcprov-jdk15on:1.69")
implementation("org.slf4j:slf4j-api:1.7.30")

View file

@ -82,7 +82,7 @@ public class AvatarStore {
}
private String getLegacyIdentifier(final SignalServiceAddress address) {
return address.getNumber().or(() -> address.getUuid().get().toString());
return address.getNumber().or(() -> address.getUuid().toString());
}
private File getProfileAvatarFile(SignalServiceAddress address) {

View file

@ -73,7 +73,6 @@ import org.whispersystems.libsignal.state.SignedPreKeyRecord;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalSessionLock;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
@ -83,6 +82,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
@ -200,7 +200,7 @@ public class Manager implements Closeable {
dependencies,
unidentifiedAccessHelper,
this::resolveSignalServiceAddress,
this::resolveRecipient,
account.getRecipientStore(),
this::handleIdentityFailure,
this::getGroup,
this::refreshRegisteredUser);
@ -211,15 +211,14 @@ public class Manager implements Closeable {
groupV2Helper,
avatarStore,
this::resolveSignalServiceAddress,
this::resolveRecipient);
account.getRecipientStore());
this.contactHelper = new ContactHelper(account);
this.syncHelper = new SyncHelper(account,
attachmentHelper,
sendHelper,
groupHelper,
avatarStore,
this::resolveSignalServiceAddress,
this::resolveRecipient);
this::resolveSignalServiceAddress);
this.context = new Context(account,
dependencies.getAccountManager(),
@ -233,7 +232,8 @@ public class Manager implements Closeable {
this.incomingMessageHandler = new IncomingMessageHandler(account,
dependencies,
this::resolveRecipient,
account.getRecipientStore(),
this::resolveSignalServiceAddress,
groupHelper,
contactHelper,
attachmentHelper,
@ -328,7 +328,7 @@ public class Manager implements Closeable {
public Map<String, Pair<String, UUID>> areUsersRegistered(Set<String> numbers) throws IOException {
Map<String, String> canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> {
try {
return canonicalizePhoneNumber(n);
return PhoneNumberFormatter.formatNumber(n, account.getUsername());
} catch (InvalidNumberException e) {
return "";
}
@ -490,7 +490,7 @@ public class Manager implements Closeable {
public SendGroupMessageResults quitGroup(
GroupId groupId, Set<RecipientIdentifier.Single> groupAdmins
) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException {
final var newAdmins = getRecipientIds(groupAdmins);
final var newAdmins = resolveRecipients(groupAdmins);
return groupHelper.quitGroup(groupId, newAdmins);
}
@ -501,7 +501,7 @@ public class Manager implements Closeable {
public Pair<GroupId, SendGroupMessageResults> createGroup(
String name, Set<RecipientIdentifier.Single> members, File avatarFile
) throws IOException, AttachmentInvalidException {
return groupHelper.createGroup(name, members == null ? null : getRecipientIds(members), avatarFile);
return groupHelper.createGroup(name, members == null ? null : resolveRecipients(members), avatarFile);
}
public SendGroupMessageResults updateGroup(
@ -523,10 +523,10 @@ public class Manager implements Closeable {
return groupHelper.updateGroup(groupId,
name,
description,
members == null ? null : getRecipientIds(members),
removeMembers == null ? null : getRecipientIds(removeMembers),
admins == null ? null : getRecipientIds(admins),
removeAdmins == null ? null : getRecipientIds(removeAdmins),
members == null ? null : resolveRecipients(members),
removeMembers == null ? null : resolveRecipients(removeMembers),
admins == null ? null : resolveRecipients(admins),
removeAdmins == null ? null : resolveRecipients(removeAdmins),
resetGroupLink,
groupLinkState,
addMemberPermission,
@ -662,7 +662,7 @@ public class Manager implements Closeable {
public void setContactName(
RecipientIdentifier.Single recipient, String name
) throws NotMasterDeviceException {
) throws NotMasterDeviceException, UnregisteredUserException {
if (!account.isMasterDevice()) {
throw new NotMasterDeviceException();
}
@ -755,53 +755,28 @@ public class Manager implements Closeable {
return certificate;
}
private Set<RecipientId> getRecipientIds(Collection<RecipientIdentifier.Single> recipients) {
final var signalServiceAddresses = new HashSet<SignalServiceAddress>(recipients.size());
final var addressesMissingUuid = new HashSet<SignalServiceAddress>();
for (var number : recipients) {
final var resolvedAddress = resolveSignalServiceAddress(resolveRecipient(number));
if (resolvedAddress.getUuid().isPresent()) {
signalServiceAddresses.add(resolvedAddress);
} else {
addressesMissingUuid.add(resolvedAddress);
}
}
final var numbersMissingUuid = addressesMissingUuid.stream()
.map(a -> a.getNumber().get())
.collect(Collectors.toSet());
Map<String, UUID> registeredUsers;
try {
registeredUsers = getRegisteredUsers(numbersMissingUuid);
} catch (IOException e) {
logger.warn("Failed to resolve uuids from server, ignoring: {}", e.getMessage());
registeredUsers = Map.of();
}
for (var address : addressesMissingUuid) {
final var number = address.getNumber().get();
if (registeredUsers.containsKey(number)) {
final var newAddress = resolveSignalServiceAddress(resolveRecipientTrusted(new SignalServiceAddress(
registeredUsers.get(number),
number)));
signalServiceAddresses.add(newAddress);
} else {
signalServiceAddresses.add(address);
}
}
return signalServiceAddresses.stream().map(this::resolveRecipient).collect(Collectors.toSet());
}
private RecipientId refreshRegisteredUser(RecipientId recipientId) throws IOException {
final var address = resolveSignalServiceAddress(recipientId);
if (!address.getNumber().isPresent()) {
return recipientId;
}
final var number = address.getNumber().get();
final var uuidMap = getRegisteredUsers(Set.of(number));
return resolveRecipientTrusted(new SignalServiceAddress(uuidMap.getOrDefault(number, null), number));
final var uuid = getRegisteredUser(number);
return resolveRecipientTrusted(new SignalServiceAddress(uuid, number));
}
private UUID getRegisteredUser(final String number) throws IOException {
final Map<String, UUID> uuidMap;
try {
uuidMap = getRegisteredUsers(Set.of(number));
} catch (NumberFormatException e) {
throw new UnregisteredUserException(number, e);
}
final var uuid = uuidMap.get(number);
if (uuid == null) {
throw new UnregisteredUserException(number, null);
}
return uuid;
}
private Map<String, UUID> getRegisteredUsers(final Set<String> numbers) throws IOException {
@ -856,9 +831,9 @@ public class Manager implements Closeable {
cachedMessage.delete();
return null;
}
if (!envelope.hasSource()) {
if (!envelope.hasSourceUuid()) {
final var identifier = e.getSender();
final var recipientId = resolveRecipient(identifier);
final var recipientId = account.getRecipientStore().resolveRecipient(identifier);
try {
account.getMessageCache().replaceSender(cachedMessage, recipientId);
} catch (IOException ioException) {
@ -901,8 +876,8 @@ public class Manager implements Closeable {
logger.debug("Checking for new message from server");
try {
var result = signalWebSocket.readOrEmpty(unit.toMillis(timeout), envelope1 -> {
final var recipientId = envelope1.hasSource()
? resolveRecipient(envelope1.getSourceIdentifier())
final var recipientId = envelope1.hasSourceUuid()
? resolveRecipient(envelope1.getSourceAddress())
: null;
// store message on disk, before acknowledging receipt to the server
cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId);
@ -944,10 +919,10 @@ public class Manager implements Closeable {
handleQueuedActions(queuedActions);
}
if (cachedMessage[0] != null) {
if (exception instanceof ProtocolUntrustedIdentityException) {
final var identifier = ((ProtocolUntrustedIdentityException) exception).getSender();
final var recipientId = resolveRecipient(identifier);
if (!envelope.hasSource()) {
if (exception instanceof UntrustedIdentityException) {
final var address = ((UntrustedIdentityException) exception).getSender();
final var recipientId = resolveRecipient(address);
if (!envelope.hasSourceUuid()) {
try {
cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId);
} catch (IOException ioException) {
@ -977,7 +952,12 @@ public class Manager implements Closeable {
}
public boolean isContactBlocked(final RecipientIdentifier.Single recipient) {
final var recipientId = resolveRecipient(recipient);
final RecipientId recipientId;
try {
recipientId = resolveRecipient(recipient);
} catch (UnregisteredUserException e) {
return false;
}
return contactHelper.isContactBlocked(recipientId);
}
@ -994,7 +974,12 @@ public class Manager implements Closeable {
}
public String getContactOrProfileName(RecipientIdentifier.Single recipientIdentifier) {
final var recipientId = resolveRecipient(recipientIdentifier);
final RecipientId recipientId;
try {
recipientId = resolveRecipient(recipientIdentifier);
} catch (UnregisteredUserException e) {
return null;
}
final var contact = account.getContactStore().getContact(recipientId);
if (contact != null && !Util.isEmpty(contact.getName())) {
@ -1018,7 +1003,12 @@ public class Manager implements Closeable {
}
public List<IdentityInfo> getIdentities(RecipientIdentifier.Single recipient) {
final var identity = account.getIdentityKeyStore().getIdentity(resolveRecipient(recipient));
IdentityInfo identity;
try {
identity = account.getIdentityKeyStore().getIdentity(resolveRecipient(recipient));
} catch (UnregisteredUserException e) {
identity = null;
}
return identity == null ? List.of() : List.of(identity);
}
@ -1029,7 +1019,12 @@ public class Manager implements Closeable {
* @param fingerprint Fingerprint
*/
public boolean trustIdentityVerified(RecipientIdentifier.Single recipient, byte[] fingerprint) {
var recipientId = resolveRecipient(recipient);
RecipientId recipientId;
try {
recipientId = resolveRecipient(recipient);
} catch (UnregisteredUserException e) {
return false;
}
return trustIdentity(recipientId,
identityKey -> Arrays.equals(identityKey.serialize(), fingerprint),
TrustLevel.TRUSTED_VERIFIED);
@ -1042,8 +1037,13 @@ public class Manager implements Closeable {
* @param safetyNumber Safety number
*/
public boolean trustIdentityVerifiedSafetyNumber(RecipientIdentifier.Single recipient, String safetyNumber) {
var recipientId = resolveRecipient(recipient);
var address = account.getRecipientStore().resolveServiceAddress(recipientId);
RecipientId recipientId;
try {
recipientId = resolveRecipient(recipient);
} catch (UnregisteredUserException e) {
return false;
}
var address = resolveSignalServiceAddress(recipientId);
return trustIdentity(recipientId,
identityKey -> safetyNumber.equals(computeSafetyNumber(address, identityKey)),
TrustLevel.TRUSTED_VERIFIED);
@ -1056,8 +1056,13 @@ public class Manager implements Closeable {
* @param safetyNumber Scannable safety number
*/
public boolean trustIdentityVerifiedSafetyNumber(RecipientIdentifier.Single recipient, byte[] safetyNumber) {
var recipientId = resolveRecipient(recipient);
var address = account.getRecipientStore().resolveServiceAddress(recipientId);
RecipientId recipientId;
try {
recipientId = resolveRecipient(recipient);
} catch (UnregisteredUserException e) {
return false;
}
var address = resolveSignalServiceAddress(recipientId);
return trustIdentity(recipientId, identityKey -> {
final var fingerprint = computeSafetyNumberFingerprint(address, identityKey);
try {
@ -1074,7 +1079,12 @@ public class Manager implements Closeable {
* @param recipient username of the identity
*/
public boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient) {
var recipientId = resolveRecipient(recipient);
RecipientId recipientId;
try {
recipientId = resolveRecipient(recipient);
} catch (UnregisteredUserException e) {
return false;
}
return trustIdentity(recipientId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED);
}
@ -1092,7 +1102,7 @@ public class Manager implements Closeable {
account.getIdentityKeyStore().setIdentityTrustLevel(recipientId, identity.getIdentityKey(), trustLevel);
try {
var address = account.getRecipientStore().resolveServiceAddress(recipientId);
var address = resolveSignalServiceAddress(recipientId);
syncHelper.sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel);
} catch (IOException e) {
logger.warn("Failed to send verification sync message: {}", e.getMessage());
@ -1136,48 +1146,61 @@ public class Manager implements Closeable {
theirIdentityKey);
}
@Deprecated
public SignalServiceAddress resolveSignalServiceAddress(String identifier) {
var address = Utils.getSignalServiceAddressFromIdentifier(identifier);
return resolveSignalServiceAddress(address);
}
@Deprecated
public SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address) {
if (address.matches(account.getSelfAddress())) {
return account.getSelfAddress();
}
return account.getRecipientStore().resolveServiceAddress(address);
return resolveSignalServiceAddress(resolveRecipient(address));
}
public SignalServiceAddress resolveSignalServiceAddress(UUID uuid) {
return resolveSignalServiceAddress(account.getRecipientStore().resolveRecipient(uuid));
}
public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) {
return account.getRecipientStore().resolveServiceAddress(recipientId);
}
private String canonicalizePhoneNumber(final String number) throws InvalidNumberException {
return PhoneNumberFormatter.formatNumber(number, account.getUsername());
}
private RecipientId resolveRecipient(final String identifier) {
var address = Utils.getSignalServiceAddressFromIdentifier(identifier);
return resolveRecipient(address);
}
private RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) {
final SignalServiceAddress address;
if (recipient instanceof RecipientIdentifier.Uuid) {
address = new SignalServiceAddress(((RecipientIdentifier.Uuid) recipient).uuid, null);
} else {
address = new SignalServiceAddress(null, ((RecipientIdentifier.Number) recipient).number);
final var address = account.getRecipientStore().resolveRecipientAddress(recipientId);
if (address.getUuid().isPresent()) {
return address.toSignalServiceAddress();
}
return resolveRecipient(address);
// Address in recipient store doesn't have a uuid, this shouldn't happen
// Try to retrieve the uuid from the server
final var number = address.getNumber().get();
try {
return resolveSignalServiceAddress(getRegisteredUser(number));
} catch (IOException e) {
logger.warn("Failed to get uuid for e164 number: {}", number, e);
// Return SignalServiceAddress with unknown UUID
return address.toSignalServiceAddress();
}
}
public RecipientId resolveRecipient(SignalServiceAddress address) {
private Set<RecipientId> resolveRecipients(Collection<RecipientIdentifier.Single> recipients) throws UnregisteredUserException {
final var recipientIds = new HashSet<RecipientId>(recipients.size());
for (var number : recipients) {
final var recipientId = resolveRecipient(number);
recipientIds.add(recipientId);
}
return recipientIds;
}
private RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws UnregisteredUserException {
if (recipient instanceof RecipientIdentifier.Uuid) {
return account.getRecipientStore().resolveRecipient(((RecipientIdentifier.Uuid) recipient).uuid);
} else {
final var number = ((RecipientIdentifier.Number) recipient).number;
return account.getRecipientStore().resolveRecipient(number, () -> {
try {
return getRegisteredUser(number);
} catch (IOException e) {
return null;
}
});
}
}
private RecipientId resolveRecipient(SignalServiceAddress address) {
return account.getRecipientStore().resolveRecipient(address);
}

View file

@ -0,0 +1,27 @@
package org.asamk.signal.manager;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
public class UntrustedIdentityException extends Exception {
private final SignalServiceAddress sender;
private final Integer senderDevice;
public UntrustedIdentityException(final SignalServiceAddress sender) {
this(sender, null);
}
public UntrustedIdentityException(final SignalServiceAddress sender, final Integer senderDevice) {
super("Untrusted identity: " + sender.getIdentifier());
this.sender = sender;
this.senderDevice = senderDevice;
}
public SignalServiceAddress getSender() {
return sender;
}
public Integer getSenderDevice() {
return senderDevice;
}
}

View file

@ -32,9 +32,7 @@ public abstract class RecipientIdentifier {
}
public static Single fromAddress(SignalServiceAddress address) {
return address.getUuid().isPresent()
? new Uuid(address.getUuid().get())
: new Number(address.getNumber().get());
return new Uuid(address.getUuid());
}
}

View file

@ -150,11 +150,9 @@ public class GroupV2Helper {
if (!areMembersValid(members)) return null;
var self = new GroupCandidate(addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId())
.getUuid()
.orNull(), Optional.fromNullable(profileKeyCredential));
var self = new GroupCandidate(getSelfUuid(), Optional.fromNullable(profileKeyCredential));
var candidates = members.stream()
.map(member -> new GroupCandidate(addressResolver.resolveSignalServiceAddress(member).getUuid().get(),
.map(member -> new GroupCandidate(addressResolver.resolveSignalServiceAddress(member).getUuid(),
Optional.fromNullable(profileKeyCredentialProvider.getProfileKeyCredential(member))))
.collect(Collectors.toSet());
@ -169,18 +167,6 @@ public class GroupV2Helper {
}
private boolean areMembersValid(final Set<RecipientId> members) {
final var noUuidCapability = members.stream()
.map(addressResolver::resolveSignalServiceAddress)
.filter(address -> !address.getUuid().isPresent())
.map(SignalServiceAddress::getNumber)
.map(Optional::get)
.collect(Collectors.toSet());
if (noUuidCapability.size() > 0) {
logger.warn("Cannot create a V2 group as some members don't have a UUID: {}",
String.join(", ", noUuidCapability));
return false;
}
final var noGv2Capability = members.stream()
.map(profileProvider::getProfile)
.filter(profile -> profile != null && !profile.getCapabilities().contains(Profile.Capability.gv2))
@ -214,11 +200,8 @@ public class GroupV2Helper {
change.setModifyAvatar(GroupChange.Actions.ModifyAvatarAction.newBuilder().setAvatar(avatarCdnKey));
}
final var uuid = addressResolver.resolveSignalServiceAddress(this.selfRecipientIdProvider.getSelfRecipientId())
.getUuid();
if (uuid.isPresent()) {
change.setSourceUuid(UuidUtil.toByteString(uuid.get()));
}
final var uuid = getSelfUuid();
change.setSourceUuid(UuidUtil.toByteString(uuid));
return commitChange(groupInfoV2, change);
}
@ -233,13 +216,11 @@ public class GroupV2Helper {
}
var candidates = newMembers.stream()
.map(member -> new GroupCandidate(addressResolver.resolveSignalServiceAddress(member).getUuid().get(),
.map(member -> new GroupCandidate(addressResolver.resolveSignalServiceAddress(member).getUuid(),
Optional.fromNullable(profileKeyCredentialProvider.getProfileKeyCredential(member))))
.collect(Collectors.toSet());
final var uuid = addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId())
.getUuid()
.get();
final var uuid = getSelfUuid();
final var change = groupOperations.createModifyGroupMembershipChange(candidates, uuid);
change.setSourceUuid(UuidUtil.toByteString(uuid));
@ -251,9 +232,7 @@ public class GroupV2Helper {
GroupInfoV2 groupInfoV2, Set<RecipientId> membersToMakeAdmin
) throws IOException {
var pendingMembersList = groupInfoV2.getGroup().getPendingMembersList();
final var selfUuid = addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId())
.getUuid()
.get();
final var selfUuid = getSelfUuid();
var selfPendingMember = DecryptedGroupUtil.findPendingByUuid(pendingMembersList, selfUuid);
if (selfPendingMember.isPresent()) {
@ -263,7 +242,6 @@ public class GroupV2Helper {
final var adminUuids = membersToMakeAdmin.stream()
.map(addressResolver::resolveSignalServiceAddress)
.map(SignalServiceAddress::getUuid)
.map(Optional::get)
.collect(Collectors.toList());
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
return commitChange(groupInfoV2, groupOperations.createLeaveAndPromoteMembersToAdmin(selfUuid, adminUuids));
@ -275,8 +253,6 @@ public class GroupV2Helper {
final var memberUuids = members.stream()
.map(addressResolver::resolveSignalServiceAddress)
.map(SignalServiceAddress::getUuid)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
return ejectMembers(groupInfoV2, memberUuids);
}
@ -288,8 +264,6 @@ public class GroupV2Helper {
final var memberUuids = members.stream()
.map(addressResolver::resolveSignalServiceAddress)
.map(SignalServiceAddress::getUuid)
.filter(Optional::isPresent)
.map(Optional::get)
.map(uuid -> DecryptedGroupUtil.findPendingByUuid(pendingMembersList, uuid))
.filter(Optional::isPresent)
.map(Optional::get)
@ -360,8 +334,7 @@ public class GroupV2Helper {
: groupOperations.createGroupJoinDirect(profileKeyCredential);
change.setSourceUuid(UuidUtil.toByteString(addressResolver.resolveSignalServiceAddress(selfRecipientId)
.getUuid()
.get()));
.getUuid()));
return commitChange(groupSecretParams, decryptedGroupJoinInfo.getRevision(), change, groupLinkPassword);
}
@ -378,9 +351,7 @@ public class GroupV2Helper {
final var change = groupOperations.createAcceptInviteChange(profileKeyCredential);
final var uuid = addressResolver.resolveSignalServiceAddress(selfRecipientId).getUuid();
if (uuid.isPresent()) {
change.setSourceUuid(UuidUtil.toByteString(uuid.get()));
}
change.setSourceUuid(UuidUtil.toByteString(uuid));
return commitChange(groupInfoV2, change);
}
@ -391,7 +362,7 @@ public class GroupV2Helper {
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
final var address = addressResolver.resolveSignalServiceAddress(recipientId);
final var newRole = admin ? Member.Role.ADMINISTRATOR : Member.Role.DEFAULT;
final var change = groupOperations.createChangeMemberRole(address.getUuid().get(), newRole);
final var change = groupOperations.createChangeMemberRole(address.getUuid(), newRole);
return commitChange(groupInfoV2, change);
}
@ -473,10 +444,7 @@ public class GroupV2Helper {
final DecryptedGroup decryptedGroupState;
try {
decryptedChange = groupOperations.decryptChange(changeActions,
addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId())
.getUuid()
.get());
decryptedChange = groupOperations.decryptChange(changeActions, getSelfUuid());
decryptedGroupState = DecryptedGroupUtil.apply(previousGroupState, decryptedChange);
} catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) {
throw new IOException(e);
@ -543,13 +511,15 @@ public class GroupV2Helper {
final var credentials = groupsV2Api.getCredentials(today);
// TODO cache credentials until they expire
var authCredentialResponse = credentials.get(today);
final var uuid = addressResolver.resolveSignalServiceAddress(this.selfRecipientIdProvider.getSelfRecipientId())
.getUuid()
.get();
final var uuid = getSelfUuid();
try {
return groupsV2Api.getGroupsV2AuthorizationString(uuid, today, groupSecretParams, authCredentialResponse);
} catch (VerificationFailedException e) {
throw new IOException(e);
}
}
private UUID getSelfUuid() {
return addressResolver.resolveSignalServiceAddress(this.selfRecipientIdProvider.getSelfRecipientId()).getUuid();
}
}

View file

@ -4,6 +4,7 @@ import org.asamk.signal.manager.JobExecutor;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.SignalDependencies;
import org.asamk.signal.manager.TrustLevel;
import org.asamk.signal.manager.UntrustedIdentityException;
import org.asamk.signal.manager.actions.HandleAction;
import org.asamk.signal.manager.actions.RenewSessionAction;
import org.asamk.signal.manager.actions.RetrieveProfileAction;
@ -34,6 +35,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
@ -48,6 +50,7 @@ public final class IncomingMessageHandler {
private final SignalAccount account;
private final SignalDependencies dependencies;
private final RecipientResolver recipientResolver;
private final SignalServiceAddressResolver addressResolver;
private final GroupHelper groupHelper;
private final ContactHelper contactHelper;
private final AttachmentHelper attachmentHelper;
@ -58,6 +61,7 @@ public final class IncomingMessageHandler {
final SignalAccount account,
final SignalDependencies dependencies,
final RecipientResolver recipientResolver,
final SignalServiceAddressResolver addressResolver,
final GroupHelper groupHelper,
final ContactHelper contactHelper,
final AttachmentHelper attachmentHelper,
@ -67,6 +71,7 @@ public final class IncomingMessageHandler {
this.account = account;
this.dependencies = dependencies;
this.recipientResolver = recipientResolver;
this.addressResolver = addressResolver;
this.groupHelper = groupHelper;
this.contactHelper = contactHelper;
this.attachmentHelper = attachmentHelper;
@ -80,7 +85,7 @@ public final class IncomingMessageHandler {
final Manager.ReceiveMessageHandler handler
) {
final var actions = new ArrayList<HandleAction>();
if (envelope.hasSource()) {
if (envelope.hasSourceUuid()) {
// Store uuid if we don't have it already
// address/uuid in envelope is sent by server
account.getRecipientStore().resolveRecipientTrusted(envelope.getSourceAddress());
@ -93,6 +98,8 @@ public final class IncomingMessageHandler {
} catch (ProtocolUntrustedIdentityException e) {
final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender());
actions.add(new RetrieveProfileAction(recipientId));
exception = new UntrustedIdentityException(addressResolver.resolveSignalServiceAddress(recipientId),
e.getSenderDevice());
} catch (ProtocolInvalidMessageException e) {
final var sender = account.getRecipientStore().resolveRecipient(e.getSender());
logger.debug("Received invalid message, queuing renew session action.");
@ -102,7 +109,7 @@ public final class IncomingMessageHandler {
exception = e;
}
if (!envelope.hasSource() && content != null) {
if (!envelope.hasSourceUuid() && content != null) {
// Store uuid if we don't have it already
// address/uuid is validated by unidentified sender certificate
account.getRecipientStore().resolveRecipientTrusted(content.getSender());
@ -113,7 +120,7 @@ public final class IncomingMessageHandler {
logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
} else if (isNotAllowedToSendToGroup(envelope, content)) {
logger.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}",
(envelope.hasSource() ? envelope.getSourceAddress() : content.getSender()).getIdentifier(),
(envelope.hasSourceUuid() ? envelope.getSourceAddress() : content.getSender()).getIdentifier(),
envelope.getTimestamp());
} else {
actions.addAll(handleMessage(envelope, content, ignoreAttachments));
@ -126,146 +133,153 @@ public final class IncomingMessageHandler {
SignalServiceEnvelope envelope, SignalServiceContent content, boolean ignoreAttachments
) {
var actions = new ArrayList<HandleAction>();
if (content != null) {
final RecipientId sender;
if (!envelope.isUnidentifiedSender() && envelope.hasSource()) {
sender = recipientResolver.resolveRecipient(envelope.getSourceAddress());
} else {
sender = recipientResolver.resolveRecipient(content.getSender());
if (content == null) {
return actions;
}
final RecipientId sender;
if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
sender = recipientResolver.resolveRecipient(envelope.getSourceAddress());
} else {
sender = recipientResolver.resolveRecipient(content.getSender());
}
if (content.getDataMessage().isPresent()) {
var message = content.getDataMessage().get();
if (content.isNeedsReceipt()) {
actions.add(new SendReceiptAction(sender, message.getTimestamp()));
}
if (content.getDataMessage().isPresent()) {
var message = content.getDataMessage().get();
actions.addAll(handleSignalServiceDataMessage(message,
false,
sender,
account.getSelfRecipientId(),
ignoreAttachments));
}
if (content.isNeedsReceipt()) {
actions.add(new SendReceiptAction(sender, message.getTimestamp()));
}
if (content.getSyncMessage().isPresent()) {
var syncMessage = content.getSyncMessage().get();
actions.addAll(handleSyncMessage(syncMessage, sender, ignoreAttachments));
}
actions.addAll(handleSignalServiceDataMessage(message,
false,
sender,
account.getSelfRecipientId(),
ignoreAttachments));
return actions;
}
private List<HandleAction> handleSyncMessage(
final SignalServiceSyncMessage syncMessage, final RecipientId sender, final boolean ignoreAttachments
) {
var actions = new ArrayList<HandleAction>();
account.setMultiDevice(true);
if (syncMessage.getSent().isPresent()) {
var message = syncMessage.getSent().get();
final var destination = message.getDestination().orNull();
actions.addAll(handleSignalServiceDataMessage(message.getMessage(),
true,
sender,
destination == null ? null : recipientResolver.resolveRecipient(destination),
ignoreAttachments));
}
if (syncMessage.getRequest().isPresent() && account.isMasterDevice()) {
var rm = syncMessage.getRequest().get();
if (rm.isContactsRequest()) {
actions.add(SendSyncContactsAction.create());
}
if (content.getSyncMessage().isPresent()) {
account.setMultiDevice(true);
var syncMessage = content.getSyncMessage().get();
if (syncMessage.getSent().isPresent()) {
var message = syncMessage.getSent().get();
final var destination = message.getDestination().orNull();
actions.addAll(handleSignalServiceDataMessage(message.getMessage(),
true,
sender,
destination == null ? null : recipientResolver.resolveRecipient(destination),
ignoreAttachments));
if (rm.isGroupsRequest()) {
actions.add(SendSyncGroupsAction.create());
}
if (rm.isBlockedListRequest()) {
actions.add(SendSyncBlockedListAction.create());
}
// TODO Handle rm.isConfigurationRequest(); rm.isKeysRequest();
}
if (syncMessage.getGroups().isPresent()) {
logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring.");
}
if (syncMessage.getBlockedList().isPresent()) {
final var blockedListMessage = syncMessage.getBlockedList().get();
for (var address : blockedListMessage.getAddresses()) {
contactHelper.setContactBlocked(recipientResolver.resolveRecipient(address), true);
}
for (var groupId : blockedListMessage.getGroupIds()
.stream()
.map(GroupId::unknownVersion)
.collect(Collectors.toSet())) {
try {
groupHelper.setGroupBlocked(groupId, true);
} catch (GroupNotFoundException e) {
logger.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}",
groupId.toBase64());
}
if (syncMessage.getRequest().isPresent() && account.isMasterDevice()) {
var rm = syncMessage.getRequest().get();
if (rm.isContactsRequest()) {
actions.add(SendSyncContactsAction.create());
}
if (rm.isGroupsRequest()) {
actions.add(SendSyncGroupsAction.create());
}
if (rm.isBlockedListRequest()) {
actions.add(SendSyncBlockedListAction.create());
}
// TODO Handle rm.isConfigurationRequest(); rm.isKeysRequest();
}
}
if (syncMessage.getContacts().isPresent()) {
try {
final var contactsMessage = syncMessage.getContacts().get();
attachmentHelper.retrieveAttachment(contactsMessage.getContactsStream(),
syncHelper::handleSyncDeviceContacts);
} catch (Exception e) {
logger.warn("Failed to handle received sync contacts, ignoring: {}", e.getMessage());
}
}
if (syncMessage.getVerified().isPresent()) {
final var verifiedMessage = syncMessage.getVerified().get();
account.getIdentityKeyStore()
.setIdentityTrustLevel(account.getRecipientStore()
.resolveRecipientTrusted(verifiedMessage.getDestination()),
verifiedMessage.getIdentityKey(),
TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
}
if (syncMessage.getStickerPackOperations().isPresent()) {
final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get();
for (var m : stickerPackOperationMessages) {
if (!m.getPackId().isPresent()) {
continue;
}
if (syncMessage.getGroups().isPresent()) {
try {
final var groupsMessage = syncMessage.getGroups().get();
attachmentHelper.retrieveAttachment(groupsMessage, syncHelper::handleSyncDeviceGroups);
} catch (Exception e) {
logger.warn("Failed to handle received sync groups, ignoring: {}", e.getMessage());
}
}
if (syncMessage.getBlockedList().isPresent()) {
final var blockedListMessage = syncMessage.getBlockedList().get();
for (var address : blockedListMessage.getAddresses()) {
contactHelper.setContactBlocked(recipientResolver.resolveRecipient(address), true);
}
for (var groupId : blockedListMessage.getGroupIds()
.stream()
.map(GroupId::unknownVersion)
.collect(Collectors.toSet())) {
try {
groupHelper.setGroupBlocked(groupId, true);
} catch (GroupNotFoundException e) {
logger.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}",
groupId.toBase64());
}
}
}
if (syncMessage.getContacts().isPresent()) {
try {
final var contactsMessage = syncMessage.getContacts().get();
attachmentHelper.retrieveAttachment(contactsMessage.getContactsStream(),
syncHelper::handleSyncDeviceContacts);
} catch (Exception e) {
logger.warn("Failed to handle received sync contacts, ignoring: {}", e.getMessage());
}
}
if (syncMessage.getVerified().isPresent()) {
final var verifiedMessage = syncMessage.getVerified().get();
account.getIdentityKeyStore()
.setIdentityTrustLevel(account.getRecipientStore()
.resolveRecipientTrusted(verifiedMessage.getDestination()),
verifiedMessage.getIdentityKey(),
TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
}
if (syncMessage.getStickerPackOperations().isPresent()) {
final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get();
for (var m : stickerPackOperationMessages) {
if (!m.getPackId().isPresent()) {
continue;
}
final var stickerPackId = StickerPackId.deserialize(m.getPackId().get());
final var installed = !m.getType().isPresent()
|| m.getType().get() == StickerPackOperationMessage.Type.INSTALL;
final var stickerPackId = StickerPackId.deserialize(m.getPackId().get());
final var installed = !m.getType().isPresent()
|| m.getType().get() == StickerPackOperationMessage.Type.INSTALL;
var sticker = account.getStickerStore().getSticker(stickerPackId);
if (m.getPackKey().isPresent()) {
if (sticker == null) {
sticker = new Sticker(stickerPackId, m.getPackKey().get());
}
if (installed) {
jobExecutor.enqueueJob(new RetrieveStickerPackJob(stickerPackId, m.getPackKey().get()));
}
}
var sticker = account.getStickerStore().getSticker(stickerPackId);
if (m.getPackKey().isPresent()) {
if (sticker == null) {
sticker = new Sticker(stickerPackId, m.getPackKey().get());
}
if (installed) {
jobExecutor.enqueueJob(new RetrieveStickerPackJob(stickerPackId, m.getPackKey().get()));
}
}
if (sticker != null) {
sticker.setInstalled(installed);
account.getStickerStore().updateSticker(sticker);
}
}
if (sticker != null) {
sticker.setInstalled(installed);
account.getStickerStore().updateSticker(sticker);
}
if (syncMessage.getFetchType().isPresent()) {
switch (syncMessage.getFetchType().get()) {
case LOCAL_PROFILE:
actions.add(new RetrieveProfileAction(account.getSelfRecipientId()));
case STORAGE_MANIFEST:
// TODO
}
}
if (syncMessage.getKeys().isPresent()) {
final var keysMessage = syncMessage.getKeys().get();
if (keysMessage.getStorageService().isPresent()) {
final var storageKey = keysMessage.getStorageService().get();
account.setStorageKey(storageKey);
}
}
if (syncMessage.getConfiguration().isPresent()) {
}
}
if (syncMessage.getFetchType().isPresent()) {
switch (syncMessage.getFetchType().get()) {
case LOCAL_PROFILE:
actions.add(new RetrieveProfileAction(account.getSelfRecipientId()));
case STORAGE_MANIFEST:
// TODO
}
}
}
if (syncMessage.getKeys().isPresent()) {
final var keysMessage = syncMessage.getKeys().get();
if (keysMessage.getStorageService().isPresent()) {
final var storageKey = keysMessage.getStorageService().get();
account.setStorageKey(storageKey);
}
}
if (syncMessage.getConfiguration().isPresent()) {
// TODO
}
return actions;
}
private boolean isMessageBlocked(SignalServiceEnvelope envelope, SignalServiceContent content) {
SignalServiceAddress source;
if (!envelope.isUnidentifiedSender() && envelope.hasSource()) {
if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
source = envelope.getSourceAddress();
} else if (content != null) {
source = content.getSender();
@ -290,7 +304,7 @@ public final class IncomingMessageHandler {
private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope, SignalServiceContent content) {
SignalServiceAddress source;
if (!envelope.isUnidentifiedSender() && envelope.hasSource()) {
if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
source = envelope.getSourceAddress();
} else if (content != null) {
source = content.getSender();

View file

@ -273,7 +273,7 @@ public final class ProfileHelper {
private Single<ProfileAndCredential> retrieveProfile(
RecipientId recipientId, SignalServiceProfile.RequestType requestType
) throws IOException {
) {
var unidentifiedAccess = getUnidentifiedAccess(recipientId);
var profileKey = Optional.fromNullable(profileKeyProvider.getProfileKey(recipientId));
@ -286,7 +286,7 @@ public final class ProfileHelper {
Optional<ProfileKey> profileKey,
Optional<UnidentifiedAccess> unidentifiedAccess,
SignalServiceProfile.RequestType requestType
) throws IOException {
) {
var profileService = profileServiceProvider.getProfileService();
Single<ServiceResponse<ProfileAndCredential>> responseSingle;
@ -294,11 +294,7 @@ public final class ProfileHelper {
responseSingle = profileService.getProfile(address, profileKey, unidentifiedAccess, requestType);
} catch (NoClassDefFoundError e) {
// Native zkgroup lib not available for ProfileKey
if (!address.getNumber().isPresent()) {
throw new NotFoundException("Can't request profile without number");
}
var addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber());
responseSingle = profileService.getProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType);
responseSingle = profileService.getProfile(address, Optional.absent(), unidentifiedAccess, requestType);
}
return responseSingle.map(pair -> {

View file

@ -1,6 +1,7 @@
package org.asamk.signal.manager.helper;
import org.asamk.signal.manager.SignalDependencies;
import org.asamk.signal.manager.UntrustedIdentityException;
import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.groups.GroupNotFoundException;
import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
@ -13,8 +14,8 @@ import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
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;
@ -43,6 +44,20 @@ public class SendHelper {
private final GroupProvider groupProvider;
private final RecipientRegistrationRefresher recipientRegistrationRefresher;
private final SignalServiceMessageSender.IndividualSendEvents sendEvents = new SignalServiceMessageSender.IndividualSendEvents() {
@Override
public void onMessageEncrypted() {
}
@Override
public void onMessageSent() {
}
@Override
public void onSyncMessageSent() {
}
};
public SendHelper(
final SignalAccount account,
final SignalDependencies dependencies,
@ -145,9 +160,12 @@ public class SendHelper {
final SignalServiceReceiptMessage receiptMessage, final RecipientId recipientId
) throws IOException, UntrustedIdentityException {
final var messageSender = dependencies.getMessageSender();
messageSender.sendReceipt(addressResolver.resolveSignalServiceAddress(recipientId),
unidentifiedAccessHelper.getAccessFor(recipientId),
receiptMessage);
final var address = addressResolver.resolveSignalServiceAddress(recipientId);
try {
messageSender.sendReceipt(address, unidentifiedAccessHelper.getAccessFor(recipientId), receiptMessage);
} catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
throw new UntrustedIdentityException(address);
}
}
public SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException {
@ -162,7 +180,7 @@ public class SendHelper {
final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId);
return messageSender.sendNullMessage(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId));
}
} catch (UntrustedIdentityException e) {
} catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
return SendMessageResult.identityFailure(address, e.getIdentityKey());
}
}
@ -183,7 +201,7 @@ public class SendHelper {
var messageSender = dependencies.getMessageSender();
try {
return messageSender.sendSyncMessage(message, unidentifiedAccessHelper.getAccessForSync());
} catch (UntrustedIdentityException e) {
} catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId());
return SendMessageResult.identityFailure(address, e.getIdentityKey());
}
@ -195,11 +213,15 @@ public class SendHelper {
var messageSender = dependencies.getMessageSender();
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);
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);
}
} catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
throw new UntrustedIdentityException(address);
}
}
@ -247,7 +269,7 @@ public class SendHelper {
message,
sendResult -> logger.trace("Partial message send result: {}", sendResult.isSuccess()),
() -> false);
} catch (UntrustedIdentityException e) {
} catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
return List.of();
}
}
@ -263,15 +285,17 @@ public class SendHelper {
return messageSender.sendDataMessage(address,
unidentifiedAccessHelper.getAccessFor(recipientId),
ContentHint.DEFAULT,
message);
message,
sendEvents);
} catch (UnregisteredUserException e) {
final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId);
return messageSender.sendDataMessage(addressResolver.resolveSignalServiceAddress(newRecipientId),
unidentifiedAccessHelper.getAccessFor(newRecipientId),
ContentHint.DEFAULT,
message);
message,
sendEvents);
}
} catch (UntrustedIdentityException e) {
} catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
return SendMessageResult.identityFailure(address, e.getIdentityKey());
}
}

View file

@ -2,11 +2,9 @@ package org.asamk.signal.manager.helper;
import org.asamk.signal.manager.AvatarStore;
import org.asamk.signal.manager.TrustLevel;
import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.groups.GroupInfoV1;
import org.asamk.signal.manager.storage.recipients.Contact;
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.asamk.signal.manager.util.AttachmentUtils;
import org.asamk.signal.manager.util.IOUtils;
import org.slf4j.Logger;
@ -21,7 +19,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
@ -36,7 +33,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class SyncHelper {
@ -49,7 +45,6 @@ public class SyncHelper {
private final GroupHelper groupHelper;
private final AvatarStore avatarStore;
private final SignalServiceAddressResolver addressResolver;
private final RecipientResolver recipientResolver;
public SyncHelper(
final SignalAccount account,
@ -57,8 +52,7 @@ public class SyncHelper {
final SendHelper sendHelper,
final GroupHelper groupHelper,
final AvatarStore avatarStore,
final SignalServiceAddressResolver addressResolver,
final RecipientResolver recipientResolver
final SignalServiceAddressResolver addressResolver
) {
this.account = account;
this.attachmentHelper = attachmentHelper;
@ -66,7 +60,6 @@ public class SyncHelper {
this.groupHelper = groupHelper;
this.avatarStore = avatarStore;
this.addressResolver = addressResolver;
this.recipientResolver = recipientResolver;
}
public void requestAllSyncData() throws IOException {
@ -222,48 +215,6 @@ public class SyncHelper {
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forVerified(verifiedMessage));
}
public void handleSyncDeviceGroups(final InputStream input) {
final var s = new DeviceGroupsInputStream(input);
DeviceGroup g;
while (true) {
try {
g = s.read();
} catch (IOException e) {
logger.warn("Sync groups contained invalid group, ignoring: {}", e.getMessage());
continue;
}
if (g == null) {
break;
}
var syncGroup = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(g.getId()));
if (syncGroup != null) {
if (g.getName().isPresent()) {
syncGroup.name = g.getName().get();
}
syncGroup.addMembers(g.getMembers()
.stream()
.map(recipientResolver::resolveRecipient)
.collect(Collectors.toSet()));
if (!g.isActive()) {
syncGroup.removeMember(account.getSelfRecipientId());
} else {
// Add ourself to the member set as it's marked as active
syncGroup.addMembers(List.of(account.getSelfRecipientId()));
}
syncGroup.blocked = g.isBlocked();
if (g.getColor().isPresent()) {
syncGroup.color = g.getColor().get();
}
if (g.getAvatar().isPresent()) {
groupHelper.downloadGroupAvatar(syncGroup.getGroupId(), g.getAvatar().get());
}
syncGroup.archived = g.isArchived();
account.getGroupStore().updateGroup(syncGroup);
}
}
}
public void handleSyncDeviceContacts(final InputStream input) {
final var s = new DeviceContactsInputStream(input);
DeviceContact c;

View file

@ -152,7 +152,7 @@ public class SignalAccount implements Closeable {
account.initStores(dataPath, identityKey, registrationId, trustNewIdentity);
account.groupStore = new GroupStore(getGroupCachePath(dataPath, username),
account.recipientStore::resolveRecipient,
account.recipientStore,
account::saveGroupStore);
account.stickerStore = new StickerStore(account::saveStickerStore);
@ -174,9 +174,9 @@ public class SignalAccount implements Closeable {
preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, username));
signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, username));
sessionStore = new SessionStore(getSessionsPath(dataPath, username), recipientStore::resolveRecipient);
sessionStore = new SessionStore(getSessionsPath(dataPath, username), recipientStore);
identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, username),
recipientStore::resolveRecipient,
recipientStore,
identityKey,
registrationId,
trustNewIdentity);
@ -254,7 +254,7 @@ public class SignalAccount implements Closeable {
account.initStores(dataPath, identityKey, registrationId, trustNewIdentity);
account.groupStore = new GroupStore(getGroupCachePath(dataPath, username),
account.recipientStore::resolveRecipient,
account.recipientStore,
account::saveGroupStore);
account.stickerStore = new StickerStore(account::saveStickerStore);
@ -453,12 +453,10 @@ public class SignalAccount implements Closeable {
groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class);
groupStore = GroupStore.fromStorage(groupStoreStorage,
getGroupCachePath(dataPath, username),
recipientStore::resolveRecipient,
recipientStore,
this::saveGroupStore);
} else {
groupStore = new GroupStore(getGroupCachePath(dataPath, username),
recipientStore::resolveRecipient,
this::saveGroupStore);
groupStore = new GroupStore(getGroupCachePath(dataPath, username), recipientStore, this::saveGroupStore);
}
if (rootNode.hasNonNull("stickerStore")) {
@ -572,7 +570,7 @@ public class SignalAccount implements Closeable {
var profileStoreNode = rootNode.get("profileStore");
final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class);
for (var profileEntry : legacyProfileStore.getProfileEntries()) {
var recipientId = recipientStore.resolveRecipient(profileEntry.getServiceAddress());
var recipientId = recipientStore.resolveRecipient(profileEntry.getAddress());
recipientStore.storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential());
recipientStore.storeProfileKey(recipientId, profileEntry.getProfileKey());
final var profile = profileEntry.getProfile();

View file

@ -9,7 +9,11 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.InvalidObjectException;
import java.util.Optional;
public class Utils {
@ -37,4 +41,12 @@ public class Utils {
return node;
}
public static RecipientAddress getRecipientAddressFromIdentifier(final String identifier) {
if (UuidUtil.isUuid(identifier)) {
return new RecipientAddress(UuidUtil.parseOrThrow(identifier));
} else {
return new RecipientAddress(Optional.empty(), Optional.of(identifier));
}
}
}

View file

@ -3,7 +3,7 @@ package org.asamk.signal.manager.storage.contacts;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import java.util.UUID;
@ -42,7 +42,7 @@ public class LegacyContactInfo {
}
@JsonIgnore
public SignalServiceAddress getAddress() {
return new SignalServiceAddress(uuid, number);
public RecipientAddress getAddress() {
return new RecipientAddress(uuid, number);
}
}

View file

@ -9,7 +9,6 @@ import org.signal.storageservice.protos.groups.Member;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.EnabledState;
import org.signal.zkgroup.groups.GroupMasterKey;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.Set;
@ -89,7 +88,7 @@ public class GroupInfoV2 extends GroupInfo {
}
return group.getMembersList()
.stream()
.map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null))
.map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray()))
.map(recipientResolver::resolveRecipient)
.collect(Collectors.toSet());
}
@ -101,7 +100,7 @@ public class GroupInfoV2 extends GroupInfo {
}
return group.getPendingMembersList()
.stream()
.map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null))
.map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray()))
.map(recipientResolver::resolveRecipient)
.collect(Collectors.toSet());
}
@ -113,7 +112,7 @@ public class GroupInfoV2 extends GroupInfo {
}
return group.getRequestingMembersList()
.stream()
.map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null))
.map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray()))
.map(recipientResolver::resolveRecipient)
.collect(Collectors.toSet());
}
@ -126,7 +125,7 @@ public class GroupInfoV2 extends GroupInfo {
return group.getMembersList()
.stream()
.filter(m -> m.getRole() == Member.Role.ADMINISTRATOR)
.map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null))
.map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray()))
.map(recipientResolver::resolveRecipient)
.collect(Collectors.toSet());
}

View file

@ -14,6 +14,7 @@ import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.groups.GroupIdV1;
import org.asamk.signal.manager.groups.GroupIdV2;
import org.asamk.signal.manager.groups.GroupUtils;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.asamk.signal.manager.util.IOUtils;
@ -22,7 +23,6 @@ import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.groups.GroupMasterKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.util.Hex;
@ -78,7 +78,7 @@ public class GroupStore {
final var g1 = (Storage.GroupV1) g;
final var members = g1.members.stream().map(m -> {
if (m.recipientId == null) {
return recipientResolver.resolveRecipient(new SignalServiceAddress(UuidUtil.parseOrNull(m.uuid),
return recipientResolver.resolveRecipient(new RecipientAddress(UuidUtil.parseOrNull(m.uuid),
m.number));
}
@ -343,17 +343,17 @@ public class GroupStore {
}
}
private static final class JsonSignalServiceAddress {
private static final class JsonRecipientAddress {
public String uuid;
public String number;
// For deserialization
public JsonSignalServiceAddress() {
public JsonRecipientAddress() {
}
JsonSignalServiceAddress(final String uuid, final String number) {
JsonRecipientAddress(final String uuid, final String number) {
this.uuid = uuid;
this.number = number;
}
@ -370,7 +370,7 @@ public class GroupStore {
if (address.recipientId != null) {
jgen.writeNumber(address.recipientId);
} else if (address.uuid != null) {
jgen.writeObject(new JsonSignalServiceAddress(address.uuid, address.number));
jgen.writeObject(new JsonRecipientAddress(address.uuid, address.number));
} else {
jgen.writeString(address.number);
}
@ -393,7 +393,7 @@ public class GroupStore {
} else if (n.isNumber()) {
addresses.add(new Member(n.numberValue().longValue(), null, null));
} else {
var address = jsonParser.getCodec().treeToValue(n, JsonSignalServiceAddress.class);
var address = jsonParser.getCodec().treeToValue(n, JsonRecipientAddress.class);
addresses.add(new Member(null, address.uuid, address.number));
}
}

View file

@ -6,7 +6,6 @@ import org.asamk.signal.manager.TrustLevel;
import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.asamk.signal.manager.util.IOUtils;
import org.asamk.signal.manager.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKey;
@ -177,7 +176,7 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
* @param identifier can be either a serialized uuid or a e164 phone number
*/
private RecipientId resolveRecipient(String identifier) {
return resolver.resolveRecipient(Utils.getSignalServiceAddressFromIdentifier(identifier));
return resolver.resolveRecipient(identifier);
}
private File getIdentityFile(final RecipientId recipientId) {

View file

@ -8,10 +8,10 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException;
@ -45,7 +45,7 @@ public class LegacyProfileStore {
for (var entry : node) {
var name = entry.hasNonNull("name") ? entry.get("name").asText() : null;
var uuid = entry.hasNonNull("uuid") ? UuidUtil.parseOrNull(entry.get("uuid").asText()) : null;
final var serviceAddress = new SignalServiceAddress(uuid, name);
final var address = new RecipientAddress(uuid, name);
ProfileKey profileKey = null;
try {
profileKey = new ProfileKey(Base64.getDecoder().decode(entry.get("profileKey").asText()));
@ -61,7 +61,7 @@ public class LegacyProfileStore {
}
var lastUpdateTimestamp = entry.get("lastUpdateTimestamp").asLong();
var profile = jsonProcessor.treeToValue(entry.get("profile"), SignalProfile.class);
profileEntries.add(new LegacySignalProfileEntry(serviceAddress,
profileEntries.add(new LegacySignalProfileEntry(address,
profileKey,
lastUpdateTimestamp,
profile,

View file

@ -1,12 +1,12 @@
package org.asamk.signal.manager.storage.profiles;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
public class LegacySignalProfileEntry {
private final SignalServiceAddress serviceAddress;
private final RecipientAddress address;
private final ProfileKey profileKey;
@ -17,21 +17,21 @@ public class LegacySignalProfileEntry {
private final ProfileKeyCredential profileKeyCredential;
public LegacySignalProfileEntry(
final SignalServiceAddress serviceAddress,
final RecipientAddress address,
final ProfileKey profileKey,
final long lastUpdateTimestamp,
final SignalProfile profile,
final ProfileKeyCredential profileKeyCredential
) {
this.serviceAddress = serviceAddress;
this.address = address;
this.profileKey = profileKey;
this.lastUpdateTimestamp = lastUpdateTimestamp;
this.profile = profile;
this.profileKeyCredential = profileKeyCredential;
}
public SignalServiceAddress getServiceAddress() {
return serviceAddress;
public RecipientAddress getAddress() {
return address;
}
public ProfileKey getProfileKey() {

View file

@ -1,30 +1,30 @@
package org.asamk.signal.manager.storage.protocol;
import org.asamk.signal.manager.TrustLevel;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.Date;
public class LegacyIdentityInfo {
SignalServiceAddress address;
RecipientAddress address;
IdentityKey identityKey;
TrustLevel trustLevel;
Date added;
LegacyIdentityInfo(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel, Date added) {
LegacyIdentityInfo(RecipientAddress address, IdentityKey identityKey, TrustLevel trustLevel, Date added) {
this.address = address;
this.identityKey = identityKey;
this.trustLevel = trustLevel;
this.added = added;
}
public SignalServiceAddress getAddress() {
public RecipientAddress getAddress() {
return address;
}
public void setAddress(final SignalServiceAddress address) {
public void setAddress(final RecipientAddress address) {
this.address = address;
}

View file

@ -6,13 +6,13 @@ import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import org.asamk.signal.manager.TrustLevel;
import org.asamk.signal.manager.util.Utils;
import org.asamk.signal.manager.storage.Utils;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException;
@ -55,11 +55,11 @@ public class LegacyJsonIdentityKeyStore {
return localRegistrationId;
}
private LegacyIdentityInfo getIdentity(SignalServiceAddress serviceAddress) {
private LegacyIdentityInfo getIdentity(RecipientAddress address) {
long maxDate = 0;
LegacyIdentityInfo maxIdentity = null;
for (var id : this.identities) {
if (!id.address.matches(serviceAddress)) {
if (!id.getAddress().matches(address)) {
continue;
}
@ -98,16 +98,16 @@ public class LegacyJsonIdentityKeyStore {
var uuid = trustedKey.hasNonNull("uuid")
? UuidUtil.parseOrNull(trustedKey.get("uuid").asText())
: null;
final var serviceAddress = uuid == null
? Utils.getSignalServiceAddressFromIdentifier(trustedKeyName)
: new SignalServiceAddress(uuid, trustedKeyName);
final var address = uuid == null
? Utils.getRecipientAddressFromIdentifier(trustedKeyName)
: new RecipientAddress(uuid, trustedKeyName);
try {
var id = new IdentityKey(Base64.getDecoder().decode(trustedKey.get("identityKey").asText()), 0);
var trustLevel = trustedKey.hasNonNull("trustLevel") ? TrustLevel.fromInt(trustedKey.get(
"trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED;
var added = trustedKey.hasNonNull("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp")
.asLong()) : new Date();
identities.add(new LegacyIdentityInfo(serviceAddress, id, trustLevel, added));
identities.add(new LegacyIdentityInfo(address, id, trustLevel, added));
} catch (InvalidKeyException e) {
logger.warn("Error while decoding key for {}: {}", trustedKeyName, e.getMessage());
}

View file

@ -5,8 +5,8 @@ import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import org.asamk.signal.manager.util.Utils;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.asamk.signal.manager.storage.Utils;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException;
@ -45,12 +45,12 @@ public class LegacyJsonSessionStore {
}
var uuid = session.hasNonNull("uuid") ? UuidUtil.parseOrNull(session.get("uuid").asText()) : null;
final var serviceAddress = uuid == null
? Utils.getSignalServiceAddressFromIdentifier(sessionName)
: new SignalServiceAddress(uuid, sessionName);
final var address = uuid == null
? Utils.getRecipientAddressFromIdentifier(sessionName)
: new RecipientAddress(uuid, sessionName);
final var deviceId = session.get("deviceId").asInt();
final var record = Base64.getDecoder().decode(session.get("record").asText());
var sessionInfo = new LegacySessionInfo(serviceAddress, deviceId, record);
var sessionInfo = new LegacySessionInfo(address, deviceId, record);
sessions.add(sessionInfo);
}
}

View file

@ -1,16 +1,16 @@
package org.asamk.signal.manager.storage.protocol;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
public class LegacySessionInfo {
public SignalServiceAddress address;
public RecipientAddress address;
public int deviceId;
public byte[] sessionRecord;
LegacySessionInfo(final SignalServiceAddress address, final int deviceId, final byte[] sessionRecord) {
LegacySessionInfo(final RecipientAddress address, final int deviceId, final byte[] sessionRecord) {
this.address = address;
this.deviceId = deviceId;
this.sessionRecord = sessionRecord;

View file

@ -129,6 +129,11 @@ public class SignalProtocolStore implements SignalServiceDataStore {
sessionStore.archiveSession(address);
}
@Override
public Set<SignalProtocolAddress> getAllAddressesWithActiveSessions(final List<String> addressNames) {
return sessionStore.getAllAddressesWithActiveSessions(addressNames);
}
@Override
public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
return signedPreKeyStore.loadSignedPreKey(signedPreKeyId);
@ -189,4 +194,11 @@ public class SignalProtocolStore implements SignalServiceDataStore {
public boolean isMultiDevice() {
return isMultiDevice.get();
}
@Override
public Transaction beginTransaction() {
return () -> {
// No-op transaction should be safe, as it's only a performance improvement
};
}
}

View file

@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException;
@ -18,28 +17,27 @@ public class LegacyRecipientStore {
@JsonProperty("recipientStore")
@JsonDeserialize(using = RecipientStoreDeserializer.class)
private final List<SignalServiceAddress> addresses = new ArrayList<>();
private final List<RecipientAddress> addresses = new ArrayList<>();
public List<SignalServiceAddress> getAddresses() {
public List<RecipientAddress> getAddresses() {
return addresses;
}
public static class RecipientStoreDeserializer extends JsonDeserializer<List<SignalServiceAddress>> {
public static class RecipientStoreDeserializer extends JsonDeserializer<List<RecipientAddress>> {
@Override
public List<SignalServiceAddress> deserialize(
public List<RecipientAddress> deserialize(
JsonParser jsonParser, DeserializationContext deserializationContext
) throws IOException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
var addresses = new ArrayList<SignalServiceAddress>();
var addresses = new ArrayList<RecipientAddress>();
if (node.isArray()) {
for (var recipient : node) {
var recipientName = recipient.get("name").asText();
var uuid = UuidUtil.parseOrThrow(recipient.get("uuid").asText());
final var serviceAddress = new SignalServiceAddress(uuid, recipientName);
addresses.add(serviceAddress);
addresses.add(new RecipientAddress(uuid, recipientName));
}
}

View file

@ -2,13 +2,12 @@ package org.asamk.signal.manager.storage.recipients;
import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
public class Recipient {
private final RecipientId recipientId;
private final SignalServiceAddress address;
private final RecipientAddress address;
private final Contact contact;
@ -20,7 +19,7 @@ public class Recipient {
public Recipient(
final RecipientId recipientId,
final SignalServiceAddress address,
final RecipientAddress address,
final Contact contact,
final ProfileKey profileKey,
final ProfileKeyCredential profileKeyCredential,
@ -62,7 +61,7 @@ public class Recipient {
return recipientId;
}
public SignalServiceAddress getAddress() {
public RecipientAddress getAddress() {
return address;
}
@ -85,7 +84,7 @@ public class Recipient {
public static final class Builder {
private RecipientId recipientId;
private SignalServiceAddress address;
private RecipientAddress address;
private Contact contact;
private ProfileKey profileKey;
private ProfileKeyCredential profileKeyCredential;
@ -99,7 +98,7 @@ public class Recipient {
return this;
}
public Builder withAddress(final SignalServiceAddress val) {
public Builder withAddress(final RecipientAddress val) {
address = val;
return this;
}

View file

@ -0,0 +1,89 @@
package org.asamk.signal.manager.storage.recipients;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.Optional;
import java.util.UUID;
public class RecipientAddress {
private final Optional<UUID> uuid;
private final Optional<String> e164;
/**
* Construct a RecipientAddress.
*
* @param uuid The UUID of the user, if available.
* @param e164 The phone number of the user, if available.
*/
public RecipientAddress(Optional<UUID> uuid, Optional<String> e164) {
if (!uuid.isPresent() && !e164.isPresent()) {
throw new AssertionError("Must have either a UUID or E164 number!");
}
this.uuid = uuid;
this.e164 = e164;
}
public RecipientAddress(UUID uuid, String e164) {
this(Optional.ofNullable(uuid), Optional.ofNullable(e164));
}
public RecipientAddress(SignalServiceAddress address) {
this.uuid = Optional.of(address.getUuid());
this.e164 = Optional.ofNullable(address.getNumber().orNull());
}
public RecipientAddress(UUID uuid) {
this.uuid = Optional.of(uuid);
this.e164 = Optional.empty();
}
public Optional<String> getNumber() {
return e164;
}
public Optional<UUID> getUuid() {
return uuid;
}
public String getIdentifier() {
if (uuid.isPresent()) {
return uuid.get().toString();
} else if (e164.isPresent()) {
return e164.get();
} else {
throw new AssertionError("Given the checks in the constructor, this should not be possible.");
}
}
public boolean matches(RecipientAddress other) {
return (uuid.isPresent() && other.uuid.isPresent() && uuid.get().equals(other.uuid.get())) || (
e164.isPresent() && other.e164.isPresent() && e164.get().equals(other.e164.get())
);
}
public SignalServiceAddress toSignalServiceAddress() {
return new SignalServiceAddress(uuid.orElse(UuidUtil.UNKNOWN_UUID),
org.whispersystems.libsignal.util.guava.Optional.fromNullable(e164.orElse(null)));
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final RecipientAddress that = (RecipientAddress) o;
if (!uuid.equals(that.uuid)) return false;
return e164.equals(that.e164);
}
@Override
public int hashCode() {
int result = uuid.hashCode();
result = 31 * result + e164.hashCode();
return result;
}
}

View file

@ -2,7 +2,15 @@ package org.asamk.signal.manager.storage.recipients;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.UUID;
public interface RecipientResolver {
RecipientId resolveRecipient(String identifier);
RecipientId resolveRecipient(RecipientAddress address);
RecipientId resolveRecipient(SignalServiceAddress address);
RecipientId resolveRecipient(UUID uuid);
}

View file

@ -12,6 +12,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.ByteArrayInputStream;
@ -30,9 +31,10 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class RecipientStore implements ContactsStore, ProfileStore {
public class RecipientStore implements RecipientResolver, ContactsStore, ProfileStore {
private final static Logger logger = LoggerFactory.getLogger(RecipientStore.class);
@ -51,9 +53,8 @@ public class RecipientStore implements ContactsStore, ProfileStore {
final var storage = objectMapper.readValue(inputStream, Storage.class);
final var recipients = storage.recipients.stream().map(r -> {
final var recipientId = new RecipientId(r.id);
final var address = new SignalServiceAddress(org.whispersystems.libsignal.util.guava.Optional.fromNullable(
r.uuid).transform(UuidUtil::parseOrThrow),
org.whispersystems.libsignal.util.guava.Optional.fromNullable(r.number));
final var address = new RecipientAddress(Optional.ofNullable(r.uuid).map(UuidUtil::parseOrThrow),
Optional.ofNullable(r.number));
Contact contact = null;
if (r.contact != null) {
@ -119,7 +120,7 @@ public class RecipientStore implements ContactsStore, ProfileStore {
this.lastId = lastId;
}
public SignalServiceAddress resolveServiceAddress(RecipientId recipientId) {
public RecipientAddress resolveRecipientAddress(RecipientId recipientId) {
synchronized (recipients) {
return getRecipient(recipientId).getAddress();
}
@ -134,24 +135,52 @@ public class RecipientStore implements ContactsStore, ProfileStore {
}
}
@Deprecated
public SignalServiceAddress resolveServiceAddress(SignalServiceAddress address) {
return resolveServiceAddress(resolveRecipient(address, false));
}
@Override
public RecipientId resolveRecipient(UUID uuid) {
return resolveRecipient(new SignalServiceAddress(uuid, null), false);
return resolveRecipient(new RecipientAddress(uuid), false);
}
public RecipientId resolveRecipient(String number) {
return resolveRecipient(new SignalServiceAddress(null, number), false);
@Override
public RecipientId resolveRecipient(final String identifier) {
return resolveRecipient(Utils.getRecipientAddressFromIdentifier(identifier), false);
}
public RecipientId resolveRecipientTrusted(SignalServiceAddress address) {
public RecipientId resolveRecipient(
final String number, Supplier<UUID> uuidSupplier
) throws UnregisteredUserException {
final Optional<Recipient> byNumber;
synchronized (recipients) {
byNumber = findByNumberLocked(number);
}
if (byNumber.isEmpty() || byNumber.get().getAddress().getUuid().isEmpty()) {
final var uuid = uuidSupplier.get();
if (uuid == null) {
throw new UnregisteredUserException(number, null);
}
return resolveRecipient(new RecipientAddress(uuid, number), false);
}
return byNumber.get().getRecipientId();
}
public RecipientId resolveRecipient(RecipientAddress address) {
return resolveRecipient(address, false);
}
@Override
public RecipientId resolveRecipient(final SignalServiceAddress address) {
return resolveRecipient(new RecipientAddress(address), false);
}
public RecipientId resolveRecipientTrusted(RecipientAddress address) {
return resolveRecipient(address, true);
}
public List<RecipientId> resolveRecipientsTrusted(List<SignalServiceAddress> addresses) {
public RecipientId resolveRecipientTrusted(SignalServiceAddress address) {
return resolveRecipient(new RecipientAddress(address), true);
}
public List<RecipientId> resolveRecipientsTrusted(List<RecipientAddress> addresses) {
final List<RecipientId> recipientIds;
final List<Pair<RecipientId, RecipientId>> toBeMerged = new ArrayList<>();
synchronized (recipients) {
@ -169,10 +198,6 @@ public class RecipientStore implements ContactsStore, ProfileStore {
return recipientIds;
}
public RecipientId resolveRecipient(SignalServiceAddress address) {
return resolveRecipient(address, false);
}
@Override
public void storeContact(final RecipientId recipientId, final Contact contact) {
synchronized (recipients) {
@ -262,7 +287,7 @@ public class RecipientStore implements ContactsStore, ProfileStore {
* @param isHighTrust true, if the number/uuid connection was obtained from a trusted source.
* Has no effect, if the address contains only a number or a uuid.
*/
private RecipientId resolveRecipient(SignalServiceAddress address, boolean isHighTrust) {
private RecipientId resolveRecipient(RecipientAddress address, boolean isHighTrust) {
final Pair<RecipientId, Optional<RecipientId>> pair;
synchronized (recipients) {
pair = resolveRecipientLocked(address, isHighTrust);
@ -278,30 +303,26 @@ public class RecipientStore implements ContactsStore, ProfileStore {
}
private Pair<RecipientId, Optional<RecipientId>> resolveRecipientLocked(
SignalServiceAddress address, boolean isHighTrust
RecipientAddress address, boolean isHighTrust
) {
final var byNumber = !address.getNumber().isPresent()
final var byNumber = address.getNumber().isEmpty()
? Optional.<Recipient>empty()
: findByNameLocked(address.getNumber().get());
final var byUuid = !address.getUuid().isPresent()
: findByNumberLocked(address.getNumber().get());
final var byUuid = address.getUuid().isEmpty() || address.getUuid().get().equals(UuidUtil.UNKNOWN_UUID)
? Optional.<Recipient>empty()
: findByUuidLocked(address.getUuid().get());
if (byNumber.isEmpty() && byUuid.isEmpty()) {
logger.debug("Got new recipient, both uuid and number are unknown");
if (isHighTrust || !address.getUuid().isPresent() || !address.getNumber().isPresent()) {
if (isHighTrust || address.getUuid().isEmpty() || address.getNumber().isEmpty()) {
return new Pair<>(addNewRecipientLocked(address), Optional.empty());
}
return new Pair<>(addNewRecipientLocked(new SignalServiceAddress(address.getUuid().get(), null)),
Optional.empty());
return new Pair<>(addNewRecipientLocked(new RecipientAddress(address.getUuid().get())), Optional.empty());
}
if (!isHighTrust
|| !address.getUuid().isPresent()
|| !address.getNumber().isPresent()
|| byNumber.equals(byUuid)) {
if (!isHighTrust || address.getUuid().isEmpty() || address.getNumber().isEmpty() || byNumber.equals(byUuid)) {
return new Pair<>(byUuid.or(() -> byNumber).map(Recipient::getRecipientId).get(), Optional.empty());
}
@ -317,7 +338,7 @@ public class RecipientStore implements ContactsStore, ProfileStore {
"Got recipient existing with number, but different uuid, so stripping its number and adding new recipient");
updateRecipientAddressLocked(byNumber.get().getRecipientId(),
new SignalServiceAddress(byNumber.get().getAddress().getUuid().get(), null));
new RecipientAddress(byNumber.get().getAddress().getUuid().get()));
return new Pair<>(addNewRecipientLocked(address), Optional.empty());
}
@ -331,7 +352,7 @@ public class RecipientStore implements ContactsStore, ProfileStore {
"Got separate recipients for high trust number and uuid, recipient for number has different uuid, so stripping its number");
updateRecipientAddressLocked(byNumber.get().getRecipientId(),
new SignalServiceAddress(byNumber.get().getAddress().getUuid().get(), null));
new RecipientAddress(byNumber.get().getAddress().getUuid().get()));
updateRecipientAddressLocked(byUuid.get().getRecipientId(), address);
return new Pair<>(byUuid.get().getRecipientId(), Optional.empty());
}
@ -342,14 +363,14 @@ public class RecipientStore implements ContactsStore, ProfileStore {
return new Pair<>(byUuid.get().getRecipientId(), byNumber.map(Recipient::getRecipientId));
}
private RecipientId addNewRecipientLocked(final SignalServiceAddress serviceAddress) {
private RecipientId addNewRecipientLocked(final RecipientAddress address) {
final var nextRecipientId = nextIdLocked();
storeRecipientLocked(nextRecipientId, new Recipient(nextRecipientId, serviceAddress, null, null, null, null));
storeRecipientLocked(nextRecipientId, new Recipient(nextRecipientId, address, null, null, null, null));
return nextRecipientId;
}
private void updateRecipientAddressLocked(
final RecipientId recipientId, final SignalServiceAddress address
final RecipientId recipientId, final RecipientAddress address
) {
final var recipient = recipients.get(recipientId);
storeRecipientLocked(recipientId, Recipient.newBuilder(recipient).withAddress(address).build());
@ -380,7 +401,7 @@ public class RecipientStore implements ContactsStore, ProfileStore {
saveLocked();
}
private Optional<Recipient> findByNameLocked(final String number) {
private Optional<Recipient> findByNumberLocked(final String number) {
return recipients.entrySet()
.stream()
.filter(entry -> entry.getValue().getAddress().getNumber().isPresent() && number.equals(entry.getValue()
@ -431,8 +452,8 @@ public class RecipientStore implements ContactsStore, ProfileStore {
.map(Enum::name)
.collect(Collectors.toSet()));
return new Storage.Recipient(pair.getKey().getId(),
recipient.getAddress().getNumber().orNull(),
recipient.getAddress().getUuid().transform(UUID::toString).orNull(),
recipient.getAddress().getNumber().orElse(null),
recipient.getAddress().getUuid().map(UUID::toString).orElse(null),
recipient.getProfileKey() == null
? null
: base64.encodeToString(recipient.getProfileKey().serialize()),

View file

@ -3,7 +3,6 @@ package org.asamk.signal.manager.storage.sessions;
import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.asamk.signal.manager.util.IOUtils;
import org.asamk.signal.manager.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.NoSessionException;
@ -23,6 +22,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -150,6 +150,19 @@ public class SessionStore implements SignalServiceSessionStore {
}
}
@Override
public Set<SignalProtocolAddress> getAllAddressesWithActiveSessions(final List<String> addressNames) {
final var recipientIdToNameMap = addressNames.stream()
.collect(Collectors.toMap(this::resolveRecipient, name -> name));
synchronized (cachedSessions) {
return recipientIdToNameMap.keySet()
.stream()
.flatMap(recipientId -> getKeysLocked(recipientId).stream())
.map(key -> new SignalProtocolAddress(recipientIdToNameMap.get(key.recipientId), key.getDeviceId()))
.collect(Collectors.toSet());
}
}
public void archiveAllSessions() {
synchronized (cachedSessions) {
final var keys = getKeysLocked();
@ -198,7 +211,7 @@ public class SessionStore implements SignalServiceSessionStore {
* @param identifier can be either a serialized uuid or a e164 phone number
*/
private RecipientId resolveRecipient(String identifier) {
return resolver.resolveRecipient(Utils.getSignalServiceAddressFromIdentifier(identifier));
return resolver.resolveRecipient(identifier);
}
private Key getKey(final SignalProtocolAddress address) {

View file

@ -48,11 +48,11 @@ public class Utils {
byte[] ownId;
byte[] theirId;
if (isUuidCapable && ownAddress.getUuid().isPresent() && theirAddress.getUuid().isPresent()) {
if (isUuidCapable) {
// Version 2: UUID user
version = 2;
ownId = UuidUtil.toByteArray(ownAddress.getUuid().get());
theirId = UuidUtil.toByteArray(theirAddress.getUuid().get());
ownId = UuidUtil.toByteArray(ownAddress.getUuid());
theirId = UuidUtil.toByteArray(theirAddress.getUuid());
} else {
// Version 1: E164 user
version = 1;
@ -69,12 +69,4 @@ public class Utils {
theirId,
theirIdentityKey);
}
public static SignalServiceAddress getSignalServiceAddressFromIdentifier(final String identifier) {
if (UuidUtil.isUuid(identifier)) {
return new SignalServiceAddress(UuidUtil.parseOrNull(identifier), null);
} else {
return new SignalServiceAddress(null, identifier);
}
}
}

View file

@ -136,8 +136,8 @@ run_main -u "$NUMBER_2" listGroups -d
run_main -u "$NUMBER_2" --output=json listGroups -d
run_main -u "$NUMBER_1" receive
run_main -u "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2"
run_main -u "$NUMBER_1" block "$GROUP_ID"
run_main -u "$NUMBER_1" unblock "$GROUP_ID"
run_main -u "$NUMBER_1" --verbose block -g "$GROUP_ID"
run_main -u "$NUMBER_1" --verbose unblock -g "$GROUP_ID"
## Identities
run_main -u "$NUMBER_1" listIdentities

View file

@ -45,7 +45,7 @@ public class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler {
e.printStackTrace();
}
} else if (content != null) {
final var sender = !envelope.isUnidentifiedSender() && envelope.hasSource()
final var sender = !envelope.isUnidentifiedSender() && envelope.hasSourceUuid()
? envelope.getSourceAddress()
: content.getSender();
if (content.getReceiptMessage().isPresent()) {

View file

@ -1,12 +1,12 @@
package org.asamk.signal;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.UntrustedIdentityException;
import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.groups.GroupUtils;
import org.asamk.signal.util.DateUtils;
import org.asamk.signal.util.Util;
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
import org.slf4j.helpers.MessageFormatter;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
@ -38,12 +38,9 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
@Override
public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) {
if (envelope.hasSource()) {
if (envelope.hasSourceUuid()) {
var source = envelope.getSourceAddress();
writer.println("Envelope from: {} (device: {})", formatContact(source), envelope.getSourceDevice());
if (source.getRelay().isPresent()) {
writer.println("Relayed by: {}", source.getRelay().get());
}
} else {
writer.println("Envelope from: unknown source");
}
@ -56,8 +53,8 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
writer.println("Got receipt.");
} else if (envelope.isSignalMessage() || envelope.isPreKeySignalMessage() || envelope.isUnidentifiedSender()) {
if (exception != null) {
if (exception instanceof ProtocolUntrustedIdentityException) {
var e = (ProtocolUntrustedIdentityException) exception;
if (exception instanceof UntrustedIdentityException) {
var e = (UntrustedIdentityException) exception;
writer.println(
"The users key is untrusted, either the user has reinstalled Signal or a third party sent this message.");
final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender()));
@ -630,7 +627,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
private void printMention(
PlainTextWriter writer, SignalServiceDataMessage.Mention mention
) {
final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null));
final var address = m.resolveSignalServiceAddress(mention.getUuid());
writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength());
}

View file

@ -74,9 +74,14 @@ public class JoinGroupCommand implements JsonRpcLocalCommand {
} catch (GroupPatchNotAcceptedException e) {
throw new UserErrorException("Failed to join group, maybe already a member");
} catch (IOException e) {
throw new IOErrorException("Failed to send message: " + e.getMessage());
throw new IOErrorException("Failed to send message: "
+ e.getMessage()
+ " ("
+ e.getClass().getSimpleName()
+ ")");
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
} catch (GroupLinkNotActiveException e) {
throw new UserErrorException("Group link is not valid: " + e.getMessage());
}

View file

@ -8,7 +8,6 @@ import org.asamk.signal.OutputWriter;
import org.asamk.signal.PlainTextWriter;
import org.asamk.signal.manager.Manager;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
@ -47,7 +46,7 @@ public class ListContactsCommand implements JsonRpcLocalCommand {
final var address = m.resolveSignalServiceAddress(contactPair.first());
final var contact = contactPair.second();
return new JsonContact(address.getNumber().orNull(),
address.getUuid().transform(UUID::toString).orNull(),
address.getUuid().toString(),
contact.getName(),
contact.isBlocked(),
contact.getMessageExpirationTime());

View file

@ -16,7 +16,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
public class ListGroupsCommand implements JsonRpcLocalCommand {
@ -46,8 +45,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
private static Set<JsonGroupMember> resolveJsonMembers(Manager m, Set<RecipientId> addresses) {
return addresses.stream()
.map(m::resolveSignalServiceAddress)
.map(address -> new JsonGroupMember(address.getNumber().orNull(),
address.getUuid().transform(UUID::toString).orNull()))
.map(address -> new JsonGroupMember(address.getNumber().orNull(), address.getUuid().toString()))
.collect(Collectors.toSet());
}

View file

@ -18,7 +18,6 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
public class ListIdentitiesCommand implements JsonRpcLocalCommand {
@ -72,7 +71,7 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
var safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(address, id.getIdentityKey()));
var scannableSafetyNumber = m.computeSafetyNumberForScanning(address, id.getIdentityKey());
return new JsonIdentity(address.getNumber().orNull(),
address.getUuid().transform(UUID::toString).orNull(),
address.getUuid().toString(),
Hex.toString(id.getFingerprint()),
safetyNumber,
scannableSafetyNumber == null

View file

@ -66,7 +66,11 @@ public class QuitGroupCommand implements JsonRpcLocalCommand {
m.deleteGroup(groupId);
}
} catch (IOException e) {
throw new IOErrorException("Failed to send message: " + e.getMessage());
throw new IOErrorException("Failed to send message: "
+ e.getMessage()
+ " ("
+ e.getClass().getSimpleName()
+ ")");
} catch (GroupNotFoundException e) {
throw new UserErrorException("Failed to send to group: " + e.getMessage());
} catch (LastGroupAdminException e) {

View file

@ -64,7 +64,8 @@ public class RemoteDeleteCommand implements DbusCommand, JsonRpcLocalCommand {
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException(e.getMessage());
} catch (IOException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
}
}
@ -104,7 +105,8 @@ public class RemoteDeleteCommand implements DbusCommand, JsonRpcLocalCommand {
} catch (Signal.Error.GroupNotFound e) {
throw new UserErrorException("Failed to send to group: " + e.getMessage());
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
}
}

View file

@ -85,7 +85,8 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand {
m.sendEndSessionMessage(singleRecipients);
return;
} catch (IOException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
}
}
@ -108,7 +109,8 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand {
outputResult(outputWriter, results.getTimestamp());
ErrorUtils.handleSendMessageResults(results.getResults());
} catch (AttachmentInvalidException | IOException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException(e.getMessage());
}
@ -141,9 +143,11 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand {
signal.sendEndSessionMessage(recipients);
return;
} catch (Signal.Error.UntrustedIdentity e) {
throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage());
throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
}
}
@ -182,7 +186,8 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand {
outputResult(outputWriter, timestamp);
return;
} catch (Signal.Error.UntrustedIdentity e) {
throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage());
throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send note to self message: " + e.getMessage());
}
@ -194,9 +199,11 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand {
} catch (UnknownObject e) {
throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage());
} catch (Signal.Error.UntrustedIdentity e) {
throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage());
throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
}
}

View file

@ -80,7 +80,8 @@ public class SendReactionCommand implements DbusCommand, JsonRpcLocalCommand {
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException(e.getMessage());
} catch (IOException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
}
}
@ -127,7 +128,8 @@ public class SendReactionCommand implements DbusCommand, JsonRpcLocalCommand {
} catch (Signal.Error.GroupNotFound e) {
throw new UserErrorException("Failed to send to group: " + e.getMessage());
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
}
}

View file

@ -7,8 +7,8 @@ import org.asamk.signal.OutputWriter;
import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.UntrustedIdentityException;
import org.asamk.signal.util.CommandUtil;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import java.io.IOException;
@ -51,7 +51,8 @@ public class SendReceiptCommand implements JsonRpcLocalCommand {
throw new UserErrorException("Unknown receipt type: " + type);
}
} catch (IOException | UntrustedIdentityException e) {
throw new UserErrorException("Failed to send message: " + e.getMessage());
throw new UserErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
}
}
}

View file

@ -8,13 +8,13 @@ import org.asamk.signal.OutputWriter;
import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.UntrustedIdentityException;
import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.TypingAction;
import org.asamk.signal.manager.groups.GroupNotFoundException;
import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.util.CommandUtil;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import java.io.IOException;
import java.util.HashSet;
@ -59,7 +59,8 @@ public class SendTypingCommand implements JsonRpcLocalCommand {
try {
m.sendTypingMessage(action, recipientIdentifiers);
} catch (IOException | UntrustedIdentityException e) {
throw new UserErrorException("Failed to send message: " + e.getMessage());
throw new UserErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException("Failed to send to group: " + e.getMessage());
}

View file

@ -174,7 +174,8 @@ public class UpdateGroupCommand implements DbusCommand, JsonRpcLocalCommand {
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException(e.getMessage());
} catch (IOException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
}
}
@ -210,7 +211,8 @@ public class UpdateGroupCommand implements DbusCommand, JsonRpcLocalCommand {
} catch (Signal.Error.AttachmentInvalid e) {
throw new UserErrorException("Failed to add avatar attachment for group\": " + e.getMessage());
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage());
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
}
}

View file

@ -21,6 +21,7 @@ import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.File;
@ -244,6 +245,8 @@ public class DbusSignalImpl implements Signal {
m.setContactName(getSingleRecipientIdentifier(number, m.getUsername()), name);
} catch (NotMasterDeviceException e) {
throw new Error.Failure("This command doesn't work on linked devices.");
} catch (UnregisteredUserException e) {
throw new Error.Failure("Contact is not registered.");
}
}

View file

@ -4,9 +4,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.UUID;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
@ -29,10 +26,10 @@ public class JsonMention {
final int length;
JsonMention(SignalServiceDataMessage.Mention mention, Manager m) {
final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null));
final var address = m.resolveSignalServiceAddress(mention.getUuid());
this.name = getLegacyIdentifier(address);
this.number = address.getNumber().orNull();
this.uuid = address.getUuid().transform(UUID::toString).orNull();
this.uuid = address.getUuid().toString();
this.start = mention.getStart();
this.length = mention.getLength();
}

View file

@ -5,14 +5,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.Signal;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.UntrustedIdentityException;
import org.asamk.signal.manager.api.RecipientIdentifier;
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.util.List;
import java.util.UUID;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
@ -34,10 +33,6 @@ public class JsonMessageEnvelope {
@JsonProperty
final Integer sourceDevice;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final String relay;
@JsonProperty
final long timestamp;
@ -64,34 +59,30 @@ public class JsonMessageEnvelope {
public JsonMessageEnvelope(
SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception, Manager m
) {
if (!envelope.isUnidentifiedSender() && envelope.hasSource()) {
var source = envelope.getSourceAddress();
if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
var source = m.resolveSignalServiceAddress(envelope.getSourceAddress());
this.source = getLegacyIdentifier(source);
this.sourceNumber = source.getNumber().orNull();
this.sourceUuid = source.getUuid().transform(UUID::toString).orNull();
this.sourceUuid = source.getUuid().toString();
this.sourceDevice = envelope.getSourceDevice();
this.relay = source.getRelay().orNull();
} else if (envelope.isUnidentifiedSender() && content != null) {
final var source = content.getSender();
final var source = m.resolveSignalServiceAddress(content.getSender());
this.source = getLegacyIdentifier(source);
this.sourceNumber = source.getNumber().orNull();
this.sourceUuid = source.getUuid().transform(UUID::toString).orNull();
this.sourceUuid = source.getUuid().toString();
this.sourceDevice = content.getSenderDevice();
this.relay = null;
} else if (exception instanceof ProtocolUntrustedIdentityException) {
var e = (ProtocolUntrustedIdentityException) exception;
} else if (exception instanceof UntrustedIdentityException) {
var e = (UntrustedIdentityException) exception;
final var source = m.resolveSignalServiceAddress(e.getSender());
this.source = getLegacyIdentifier(source);
this.sourceNumber = source.getNumber().orNull();
this.sourceUuid = source.getUuid().transform(UUID::toString).orNull();
this.sourceUuid = source.getUuid().toString();
this.sourceDevice = e.getSenderDevice();
this.relay = null;
} else {
this.source = null;
this.sourceNumber = null;
this.sourceUuid = null;
this.sourceDevice = null;
this.relay = null;
}
String name;
try {
@ -129,7 +120,6 @@ public class JsonMessageEnvelope {
sourceUuid = null;
sourceName = null;
sourceDevice = null;
relay = null;
timestamp = messageReceived.getTimestamp();
receiptMessage = null;
dataMessage = new JsonDataMessage(messageReceived);
@ -144,7 +134,6 @@ public class JsonMessageEnvelope {
sourceUuid = null;
sourceName = null;
sourceDevice = null;
relay = null;
timestamp = receiptReceived.getTimestamp();
receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp));
dataMessage = null;
@ -159,7 +148,6 @@ public class JsonMessageEnvelope {
sourceUuid = null;
sourceName = null;
sourceDevice = null;
relay = null;
timestamp = messageReceived.getTimestamp();
receiptMessage = null;
dataMessage = null;

View file

@ -8,7 +8,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
@ -43,7 +42,7 @@ public class JsonQuote {
final var address = m.resolveSignalServiceAddress(quote.getAuthor());
this.author = getLegacyIdentifier(address);
this.authorNumber = address.getNumber().orNull();
this.authorUuid = address.getUuid().transform(UUID::toString).orNull();
this.authorUuid = address.getUuid().toString();
this.text = quote.getText();
if (quote.getMentions() != null && quote.getMentions().size() > 0) {

View file

@ -5,8 +5,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction;
import java.util.UUID;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class JsonReaction {
@ -35,7 +33,7 @@ public class JsonReaction {
final var address = m.resolveSignalServiceAddress(reaction.getTargetAuthor());
this.targetAuthor = getLegacyIdentifier(address);
this.targetAuthorNumber = address.getNumber().orNull();
this.targetAuthorUuid = address.getUuid().transform(UUID::toString).orNull();
this.targetAuthorUuid = address.getUuid().toString();
this.targetSentTimestamp = reaction.getTargetSentTimestamp();
this.isRemove = reaction.isRemove();
}

View file

@ -6,8 +6,6 @@ import org.asamk.Signal;
import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
import java.util.UUID;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
class JsonSyncDataMessage extends JsonDataMessage {
@ -29,7 +27,7 @@ class JsonSyncDataMessage extends JsonDataMessage {
final var address = transcriptMessage.getDestination().get();
this.destination = getLegacyIdentifier(address);
this.destinationNumber = address.getNumber().orNull();
this.destinationUuid = address.getUuid().transform(UUID::toString).orNull();
this.destinationUuid = address.getUuid().toString();
} else {
this.destination = null;
this.destinationNumber = null;

View file

@ -4,8 +4,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
import java.util.UUID;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
class JsonSyncReadMessage {
@ -27,7 +25,7 @@ class JsonSyncReadMessage {
final var sender = readMessage.getSender();
this.sender = getLegacyIdentifier(sender);
this.senderNumber = sender.getNumber().orNull();
this.senderUuid = sender.getUuid().transform(UUID::toString).orNull();
this.senderUuid = sender.getUuid().toString();
this.timestamp = readMessage.getTimestamp();
}
}

View file

@ -60,7 +60,7 @@ public class Util {
}
public static String getLegacyIdentifier(final SignalServiceAddress address) {
return address.getNumber().or(() -> address.getUuid().get().toString());
return address.getNumber().or(() -> address.getUuid().toString());
}
public static ObjectMapper createJsonObjectMapper() {