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 { 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("com.google.protobuf:protobuf-javalite:3.10.0")
implementation("org.bouncycastle:bcprov-jdk15on:1.69") implementation("org.bouncycastle:bcprov-jdk15on:1.69")
implementation("org.slf4j:slf4j-api:1.7.30") implementation("org.slf4j:slf4j-api:1.7.30")

View file

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

View file

@ -150,11 +150,9 @@ public class GroupV2Helper {
if (!areMembersValid(members)) return null; if (!areMembersValid(members)) return null;
var self = new GroupCandidate(addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId()) var self = new GroupCandidate(getSelfUuid(), Optional.fromNullable(profileKeyCredential));
.getUuid()
.orNull(), Optional.fromNullable(profileKeyCredential));
var candidates = members.stream() 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)))) Optional.fromNullable(profileKeyCredentialProvider.getProfileKeyCredential(member))))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@ -169,18 +167,6 @@ public class GroupV2Helper {
} }
private boolean areMembersValid(final Set<RecipientId> members) { 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() final var noGv2Capability = members.stream()
.map(profileProvider::getProfile) .map(profileProvider::getProfile)
.filter(profile -> profile != null && !profile.getCapabilities().contains(Profile.Capability.gv2)) .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)); change.setModifyAvatar(GroupChange.Actions.ModifyAvatarAction.newBuilder().setAvatar(avatarCdnKey));
} }
final var uuid = addressResolver.resolveSignalServiceAddress(this.selfRecipientIdProvider.getSelfRecipientId()) final var uuid = getSelfUuid();
.getUuid(); change.setSourceUuid(UuidUtil.toByteString(uuid));
if (uuid.isPresent()) {
change.setSourceUuid(UuidUtil.toByteString(uuid.get()));
}
return commitChange(groupInfoV2, change); return commitChange(groupInfoV2, change);
} }
@ -233,13 +216,11 @@ public class GroupV2Helper {
} }
var candidates = newMembers.stream() 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)))) Optional.fromNullable(profileKeyCredentialProvider.getProfileKeyCredential(member))))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
final var uuid = addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId()) final var uuid = getSelfUuid();
.getUuid()
.get();
final var change = groupOperations.createModifyGroupMembershipChange(candidates, uuid); final var change = groupOperations.createModifyGroupMembershipChange(candidates, uuid);
change.setSourceUuid(UuidUtil.toByteString(uuid)); change.setSourceUuid(UuidUtil.toByteString(uuid));
@ -251,9 +232,7 @@ public class GroupV2Helper {
GroupInfoV2 groupInfoV2, Set<RecipientId> membersToMakeAdmin GroupInfoV2 groupInfoV2, Set<RecipientId> membersToMakeAdmin
) throws IOException { ) throws IOException {
var pendingMembersList = groupInfoV2.getGroup().getPendingMembersList(); var pendingMembersList = groupInfoV2.getGroup().getPendingMembersList();
final var selfUuid = addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId()) final var selfUuid = getSelfUuid();
.getUuid()
.get();
var selfPendingMember = DecryptedGroupUtil.findPendingByUuid(pendingMembersList, selfUuid); var selfPendingMember = DecryptedGroupUtil.findPendingByUuid(pendingMembersList, selfUuid);
if (selfPendingMember.isPresent()) { if (selfPendingMember.isPresent()) {
@ -263,7 +242,6 @@ public class GroupV2Helper {
final var adminUuids = membersToMakeAdmin.stream() final var adminUuids = membersToMakeAdmin.stream()
.map(addressResolver::resolveSignalServiceAddress) .map(addressResolver::resolveSignalServiceAddress)
.map(SignalServiceAddress::getUuid) .map(SignalServiceAddress::getUuid)
.map(Optional::get)
.collect(Collectors.toList()); .collect(Collectors.toList());
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
return commitChange(groupInfoV2, groupOperations.createLeaveAndPromoteMembersToAdmin(selfUuid, adminUuids)); return commitChange(groupInfoV2, groupOperations.createLeaveAndPromoteMembersToAdmin(selfUuid, adminUuids));
@ -275,8 +253,6 @@ public class GroupV2Helper {
final var memberUuids = members.stream() final var memberUuids = members.stream()
.map(addressResolver::resolveSignalServiceAddress) .map(addressResolver::resolveSignalServiceAddress)
.map(SignalServiceAddress::getUuid) .map(SignalServiceAddress::getUuid)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
return ejectMembers(groupInfoV2, memberUuids); return ejectMembers(groupInfoV2, memberUuids);
} }
@ -288,8 +264,6 @@ public class GroupV2Helper {
final var memberUuids = members.stream() final var memberUuids = members.stream()
.map(addressResolver::resolveSignalServiceAddress) .map(addressResolver::resolveSignalServiceAddress)
.map(SignalServiceAddress::getUuid) .map(SignalServiceAddress::getUuid)
.filter(Optional::isPresent)
.map(Optional::get)
.map(uuid -> DecryptedGroupUtil.findPendingByUuid(pendingMembersList, uuid)) .map(uuid -> DecryptedGroupUtil.findPendingByUuid(pendingMembersList, uuid))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
@ -360,8 +334,7 @@ public class GroupV2Helper {
: groupOperations.createGroupJoinDirect(profileKeyCredential); : groupOperations.createGroupJoinDirect(profileKeyCredential);
change.setSourceUuid(UuidUtil.toByteString(addressResolver.resolveSignalServiceAddress(selfRecipientId) change.setSourceUuid(UuidUtil.toByteString(addressResolver.resolveSignalServiceAddress(selfRecipientId)
.getUuid() .getUuid()));
.get()));
return commitChange(groupSecretParams, decryptedGroupJoinInfo.getRevision(), change, groupLinkPassword); return commitChange(groupSecretParams, decryptedGroupJoinInfo.getRevision(), change, groupLinkPassword);
} }
@ -378,9 +351,7 @@ public class GroupV2Helper {
final var change = groupOperations.createAcceptInviteChange(profileKeyCredential); final var change = groupOperations.createAcceptInviteChange(profileKeyCredential);
final var uuid = addressResolver.resolveSignalServiceAddress(selfRecipientId).getUuid(); final var uuid = addressResolver.resolveSignalServiceAddress(selfRecipientId).getUuid();
if (uuid.isPresent()) { change.setSourceUuid(UuidUtil.toByteString(uuid));
change.setSourceUuid(UuidUtil.toByteString(uuid.get()));
}
return commitChange(groupInfoV2, change); return commitChange(groupInfoV2, change);
} }
@ -391,7 +362,7 @@ public class GroupV2Helper {
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
final var address = addressResolver.resolveSignalServiceAddress(recipientId); final var address = addressResolver.resolveSignalServiceAddress(recipientId);
final var newRole = admin ? Member.Role.ADMINISTRATOR : Member.Role.DEFAULT; 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); return commitChange(groupInfoV2, change);
} }
@ -473,10 +444,7 @@ public class GroupV2Helper {
final DecryptedGroup decryptedGroupState; final DecryptedGroup decryptedGroupState;
try { try {
decryptedChange = groupOperations.decryptChange(changeActions, decryptedChange = groupOperations.decryptChange(changeActions, getSelfUuid());
addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId())
.getUuid()
.get());
decryptedGroupState = DecryptedGroupUtil.apply(previousGroupState, decryptedChange); decryptedGroupState = DecryptedGroupUtil.apply(previousGroupState, decryptedChange);
} catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) { } catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) {
throw new IOException(e); throw new IOException(e);
@ -543,13 +511,15 @@ public class GroupV2Helper {
final var credentials = groupsV2Api.getCredentials(today); final var credentials = groupsV2Api.getCredentials(today);
// TODO cache credentials until they expire // TODO cache credentials until they expire
var authCredentialResponse = credentials.get(today); var authCredentialResponse = credentials.get(today);
final var uuid = addressResolver.resolveSignalServiceAddress(this.selfRecipientIdProvider.getSelfRecipientId()) final var uuid = getSelfUuid();
.getUuid()
.get();
try { try {
return groupsV2Api.getGroupsV2AuthorizationString(uuid, today, groupSecretParams, authCredentialResponse); return groupsV2Api.getGroupsV2AuthorizationString(uuid, today, groupSecretParams, authCredentialResponse);
} catch (VerificationFailedException e) { } catch (VerificationFailedException e) {
throw new IOException(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.Manager;
import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.SignalDependencies;
import org.asamk.signal.manager.TrustLevel; 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.HandleAction;
import org.asamk.signal.manager.actions.RenewSessionAction; import org.asamk.signal.manager.actions.RenewSessionAction;
import org.asamk.signal.manager.actions.RetrieveProfileAction; 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.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup; 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.messages.multidevice.StickerPackOperationMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
@ -48,6 +50,7 @@ public final class IncomingMessageHandler {
private final SignalAccount account; private final SignalAccount account;
private final SignalDependencies dependencies; private final SignalDependencies dependencies;
private final RecipientResolver recipientResolver; private final RecipientResolver recipientResolver;
private final SignalServiceAddressResolver addressResolver;
private final GroupHelper groupHelper; private final GroupHelper groupHelper;
private final ContactHelper contactHelper; private final ContactHelper contactHelper;
private final AttachmentHelper attachmentHelper; private final AttachmentHelper attachmentHelper;
@ -58,6 +61,7 @@ public final class IncomingMessageHandler {
final SignalAccount account, final SignalAccount account,
final SignalDependencies dependencies, final SignalDependencies dependencies,
final RecipientResolver recipientResolver, final RecipientResolver recipientResolver,
final SignalServiceAddressResolver addressResolver,
final GroupHelper groupHelper, final GroupHelper groupHelper,
final ContactHelper contactHelper, final ContactHelper contactHelper,
final AttachmentHelper attachmentHelper, final AttachmentHelper attachmentHelper,
@ -67,6 +71,7 @@ public final class IncomingMessageHandler {
this.account = account; this.account = account;
this.dependencies = dependencies; this.dependencies = dependencies;
this.recipientResolver = recipientResolver; this.recipientResolver = recipientResolver;
this.addressResolver = addressResolver;
this.groupHelper = groupHelper; this.groupHelper = groupHelper;
this.contactHelper = contactHelper; this.contactHelper = contactHelper;
this.attachmentHelper = attachmentHelper; this.attachmentHelper = attachmentHelper;
@ -80,7 +85,7 @@ public final class IncomingMessageHandler {
final Manager.ReceiveMessageHandler handler final Manager.ReceiveMessageHandler handler
) { ) {
final var actions = new ArrayList<HandleAction>(); final var actions = new ArrayList<HandleAction>();
if (envelope.hasSource()) { if (envelope.hasSourceUuid()) {
// Store uuid if we don't have it already // Store uuid if we don't have it already
// address/uuid in envelope is sent by server // address/uuid in envelope is sent by server
account.getRecipientStore().resolveRecipientTrusted(envelope.getSourceAddress()); account.getRecipientStore().resolveRecipientTrusted(envelope.getSourceAddress());
@ -93,6 +98,8 @@ public final class IncomingMessageHandler {
} catch (ProtocolUntrustedIdentityException e) { } catch (ProtocolUntrustedIdentityException e) {
final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender()); final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender());
actions.add(new RetrieveProfileAction(recipientId)); actions.add(new RetrieveProfileAction(recipientId));
exception = new UntrustedIdentityException(addressResolver.resolveSignalServiceAddress(recipientId),
e.getSenderDevice());
} catch (ProtocolInvalidMessageException e) { } catch (ProtocolInvalidMessageException e) {
final var sender = account.getRecipientStore().resolveRecipient(e.getSender()); final var sender = account.getRecipientStore().resolveRecipient(e.getSender());
logger.debug("Received invalid message, queuing renew session action."); logger.debug("Received invalid message, queuing renew session action.");
@ -102,7 +109,7 @@ public final class IncomingMessageHandler {
exception = e; exception = e;
} }
if (!envelope.hasSource() && content != null) { if (!envelope.hasSourceUuid() && content != null) {
// Store uuid if we don't have it already // Store uuid if we don't have it already
// address/uuid is validated by unidentified sender certificate // address/uuid is validated by unidentified sender certificate
account.getRecipientStore().resolveRecipientTrusted(content.getSender()); account.getRecipientStore().resolveRecipientTrusted(content.getSender());
@ -113,7 +120,7 @@ public final class IncomingMessageHandler {
logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp()); logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
} else if (isNotAllowedToSendToGroup(envelope, content)) { } else if (isNotAllowedToSendToGroup(envelope, content)) {
logger.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}", 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()); envelope.getTimestamp());
} else { } else {
actions.addAll(handleMessage(envelope, content, ignoreAttachments)); actions.addAll(handleMessage(envelope, content, ignoreAttachments));
@ -126,9 +133,12 @@ public final class IncomingMessageHandler {
SignalServiceEnvelope envelope, SignalServiceContent content, boolean ignoreAttachments SignalServiceEnvelope envelope, SignalServiceContent content, boolean ignoreAttachments
) { ) {
var actions = new ArrayList<HandleAction>(); var actions = new ArrayList<HandleAction>();
if (content != null) { if (content == null) {
return actions;
}
final RecipientId sender; final RecipientId sender;
if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
sender = recipientResolver.resolveRecipient(envelope.getSourceAddress()); sender = recipientResolver.resolveRecipient(envelope.getSourceAddress());
} else { } else {
sender = recipientResolver.resolveRecipient(content.getSender()); sender = recipientResolver.resolveRecipient(content.getSender());
@ -147,9 +157,20 @@ public final class IncomingMessageHandler {
account.getSelfRecipientId(), account.getSelfRecipientId(),
ignoreAttachments)); ignoreAttachments));
} }
if (content.getSyncMessage().isPresent()) { if (content.getSyncMessage().isPresent()) {
account.setMultiDevice(true);
var syncMessage = content.getSyncMessage().get(); var syncMessage = content.getSyncMessage().get();
actions.addAll(handleSyncMessage(syncMessage, sender, 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()) { if (syncMessage.getSent().isPresent()) {
var message = syncMessage.getSent().get(); var message = syncMessage.getSent().get();
final var destination = message.getDestination().orNull(); final var destination = message.getDestination().orNull();
@ -173,12 +194,7 @@ public final class IncomingMessageHandler {
// TODO Handle rm.isConfigurationRequest(); rm.isKeysRequest(); // TODO Handle rm.isConfigurationRequest(); rm.isKeysRequest();
} }
if (syncMessage.getGroups().isPresent()) { if (syncMessage.getGroups().isPresent()) {
try { logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring.");
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()) { if (syncMessage.getBlockedList().isPresent()) {
final var blockedListMessage = syncMessage.getBlockedList().get(); final var blockedListMessage = syncMessage.getBlockedList().get();
@ -258,14 +274,12 @@ public final class IncomingMessageHandler {
if (syncMessage.getConfiguration().isPresent()) { if (syncMessage.getConfiguration().isPresent()) {
// TODO // TODO
} }
}
}
return actions; return actions;
} }
private boolean isMessageBlocked(SignalServiceEnvelope envelope, SignalServiceContent content) { private boolean isMessageBlocked(SignalServiceEnvelope envelope, SignalServiceContent content) {
SignalServiceAddress source; SignalServiceAddress source;
if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
source = envelope.getSourceAddress(); source = envelope.getSourceAddress();
} else if (content != null) { } else if (content != null) {
source = content.getSender(); source = content.getSender();
@ -290,7 +304,7 @@ public final class IncomingMessageHandler {
private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope, SignalServiceContent content) { private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope, SignalServiceContent content) {
SignalServiceAddress source; SignalServiceAddress source;
if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
source = envelope.getSourceAddress(); source = envelope.getSourceAddress();
} else if (content != null) { } else if (content != null) {
source = content.getSender(); source = content.getSender();

View file

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

View file

@ -1,6 +1,7 @@
package org.asamk.signal.manager.helper; package org.asamk.signal.manager.helper;
import org.asamk.signal.manager.SignalDependencies; 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.GroupId;
import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupNotFoundException;
import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.util.guava.Optional; 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.ContentHint;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
@ -43,6 +44,20 @@ public class SendHelper {
private final GroupProvider groupProvider; private final GroupProvider groupProvider;
private final RecipientRegistrationRefresher recipientRegistrationRefresher; 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( public SendHelper(
final SignalAccount account, final SignalAccount account,
final SignalDependencies dependencies, final SignalDependencies dependencies,
@ -145,9 +160,12 @@ public class SendHelper {
final SignalServiceReceiptMessage receiptMessage, final RecipientId recipientId final SignalServiceReceiptMessage receiptMessage, final RecipientId recipientId
) throws IOException, UntrustedIdentityException { ) throws IOException, UntrustedIdentityException {
final var messageSender = dependencies.getMessageSender(); final var messageSender = dependencies.getMessageSender();
messageSender.sendReceipt(addressResolver.resolveSignalServiceAddress(recipientId), final var address = addressResolver.resolveSignalServiceAddress(recipientId);
unidentifiedAccessHelper.getAccessFor(recipientId), try {
receiptMessage); 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 { public SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException {
@ -162,7 +180,7 @@ public class SendHelper {
final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId); final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId);
return messageSender.sendNullMessage(newAddress, unidentifiedAccessHelper.getAccessFor(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()); return SendMessageResult.identityFailure(address, e.getIdentityKey());
} }
} }
@ -183,7 +201,7 @@ public class SendHelper {
var messageSender = dependencies.getMessageSender(); var messageSender = dependencies.getMessageSender();
try { try {
return messageSender.sendSyncMessage(message, unidentifiedAccessHelper.getAccessForSync()); return messageSender.sendSyncMessage(message, unidentifiedAccessHelper.getAccessForSync());
} catch (UntrustedIdentityException e) { } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId()); var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId());
return SendMessageResult.identityFailure(address, e.getIdentityKey()); return SendMessageResult.identityFailure(address, e.getIdentityKey());
} }
@ -194,6 +212,7 @@ public class SendHelper {
) throws IOException, UntrustedIdentityException { ) throws IOException, UntrustedIdentityException {
var messageSender = dependencies.getMessageSender(); var messageSender = dependencies.getMessageSender();
final var address = addressResolver.resolveSignalServiceAddress(recipientId); final var address = addressResolver.resolveSignalServiceAddress(recipientId);
try {
try { try {
messageSender.sendTyping(address, unidentifiedAccessHelper.getAccessFor(recipientId), message); messageSender.sendTyping(address, unidentifiedAccessHelper.getAccessFor(recipientId), message);
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
@ -201,6 +220,9 @@ public class SendHelper {
final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId); final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId);
messageSender.sendTyping(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId), message); messageSender.sendTyping(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId), message);
} }
} catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
throw new UntrustedIdentityException(address);
}
} }
public void sendGroupTypingMessage( public void sendGroupTypingMessage(
@ -247,7 +269,7 @@ public class SendHelper {
message, message,
sendResult -> logger.trace("Partial message send result: {}", sendResult.isSuccess()), sendResult -> logger.trace("Partial message send result: {}", sendResult.isSuccess()),
() -> false); () -> false);
} catch (UntrustedIdentityException e) { } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
return List.of(); return List.of();
} }
} }
@ -263,15 +285,17 @@ public class SendHelper {
return messageSender.sendDataMessage(address, return messageSender.sendDataMessage(address,
unidentifiedAccessHelper.getAccessFor(recipientId), unidentifiedAccessHelper.getAccessFor(recipientId),
ContentHint.DEFAULT, ContentHint.DEFAULT,
message); message,
sendEvents);
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId); final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId);
return messageSender.sendDataMessage(addressResolver.resolveSignalServiceAddress(newRecipientId), return messageSender.sendDataMessage(addressResolver.resolveSignalServiceAddress(newRecipientId),
unidentifiedAccessHelper.getAccessFor(newRecipientId), unidentifiedAccessHelper.getAccessFor(newRecipientId),
ContentHint.DEFAULT, ContentHint.DEFAULT,
message); message,
sendEvents);
} }
} catch (UntrustedIdentityException e) { } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
return SendMessageResult.identityFailure(address, e.getIdentityKey()); 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.AvatarStore;
import org.asamk.signal.manager.TrustLevel; 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.SignalAccount;
import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.GroupInfoV1;
import org.asamk.signal.manager.storage.recipients.Contact; 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.AttachmentUtils;
import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.IOUtils;
import org.slf4j.Logger; 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.DeviceContactsInputStream;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup; 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.DeviceGroupsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
@ -36,7 +33,6 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class SyncHelper { public class SyncHelper {
@ -49,7 +45,6 @@ public class SyncHelper {
private final GroupHelper groupHelper; private final GroupHelper groupHelper;
private final AvatarStore avatarStore; private final AvatarStore avatarStore;
private final SignalServiceAddressResolver addressResolver; private final SignalServiceAddressResolver addressResolver;
private final RecipientResolver recipientResolver;
public SyncHelper( public SyncHelper(
final SignalAccount account, final SignalAccount account,
@ -57,8 +52,7 @@ public class SyncHelper {
final SendHelper sendHelper, final SendHelper sendHelper,
final GroupHelper groupHelper, final GroupHelper groupHelper,
final AvatarStore avatarStore, final AvatarStore avatarStore,
final SignalServiceAddressResolver addressResolver, final SignalServiceAddressResolver addressResolver
final RecipientResolver recipientResolver
) { ) {
this.account = account; this.account = account;
this.attachmentHelper = attachmentHelper; this.attachmentHelper = attachmentHelper;
@ -66,7 +60,6 @@ public class SyncHelper {
this.groupHelper = groupHelper; this.groupHelper = groupHelper;
this.avatarStore = avatarStore; this.avatarStore = avatarStore;
this.addressResolver = addressResolver; this.addressResolver = addressResolver;
this.recipientResolver = recipientResolver;
} }
public void requestAllSyncData() throws IOException { public void requestAllSyncData() throws IOException {
@ -222,48 +215,6 @@ public class SyncHelper {
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forVerified(verifiedMessage)); 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) { public void handleSyncDeviceContacts(final InputStream input) {
final var s = new DeviceContactsInputStream(input); final var s = new DeviceContactsInputStream(input);
DeviceContact c; DeviceContact c;

View file

@ -152,7 +152,7 @@ public class SignalAccount implements Closeable {
account.initStores(dataPath, identityKey, registrationId, trustNewIdentity); account.initStores(dataPath, identityKey, registrationId, trustNewIdentity);
account.groupStore = new GroupStore(getGroupCachePath(dataPath, username), account.groupStore = new GroupStore(getGroupCachePath(dataPath, username),
account.recipientStore::resolveRecipient, account.recipientStore,
account::saveGroupStore); account::saveGroupStore);
account.stickerStore = new StickerStore(account::saveStickerStore); account.stickerStore = new StickerStore(account::saveStickerStore);
@ -174,9 +174,9 @@ public class SignalAccount implements Closeable {
preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, username)); preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, username));
signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(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), identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, username),
recipientStore::resolveRecipient, recipientStore,
identityKey, identityKey,
registrationId, registrationId,
trustNewIdentity); trustNewIdentity);
@ -254,7 +254,7 @@ public class SignalAccount implements Closeable {
account.initStores(dataPath, identityKey, registrationId, trustNewIdentity); account.initStores(dataPath, identityKey, registrationId, trustNewIdentity);
account.groupStore = new GroupStore(getGroupCachePath(dataPath, username), account.groupStore = new GroupStore(getGroupCachePath(dataPath, username),
account.recipientStore::resolveRecipient, account.recipientStore,
account::saveGroupStore); account::saveGroupStore);
account.stickerStore = new StickerStore(account::saveStickerStore); account.stickerStore = new StickerStore(account::saveStickerStore);
@ -453,12 +453,10 @@ public class SignalAccount implements Closeable {
groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class); groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class);
groupStore = GroupStore.fromStorage(groupStoreStorage, groupStore = GroupStore.fromStorage(groupStoreStorage,
getGroupCachePath(dataPath, username), getGroupCachePath(dataPath, username),
recipientStore::resolveRecipient, recipientStore,
this::saveGroupStore); this::saveGroupStore);
} else { } else {
groupStore = new GroupStore(getGroupCachePath(dataPath, username), groupStore = new GroupStore(getGroupCachePath(dataPath, username), recipientStore, this::saveGroupStore);
recipientStore::resolveRecipient,
this::saveGroupStore);
} }
if (rootNode.hasNonNull("stickerStore")) { if (rootNode.hasNonNull("stickerStore")) {
@ -572,7 +570,7 @@ public class SignalAccount implements Closeable {
var profileStoreNode = rootNode.get("profileStore"); var profileStoreNode = rootNode.get("profileStore");
final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class); final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class);
for (var profileEntry : legacyProfileStore.getProfileEntries()) { for (var profileEntry : legacyProfileStore.getProfileEntries()) {
var recipientId = recipientStore.resolveRecipient(profileEntry.getServiceAddress()); var recipientId = recipientStore.resolveRecipient(profileEntry.getAddress());
recipientStore.storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); recipientStore.storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential());
recipientStore.storeProfileKey(recipientId, profileEntry.getProfileKey()); recipientStore.storeProfileKey(recipientId, profileEntry.getProfileKey());
final var profile = profileEntry.getProfile(); 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.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; 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.io.InvalidObjectException;
import java.util.Optional;
public class Utils { public class Utils {
@ -37,4 +41,12 @@ public class Utils {
return node; 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.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; 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; import java.util.UUID;
@ -42,7 +42,7 @@ public class LegacyContactInfo {
} }
@JsonIgnore @JsonIgnore
public SignalServiceAddress getAddress() { public RecipientAddress getAddress() {
return new SignalServiceAddress(uuid, number); 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.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.EnabledState; import org.signal.storageservice.protos.groups.local.EnabledState;
import org.signal.zkgroup.groups.GroupMasterKey; import org.signal.zkgroup.groups.GroupMasterKey;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.Set; import java.util.Set;
@ -89,7 +88,7 @@ public class GroupInfoV2 extends GroupInfo {
} }
return group.getMembersList() return group.getMembersList()
.stream() .stream()
.map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null)) .map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray()))
.map(recipientResolver::resolveRecipient) .map(recipientResolver::resolveRecipient)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
@ -101,7 +100,7 @@ public class GroupInfoV2 extends GroupInfo {
} }
return group.getPendingMembersList() return group.getPendingMembersList()
.stream() .stream()
.map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null)) .map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray()))
.map(recipientResolver::resolveRecipient) .map(recipientResolver::resolveRecipient)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
@ -113,7 +112,7 @@ public class GroupInfoV2 extends GroupInfo {
} }
return group.getRequestingMembersList() return group.getRequestingMembersList()
.stream() .stream()
.map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null)) .map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray()))
.map(recipientResolver::resolveRecipient) .map(recipientResolver::resolveRecipient)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
@ -126,7 +125,7 @@ public class GroupInfoV2 extends GroupInfo {
return group.getMembersList() return group.getMembersList()
.stream() .stream()
.filter(m -> m.getRole() == Member.Role.ADMINISTRATOR) .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) .map(recipientResolver::resolveRecipient)
.collect(Collectors.toSet()); .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.GroupIdV1;
import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupIdV2;
import org.asamk.signal.manager.groups.GroupUtils; 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.RecipientId;
import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.IOUtils;
@ -22,7 +23,6 @@ import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.groups.GroupMasterKey; import org.signal.zkgroup.groups.GroupMasterKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.util.Hex; import org.whispersystems.signalservice.internal.util.Hex;
@ -78,7 +78,7 @@ public class GroupStore {
final var g1 = (Storage.GroupV1) g; final var g1 = (Storage.GroupV1) g;
final var members = g1.members.stream().map(m -> { final var members = g1.members.stream().map(m -> {
if (m.recipientId == null) { if (m.recipientId == null) {
return recipientResolver.resolveRecipient(new SignalServiceAddress(UuidUtil.parseOrNull(m.uuid), return recipientResolver.resolveRecipient(new RecipientAddress(UuidUtil.parseOrNull(m.uuid),
m.number)); m.number));
} }
@ -343,17 +343,17 @@ public class GroupStore {
} }
} }
private static final class JsonSignalServiceAddress { private static final class JsonRecipientAddress {
public String uuid; public String uuid;
public String number; public String number;
// For deserialization // For deserialization
public JsonSignalServiceAddress() { public JsonRecipientAddress() {
} }
JsonSignalServiceAddress(final String uuid, final String number) { JsonRecipientAddress(final String uuid, final String number) {
this.uuid = uuid; this.uuid = uuid;
this.number = number; this.number = number;
} }
@ -370,7 +370,7 @@ public class GroupStore {
if (address.recipientId != null) { if (address.recipientId != null) {
jgen.writeNumber(address.recipientId); jgen.writeNumber(address.recipientId);
} else if (address.uuid != null) { } else if (address.uuid != null) {
jgen.writeObject(new JsonSignalServiceAddress(address.uuid, address.number)); jgen.writeObject(new JsonRecipientAddress(address.uuid, address.number));
} else { } else {
jgen.writeString(address.number); jgen.writeString(address.number);
} }
@ -393,7 +393,7 @@ public class GroupStore {
} else if (n.isNumber()) { } else if (n.isNumber()) {
addresses.add(new Member(n.numberValue().longValue(), null, null)); addresses.add(new Member(n.numberValue().longValue(), null, null));
} else { } 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)); 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.RecipientId;
import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.IOUtils;
import org.asamk.signal.manager.util.Utils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKey; 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 * @param identifier can be either a serialized uuid or a e164 phone number
*/ */
private RecipientId resolveRecipient(String identifier) { private RecipientId resolveRecipient(String identifier) {
return resolver.resolveRecipient(Utils.getSignalServiceAddressFromIdentifier(identifier)); return resolver.resolveRecipient(identifier);
} }
private File getIdentityFile(final RecipientId recipientId) { 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.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential; import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException; import java.io.IOException;
@ -45,7 +45,7 @@ public class LegacyProfileStore {
for (var entry : node) { for (var entry : node) {
var name = entry.hasNonNull("name") ? entry.get("name").asText() : null; var name = entry.hasNonNull("name") ? entry.get("name").asText() : null;
var uuid = entry.hasNonNull("uuid") ? UuidUtil.parseOrNull(entry.get("uuid").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; ProfileKey profileKey = null;
try { try {
profileKey = new ProfileKey(Base64.getDecoder().decode(entry.get("profileKey").asText())); profileKey = new ProfileKey(Base64.getDecoder().decode(entry.get("profileKey").asText()));
@ -61,7 +61,7 @@ public class LegacyProfileStore {
} }
var lastUpdateTimestamp = entry.get("lastUpdateTimestamp").asLong(); var lastUpdateTimestamp = entry.get("lastUpdateTimestamp").asLong();
var profile = jsonProcessor.treeToValue(entry.get("profile"), SignalProfile.class); var profile = jsonProcessor.treeToValue(entry.get("profile"), SignalProfile.class);
profileEntries.add(new LegacySignalProfileEntry(serviceAddress, profileEntries.add(new LegacySignalProfileEntry(address,
profileKey, profileKey,
lastUpdateTimestamp, lastUpdateTimestamp,
profile, profile,

View file

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

View file

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

View file

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

View file

@ -1,16 +1,16 @@
package org.asamk.signal.manager.storage.protocol; 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 class LegacySessionInfo {
public SignalServiceAddress address; public RecipientAddress address;
public int deviceId; public int deviceId;
public byte[] sessionRecord; 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.address = address;
this.deviceId = deviceId; this.deviceId = deviceId;
this.sessionRecord = sessionRecord; this.sessionRecord = sessionRecord;

View file

@ -129,6 +129,11 @@ public class SignalProtocolStore implements SignalServiceDataStore {
sessionStore.archiveSession(address); sessionStore.archiveSession(address);
} }
@Override
public Set<SignalProtocolAddress> getAllAddressesWithActiveSessions(final List<String> addressNames) {
return sessionStore.getAllAddressesWithActiveSessions(addressNames);
}
@Override @Override
public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
return signedPreKeyStore.loadSignedPreKey(signedPreKeyId); return signedPreKeyStore.loadSignedPreKey(signedPreKeyId);
@ -189,4 +194,11 @@ public class SignalProtocolStore implements SignalServiceDataStore {
public boolean isMultiDevice() { public boolean isMultiDevice() {
return isMultiDevice.get(); 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.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException; import java.io.IOException;
@ -18,28 +17,27 @@ public class LegacyRecipientStore {
@JsonProperty("recipientStore") @JsonProperty("recipientStore")
@JsonDeserialize(using = RecipientStoreDeserializer.class) @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; return addresses;
} }
public static class RecipientStoreDeserializer extends JsonDeserializer<List<SignalServiceAddress>> { public static class RecipientStoreDeserializer extends JsonDeserializer<List<RecipientAddress>> {
@Override @Override
public List<SignalServiceAddress> deserialize( public List<RecipientAddress> deserialize(
JsonParser jsonParser, DeserializationContext deserializationContext JsonParser jsonParser, DeserializationContext deserializationContext
) throws IOException { ) throws IOException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser); JsonNode node = jsonParser.getCodec().readTree(jsonParser);
var addresses = new ArrayList<SignalServiceAddress>(); var addresses = new ArrayList<RecipientAddress>();
if (node.isArray()) { if (node.isArray()) {
for (var recipient : node) { for (var recipient : node) {
var recipientName = recipient.get("name").asText(); var recipientName = recipient.get("name").asText();
var uuid = UuidUtil.parseOrThrow(recipient.get("uuid").asText()); var uuid = UuidUtil.parseOrThrow(recipient.get("uuid").asText());
final var serviceAddress = new SignalServiceAddress(uuid, recipientName); addresses.add(new RecipientAddress(uuid, recipientName));
addresses.add(serviceAddress);
} }
} }

View file

@ -2,13 +2,12 @@ package org.asamk.signal.manager.storage.recipients;
import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential; import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
public class Recipient { public class Recipient {
private final RecipientId recipientId; private final RecipientId recipientId;
private final SignalServiceAddress address; private final RecipientAddress address;
private final Contact contact; private final Contact contact;
@ -20,7 +19,7 @@ public class Recipient {
public Recipient( public Recipient(
final RecipientId recipientId, final RecipientId recipientId,
final SignalServiceAddress address, final RecipientAddress address,
final Contact contact, final Contact contact,
final ProfileKey profileKey, final ProfileKey profileKey,
final ProfileKeyCredential profileKeyCredential, final ProfileKeyCredential profileKeyCredential,
@ -62,7 +61,7 @@ public class Recipient {
return recipientId; return recipientId;
} }
public SignalServiceAddress getAddress() { public RecipientAddress getAddress() {
return address; return address;
} }
@ -85,7 +84,7 @@ public class Recipient {
public static final class Builder { public static final class Builder {
private RecipientId recipientId; private RecipientId recipientId;
private SignalServiceAddress address; private RecipientAddress address;
private Contact contact; private Contact contact;
private ProfileKey profileKey; private ProfileKey profileKey;
private ProfileKeyCredential profileKeyCredential; private ProfileKeyCredential profileKeyCredential;
@ -99,7 +98,7 @@ public class Recipient {
return this; return this;
} }
public Builder withAddress(final SignalServiceAddress val) { public Builder withAddress(final RecipientAddress val) {
address = val; address = val;
return this; 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 org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.UUID;
public interface RecipientResolver { public interface RecipientResolver {
RecipientId resolveRecipient(String identifier);
RecipientId resolveRecipient(RecipientAddress address);
RecipientId resolveRecipient(SignalServiceAddress 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.slf4j.LoggerFactory;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -30,9 +31,10 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.function.Supplier;
import java.util.stream.Collectors; 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); 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 storage = objectMapper.readValue(inputStream, Storage.class);
final var recipients = storage.recipients.stream().map(r -> { final var recipients = storage.recipients.stream().map(r -> {
final var recipientId = new RecipientId(r.id); final var recipientId = new RecipientId(r.id);
final var address = new SignalServiceAddress(org.whispersystems.libsignal.util.guava.Optional.fromNullable( final var address = new RecipientAddress(Optional.ofNullable(r.uuid).map(UuidUtil::parseOrThrow),
r.uuid).transform(UuidUtil::parseOrThrow), Optional.ofNullable(r.number));
org.whispersystems.libsignal.util.guava.Optional.fromNullable(r.number));
Contact contact = null; Contact contact = null;
if (r.contact != null) { if (r.contact != null) {
@ -119,7 +120,7 @@ public class RecipientStore implements ContactsStore, ProfileStore {
this.lastId = lastId; this.lastId = lastId;
} }
public SignalServiceAddress resolveServiceAddress(RecipientId recipientId) { public RecipientAddress resolveRecipientAddress(RecipientId recipientId) {
synchronized (recipients) { synchronized (recipients) {
return getRecipient(recipientId).getAddress(); return getRecipient(recipientId).getAddress();
} }
@ -134,24 +135,52 @@ public class RecipientStore implements ContactsStore, ProfileStore {
} }
} }
@Deprecated @Override
public SignalServiceAddress resolveServiceAddress(SignalServiceAddress address) {
return resolveServiceAddress(resolveRecipient(address, false));
}
public RecipientId resolveRecipient(UUID uuid) { public RecipientId resolveRecipient(UUID uuid) {
return resolveRecipient(new SignalServiceAddress(uuid, null), false); return resolveRecipient(new RecipientAddress(uuid), false);
} }
public RecipientId resolveRecipient(String number) { @Override
return resolveRecipient(new SignalServiceAddress(null, number), false); 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); 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<RecipientId> recipientIds;
final List<Pair<RecipientId, RecipientId>> toBeMerged = new ArrayList<>(); final List<Pair<RecipientId, RecipientId>> toBeMerged = new ArrayList<>();
synchronized (recipients) { synchronized (recipients) {
@ -169,10 +198,6 @@ public class RecipientStore implements ContactsStore, ProfileStore {
return recipientIds; return recipientIds;
} }
public RecipientId resolveRecipient(SignalServiceAddress address) {
return resolveRecipient(address, false);
}
@Override @Override
public void storeContact(final RecipientId recipientId, final Contact contact) { public void storeContact(final RecipientId recipientId, final Contact contact) {
synchronized (recipients) { 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. * @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. * 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; final Pair<RecipientId, Optional<RecipientId>> pair;
synchronized (recipients) { synchronized (recipients) {
pair = resolveRecipientLocked(address, isHighTrust); pair = resolveRecipientLocked(address, isHighTrust);
@ -278,30 +303,26 @@ public class RecipientStore implements ContactsStore, ProfileStore {
} }
private Pair<RecipientId, Optional<RecipientId>> resolveRecipientLocked( 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() ? Optional.<Recipient>empty()
: findByNameLocked(address.getNumber().get()); : findByNumberLocked(address.getNumber().get());
final var byUuid = !address.getUuid().isPresent() final var byUuid = address.getUuid().isEmpty() || address.getUuid().get().equals(UuidUtil.UNKNOWN_UUID)
? Optional.<Recipient>empty() ? Optional.<Recipient>empty()
: findByUuidLocked(address.getUuid().get()); : findByUuidLocked(address.getUuid().get());
if (byNumber.isEmpty() && byUuid.isEmpty()) { if (byNumber.isEmpty() && byUuid.isEmpty()) {
logger.debug("Got new recipient, both uuid and number are unknown"); 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(address), Optional.empty());
} }
return new Pair<>(addNewRecipientLocked(new SignalServiceAddress(address.getUuid().get(), null)), return new Pair<>(addNewRecipientLocked(new RecipientAddress(address.getUuid().get())), Optional.empty());
Optional.empty());
} }
if (!isHighTrust if (!isHighTrust || address.getUuid().isEmpty() || address.getNumber().isEmpty() || byNumber.equals(byUuid)) {
|| !address.getUuid().isPresent()
|| !address.getNumber().isPresent()
|| byNumber.equals(byUuid)) {
return new Pair<>(byUuid.or(() -> byNumber).map(Recipient::getRecipientId).get(), Optional.empty()); 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"); "Got recipient existing with number, but different uuid, so stripping its number and adding new recipient");
updateRecipientAddressLocked(byNumber.get().getRecipientId(), 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()); 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"); "Got separate recipients for high trust number and uuid, recipient for number has different uuid, so stripping its number");
updateRecipientAddressLocked(byNumber.get().getRecipientId(), updateRecipientAddressLocked(byNumber.get().getRecipientId(),
new SignalServiceAddress(byNumber.get().getAddress().getUuid().get(), null)); new RecipientAddress(byNumber.get().getAddress().getUuid().get()));
updateRecipientAddressLocked(byUuid.get().getRecipientId(), address); updateRecipientAddressLocked(byUuid.get().getRecipientId(), address);
return new Pair<>(byUuid.get().getRecipientId(), Optional.empty()); 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)); 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(); 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; return nextRecipientId;
} }
private void updateRecipientAddressLocked( private void updateRecipientAddressLocked(
final RecipientId recipientId, final SignalServiceAddress address final RecipientId recipientId, final RecipientAddress address
) { ) {
final var recipient = recipients.get(recipientId); final var recipient = recipients.get(recipientId);
storeRecipientLocked(recipientId, Recipient.newBuilder(recipient).withAddress(address).build()); storeRecipientLocked(recipientId, Recipient.newBuilder(recipient).withAddress(address).build());
@ -380,7 +401,7 @@ public class RecipientStore implements ContactsStore, ProfileStore {
saveLocked(); saveLocked();
} }
private Optional<Recipient> findByNameLocked(final String number) { private Optional<Recipient> findByNumberLocked(final String number) {
return recipients.entrySet() return recipients.entrySet()
.stream() .stream()
.filter(entry -> entry.getValue().getAddress().getNumber().isPresent() && number.equals(entry.getValue() .filter(entry -> entry.getValue().getAddress().getNumber().isPresent() && number.equals(entry.getValue()
@ -431,8 +452,8 @@ public class RecipientStore implements ContactsStore, ProfileStore {
.map(Enum::name) .map(Enum::name)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
return new Storage.Recipient(pair.getKey().getId(), return new Storage.Recipient(pair.getKey().getId(),
recipient.getAddress().getNumber().orNull(), recipient.getAddress().getNumber().orElse(null),
recipient.getAddress().getUuid().transform(UUID::toString).orNull(), recipient.getAddress().getUuid().map(UUID::toString).orElse(null),
recipient.getProfileKey() == null recipient.getProfileKey() == null
? null ? null
: base64.encodeToString(recipient.getProfileKey().serialize()), : 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.RecipientId;
import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.IOUtils;
import org.asamk.signal.manager.util.Utils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.NoSessionException; import org.whispersystems.libsignal.NoSessionException;
@ -23,6 +22,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; 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() { public void archiveAllSessions() {
synchronized (cachedSessions) { synchronized (cachedSessions) {
final var keys = getKeysLocked(); 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 * @param identifier can be either a serialized uuid or a e164 phone number
*/ */
private RecipientId resolveRecipient(String identifier) { private RecipientId resolveRecipient(String identifier) {
return resolver.resolveRecipient(Utils.getSignalServiceAddressFromIdentifier(identifier)); return resolver.resolveRecipient(identifier);
} }
private Key getKey(final SignalProtocolAddress address) { private Key getKey(final SignalProtocolAddress address) {

View file

@ -48,11 +48,11 @@ public class Utils {
byte[] ownId; byte[] ownId;
byte[] theirId; byte[] theirId;
if (isUuidCapable && ownAddress.getUuid().isPresent() && theirAddress.getUuid().isPresent()) { if (isUuidCapable) {
// Version 2: UUID user // Version 2: UUID user
version = 2; version = 2;
ownId = UuidUtil.toByteArray(ownAddress.getUuid().get()); ownId = UuidUtil.toByteArray(ownAddress.getUuid());
theirId = UuidUtil.toByteArray(theirAddress.getUuid().get()); theirId = UuidUtil.toByteArray(theirAddress.getUuid());
} else { } else {
// Version 1: E164 user // Version 1: E164 user
version = 1; version = 1;
@ -69,12 +69,4 @@ public class Utils {
theirId, theirId,
theirIdentityKey); 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_2" --output=json listGroups -d
run_main -u "$NUMBER_1" receive run_main -u "$NUMBER_1" receive
run_main -u "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2" 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" --verbose block -g "$GROUP_ID"
run_main -u "$NUMBER_1" unblock "$GROUP_ID" run_main -u "$NUMBER_1" --verbose unblock -g "$GROUP_ID"
## Identities ## Identities
run_main -u "$NUMBER_1" listIdentities run_main -u "$NUMBER_1" listIdentities

View file

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

View file

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

View file

@ -74,9 +74,14 @@ public class JoinGroupCommand implements JsonRpcLocalCommand {
} catch (GroupPatchNotAcceptedException e) { } catch (GroupPatchNotAcceptedException e) {
throw new UserErrorException("Failed to join group, maybe already a member"); throw new UserErrorException("Failed to join group, maybe already a member");
} catch (IOException e) { } 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) { } 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) { } catch (GroupLinkNotActiveException e) {
throw new UserErrorException("Group link is not valid: " + e.getMessage()); 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.PlainTextWriter;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.asamk.signal.util.Util.getLegacyIdentifier; 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 address = m.resolveSignalServiceAddress(contactPair.first());
final var contact = contactPair.second(); final var contact = contactPair.second();
return new JsonContact(address.getNumber().orNull(), return new JsonContact(address.getNumber().orNull(),
address.getUuid().transform(UUID::toString).orNull(), address.getUuid().toString(),
contact.getName(), contact.getName(),
contact.isBlocked(), contact.isBlocked(),
contact.getMessageExpirationTime()); contact.getMessageExpirationTime());

View file

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

View file

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

View file

@ -66,7 +66,11 @@ public class QuitGroupCommand implements JsonRpcLocalCommand {
m.deleteGroup(groupId); m.deleteGroup(groupId);
} }
} catch (IOException e) { } 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) { } catch (GroupNotFoundException e) {
throw new UserErrorException("Failed to send to group: " + e.getMessage()); throw new UserErrorException("Failed to send to group: " + e.getMessage());
} catch (LastGroupAdminException e) { } catch (LastGroupAdminException e) {

View file

@ -64,7 +64,8 @@ public class RemoteDeleteCommand implements DbusCommand, JsonRpcLocalCommand {
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException(e.getMessage()); throw new UserErrorException(e.getMessage());
} catch (IOException e) { } 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) { } catch (Signal.Error.GroupNotFound e) {
throw new UserErrorException("Failed to send to group: " + e.getMessage()); throw new UserErrorException("Failed to send to group: " + e.getMessage());
} catch (DBusExecutionException e) { } 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); m.sendEndSessionMessage(singleRecipients);
return; return;
} catch (IOException e) { } 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()); outputResult(outputWriter, results.getTimestamp());
ErrorUtils.handleSendMessageResults(results.getResults()); ErrorUtils.handleSendMessageResults(results.getResults());
} catch (AttachmentInvalidException | IOException e) { } 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) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException(e.getMessage()); throw new UserErrorException(e.getMessage());
} }
@ -141,9 +143,11 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand {
signal.sendEndSessionMessage(recipients); signal.sendEndSessionMessage(recipients);
return; return;
} catch (Signal.Error.UntrustedIdentity e) { } 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) { } 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); outputResult(outputWriter, timestamp);
return; return;
} catch (Signal.Error.UntrustedIdentity e) { } 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) { } catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send note to self message: " + e.getMessage()); 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) { } catch (UnknownObject e) {
throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage());
} catch (Signal.Error.UntrustedIdentity e) { } 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) { } 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) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException(e.getMessage()); throw new UserErrorException(e.getMessage());
} catch (IOException e) { } 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) { } catch (Signal.Error.GroupNotFound e) {
throw new UserErrorException("Failed to send to group: " + e.getMessage()); throw new UserErrorException("Failed to send to group: " + e.getMessage());
} catch (DBusExecutionException e) { } 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.CommandException;
import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.UntrustedIdentityException;
import org.asamk.signal.util.CommandUtil; import org.asamk.signal.util.CommandUtil;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import java.io.IOException; import java.io.IOException;
@ -51,7 +51,8 @@ public class SendReceiptCommand implements JsonRpcLocalCommand {
throw new UserErrorException("Unknown receipt type: " + type); throw new UserErrorException("Unknown receipt type: " + type);
} }
} catch (IOException | UntrustedIdentityException e) { } 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.CommandException;
import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.manager.Manager; 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.RecipientIdentifier;
import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.TypingAction;
import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupNotFoundException;
import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.util.CommandUtil; import org.asamk.signal.util.CommandUtil;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet; import java.util.HashSet;
@ -59,7 +59,8 @@ public class SendTypingCommand implements JsonRpcLocalCommand {
try { try {
m.sendTypingMessage(action, recipientIdentifiers); m.sendTypingMessage(action, recipientIdentifiers);
} catch (IOException | UntrustedIdentityException e) { } 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) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException("Failed to send to group: " + e.getMessage()); 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) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException(e.getMessage()); throw new UserErrorException(e.getMessage());
} catch (IOException e) { } 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) { } catch (Signal.Error.AttachmentInvalid e) {
throw new UserErrorException("Failed to add avatar attachment for group\": " + e.getMessage()); throw new UserErrorException("Failed to add avatar attachment for group\": " + e.getMessage());
} catch (DBusExecutionException e) { } 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.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.File; import java.io.File;
@ -244,6 +245,8 @@ public class DbusSignalImpl implements Signal {
m.setContactName(getSingleRecipientIdentifier(number, m.getUsername()), name); m.setContactName(getSingleRecipientIdentifier(number, m.getUsername()), name);
} catch (NotMasterDeviceException e) { } catch (NotMasterDeviceException e) {
throw new Error.Failure("This command doesn't work on linked devices."); 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.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; 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; import static org.asamk.signal.util.Util.getLegacyIdentifier;
@ -29,10 +26,10 @@ public class JsonMention {
final int length; final int length;
JsonMention(SignalServiceDataMessage.Mention mention, Manager m) { 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.name = getLegacyIdentifier(address);
this.number = address.getNumber().orNull(); this.number = address.getNumber().orNull();
this.uuid = address.getUuid().transform(UUID::toString).orNull(); this.uuid = address.getUuid().toString();
this.start = mention.getStart(); this.start = mention.getStart();
this.length = mention.getLength(); this.length = mention.getLength();
} }

View file

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

View file

@ -8,7 +8,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
@ -43,7 +42,7 @@ public class JsonQuote {
final var address = m.resolveSignalServiceAddress(quote.getAuthor()); final var address = m.resolveSignalServiceAddress(quote.getAuthor());
this.author = getLegacyIdentifier(address); this.author = getLegacyIdentifier(address);
this.authorNumber = address.getNumber().orNull(); this.authorNumber = address.getNumber().orNull();
this.authorUuid = address.getUuid().transform(UUID::toString).orNull(); this.authorUuid = address.getUuid().toString();
this.text = quote.getText(); this.text = quote.getText();
if (quote.getMentions() != null && quote.getMentions().size() > 0) { 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.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction;
import java.util.UUID;
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class JsonReaction { public class JsonReaction {
@ -35,7 +33,7 @@ public class JsonReaction {
final var address = m.resolveSignalServiceAddress(reaction.getTargetAuthor()); final var address = m.resolveSignalServiceAddress(reaction.getTargetAuthor());
this.targetAuthor = getLegacyIdentifier(address); this.targetAuthor = getLegacyIdentifier(address);
this.targetAuthorNumber = address.getNumber().orNull(); this.targetAuthorNumber = address.getNumber().orNull();
this.targetAuthorUuid = address.getUuid().transform(UUID::toString).orNull(); this.targetAuthorUuid = address.getUuid().toString();
this.targetSentTimestamp = reaction.getTargetSentTimestamp(); this.targetSentTimestamp = reaction.getTargetSentTimestamp();
this.isRemove = reaction.isRemove(); this.isRemove = reaction.isRemove();
} }

View file

@ -6,8 +6,6 @@ import org.asamk.Signal;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
import java.util.UUID;
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
class JsonSyncDataMessage extends JsonDataMessage { class JsonSyncDataMessage extends JsonDataMessage {
@ -29,7 +27,7 @@ class JsonSyncDataMessage extends JsonDataMessage {
final var address = transcriptMessage.getDestination().get(); final var address = transcriptMessage.getDestination().get();
this.destination = getLegacyIdentifier(address); this.destination = getLegacyIdentifier(address);
this.destinationNumber = address.getNumber().orNull(); this.destinationNumber = address.getNumber().orNull();
this.destinationUuid = address.getUuid().transform(UUID::toString).orNull(); this.destinationUuid = address.getUuid().toString();
} else { } else {
this.destination = null; this.destination = null;
this.destinationNumber = 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 org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
import java.util.UUID;
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
class JsonSyncReadMessage { class JsonSyncReadMessage {
@ -27,7 +25,7 @@ class JsonSyncReadMessage {
final var sender = readMessage.getSender(); final var sender = readMessage.getSender();
this.sender = getLegacyIdentifier(sender); this.sender = getLegacyIdentifier(sender);
this.senderNumber = sender.getNumber().orNull(); this.senderNumber = sender.getNumber().orNull();
this.senderUuid = sender.getUuid().transform(UUID::toString).orNull(); this.senderUuid = sender.getUuid().toString();
this.timestamp = readMessage.getTimestamp(); this.timestamp = readMessage.getTimestamp();
} }
} }

View file

@ -60,7 +60,7 @@ public class Util {
} }
public static String getLegacyIdentifier(final SignalServiceAddress address) { 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() { public static ObjectMapper createJsonObjectMapper() {