Refactor RecipientAddress

This commit is contained in:
AsamK 2022-10-08 12:40:00 +02:00
parent 34cc64f8ce
commit 7eb7ee44f2
40 changed files with 392 additions and 127 deletions

View file

@ -14,6 +14,7 @@ import org.asamk.signal.manager.api.NotPrimaryDeviceException;
import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.Pair;
import org.asamk.signal.manager.api.PendingAdminApprovalException; import org.asamk.signal.manager.api.PendingAdminApprovalException;
import org.asamk.signal.manager.api.ReceiveConfig; import org.asamk.signal.manager.api.ReceiveConfig;
import org.asamk.signal.manager.api.Recipient;
import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendGroupMessageResults;
import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.manager.api.SendMessageResults;
@ -32,7 +33,6 @@ import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.LastGroupAdminException;
import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.Recipient;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import java.io.Closeable; import java.io.Closeable;

View file

@ -29,6 +29,7 @@ import org.asamk.signal.manager.api.NotPrimaryDeviceException;
import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.Pair;
import org.asamk.signal.manager.api.PendingAdminApprovalException; import org.asamk.signal.manager.api.PendingAdminApprovalException;
import org.asamk.signal.manager.api.ReceiveConfig; import org.asamk.signal.manager.api.ReceiveConfig;
import org.asamk.signal.manager.api.Recipient;
import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendGroupMessageResults;
import org.asamk.signal.manager.api.SendMessageResult; import org.asamk.signal.manager.api.SendMessageResult;
@ -54,7 +55,6 @@ import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.groups.GroupInfo;
import org.asamk.signal.manager.storage.identities.IdentityInfo; import org.asamk.signal.manager.storage.identities.IdentityInfo;
import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.Recipient;
import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack;
import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore; import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore;
@ -707,13 +707,13 @@ class ManagerImpl implements Manager {
@Override @Override
public void deleteRecipient(final RecipientIdentifier.Single recipient) { public void deleteRecipient(final RecipientIdentifier.Single recipient) {
account.removeRecipient(account.getRecipientResolver().resolveRecipient(recipient.toPartialRecipientAddress())); account.removeRecipient(account.getRecipientResolver().resolveRecipient(recipient.getIdentifier()));
} }
@Override @Override
public void deleteContact(final RecipientIdentifier.Single recipient) { public void deleteContact(final RecipientIdentifier.Single recipient) {
account.getContactStore() account.getContactStore()
.deleteContact(account.getRecipientResolver().resolveRecipient(recipient.toPartialRecipientAddress())); .deleteContact(account.getRecipientResolver().resolveRecipient(recipient.getIdentifier()));
} }
@Override @Override
@ -1005,7 +1005,16 @@ class ManagerImpl implements Manager {
} }
// refresh profiles of explicitly given recipients // refresh profiles of explicitly given recipients
context.getProfileHelper().refreshRecipientProfiles(recipientIds); context.getProfileHelper().refreshRecipientProfiles(recipientIds);
return account.getRecipientStore().getRecipients(onlyContacts, blocked, recipientIds, name); return account.getRecipientStore()
.getRecipients(onlyContacts, blocked, recipientIds, name)
.stream()
.map(s -> new Recipient(s.getRecipientId(),
s.getAddress().toApiRecipientAddress(),
s.getContact(),
s.getProfileKey(),
s.getExpiringProfileKeyCredential(),
s.getProfile()))
.toList();
} }
@Override @Override
@ -1049,7 +1058,7 @@ class ManagerImpl implements Manager {
.resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(identityInfo.getServiceId())); .resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(identityInfo.getServiceId()));
final var scannableFingerprint = context.getIdentityHelper() final var scannableFingerprint = context.getIdentityHelper()
.computeSafetyNumberForScanning(identityInfo.getServiceId(), identityInfo.getIdentityKey()); .computeSafetyNumberForScanning(identityInfo.getServiceId(), identityInfo.getIdentityKey());
return new Identity(address, return new Identity(address.toApiRecipientAddress(),
identityInfo.getIdentityKey(), identityInfo.getIdentityKey(),
context.getIdentityHelper() context.getIdentityHelper()
.computeSafetyNumber(identityInfo.getServiceId(), identityInfo.getIdentityKey()), .computeSafetyNumber(identityInfo.getServiceId(), identityInfo.getIdentityKey()),

View file

@ -5,7 +5,6 @@ import org.asamk.signal.manager.groups.GroupInviteLinkUrl;
import org.asamk.signal.manager.groups.GroupPermission; import org.asamk.signal.manager.groups.GroupPermission;
import org.asamk.signal.manager.helper.RecipientAddressResolver; import org.asamk.signal.manager.helper.RecipientAddressResolver;
import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.groups.GroupInfo;
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 java.util.Set; import java.util.Set;
@ -40,22 +39,27 @@ public record Group(
groupInfo.getMembers() groupInfo.getMembers()
.stream() .stream()
.map(recipientStore::resolveRecipientAddress) .map(recipientStore::resolveRecipientAddress)
.map(org.asamk.signal.manager.storage.recipients.RecipientAddress::toApiRecipientAddress)
.collect(Collectors.toSet()), .collect(Collectors.toSet()),
groupInfo.getPendingMembers() groupInfo.getPendingMembers()
.stream() .stream()
.map(recipientStore::resolveRecipientAddress) .map(recipientStore::resolveRecipientAddress)
.map(org.asamk.signal.manager.storage.recipients.RecipientAddress::toApiRecipientAddress)
.collect(Collectors.toSet()), .collect(Collectors.toSet()),
groupInfo.getRequestingMembers() groupInfo.getRequestingMembers()
.stream() .stream()
.map(recipientStore::resolveRecipientAddress) .map(recipientStore::resolveRecipientAddress)
.map(org.asamk.signal.manager.storage.recipients.RecipientAddress::toApiRecipientAddress)
.collect(Collectors.toSet()), .collect(Collectors.toSet()),
groupInfo.getAdminMembers() groupInfo.getAdminMembers()
.stream() .stream()
.map(recipientStore::resolveRecipientAddress) .map(recipientStore::resolveRecipientAddress)
.map(org.asamk.signal.manager.storage.recipients.RecipientAddress::toApiRecipientAddress)
.collect(Collectors.toSet()), .collect(Collectors.toSet()),
groupInfo.getBannedMembers() groupInfo.getBannedMembers()
.stream() .stream()
.map(recipientStore::resolveRecipientAddress) .map(recipientStore::resolveRecipientAddress)
.map(org.asamk.signal.manager.storage.recipients.RecipientAddress::toApiRecipientAddress)
.collect(Collectors.toSet()), .collect(Collectors.toSet()),
groupInfo.isBlocked(), groupInfo.isBlocked(),
groupInfo.getMessageExpirationTimer(), groupInfo.getMessageExpirationTimer(),

View file

@ -1,6 +1,5 @@
package org.asamk.signal.manager.api; package org.asamk.signal.manager.api;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKey;
public record Identity( public record Identity(

View file

@ -3,7 +3,6 @@ package org.asamk.signal.manager.api;
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.manager.helper.RecipientAddressResolver; import org.asamk.signal.manager.helper.RecipientAddressResolver;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.signal.libsignal.metadata.ProtocolException; import org.signal.libsignal.metadata.ProtocolException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
@ -184,7 +183,7 @@ public record MessageEnvelope(
RecipientAddressResolver addressResolver RecipientAddressResolver addressResolver
) { ) {
return new StoryContext(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient( return new StoryContext(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(
storyContext.getAuthorServiceId())), storyContext.getSentTimestamp()); storyContext.getAuthorServiceId())).toApiRecipientAddress(), storyContext.getSentTimestamp());
} }
} }
@ -205,7 +204,8 @@ public record MessageEnvelope(
RecipientAddressResolver addressResolver RecipientAddressResolver addressResolver
) { ) {
return new Reaction(reaction.getTargetSentTimestamp(), return new Reaction(reaction.getTargetSentTimestamp(),
addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(reaction.getTargetAuthor())), addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(reaction.getTargetAuthor()))
.toApiRecipientAddress(),
reaction.getEmoji(), reaction.getEmoji(),
reaction.isRemove()); reaction.isRemove());
} }
@ -226,7 +226,8 @@ public record MessageEnvelope(
final AttachmentFileProvider fileProvider final AttachmentFileProvider fileProvider
) { ) {
return new Quote(quote.getId(), return new Quote(quote.getId(),
addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(quote.getAuthor())), addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(quote.getAuthor()))
.toApiRecipientAddress(),
Optional.ofNullable(quote.getText()), Optional.ofNullable(quote.getText()),
quote.getMentions() == null quote.getMentions() == null
? List.of() ? List.of()
@ -255,9 +256,8 @@ public record MessageEnvelope(
RecipientResolver recipientResolver, RecipientResolver recipientResolver,
RecipientAddressResolver addressResolver RecipientAddressResolver addressResolver
) { ) {
return new Mention(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(mention.getServiceId())), return new Mention(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(mention.getServiceId()))
mention.getStart(), .toApiRecipientAddress(), mention.getStart(), mention.getLength());
mention.getLength());
} }
} }
@ -552,10 +552,12 @@ public record MessageEnvelope(
return new Sent(sentMessage.getTimestamp(), return new Sent(sentMessage.getTimestamp(),
sentMessage.getExpirationStartTimestamp(), sentMessage.getExpirationStartTimestamp(),
sentMessage.getDestination() sentMessage.getDestination()
.map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))), .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))
.toApiRecipientAddress()),
sentMessage.getRecipients() sentMessage.getRecipients()
.stream() .stream()
.map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))) .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))
.toApiRecipientAddress())
.collect(Collectors.toSet()), .collect(Collectors.toSet()),
sentMessage.getDataMessage() sentMessage.getDataMessage()
.map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider)), .map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider)),
@ -572,7 +574,8 @@ public record MessageEnvelope(
) { ) {
return new Blocked(blockedListMessage.getAddresses() return new Blocked(blockedListMessage.getAddresses()
.stream() .stream()
.map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))) .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))
.toApiRecipientAddress())
.toList(), blockedListMessage.getGroupIds().stream().map(GroupId::unknownVersion).toList()); .toList(), blockedListMessage.getGroupIds().stream().map(GroupId::unknownVersion).toList());
} }
} }
@ -584,8 +587,8 @@ public record MessageEnvelope(
RecipientResolver recipientResolver, RecipientResolver recipientResolver,
RecipientAddressResolver addressResolver RecipientAddressResolver addressResolver
) { ) {
return new Read(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(readMessage.getSender())), return new Read(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(readMessage.getSender()))
readMessage.getTimestamp()); .toApiRecipientAddress(), readMessage.getTimestamp());
} }
} }
@ -596,8 +599,8 @@ public record MessageEnvelope(
RecipientResolver recipientResolver, RecipientResolver recipientResolver,
RecipientAddressResolver addressResolver RecipientAddressResolver addressResolver
) { ) {
return new Viewed(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(readMessage.getSender())), return new Viewed(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(readMessage.getSender()))
readMessage.getTimestamp()); .toApiRecipientAddress(), readMessage.getTimestamp());
} }
} }
@ -609,7 +612,7 @@ public record MessageEnvelope(
RecipientAddressResolver addressResolver RecipientAddressResolver addressResolver
) { ) {
return new ViewOnceOpen(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient( return new ViewOnceOpen(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(
readMessage.getSender())), readMessage.getTimestamp()); readMessage.getSender())).toApiRecipientAddress(), readMessage.getTimestamp());
} }
} }
@ -637,7 +640,8 @@ public record MessageEnvelope(
return new MessageRequestResponse(Type.from(messageRequestResponse.getType()), return new MessageRequestResponse(Type.from(messageRequestResponse.getType()),
messageRequestResponse.getGroupId().map(GroupId::unknownVersion), messageRequestResponse.getGroupId().map(GroupId::unknownVersion),
messageRequestResponse.getPerson() messageRequestResponse.getPerson()
.map(p -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(p)))); .map(p -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(p))
.toApiRecipientAddress()));
} }
public enum Type { public enum Type {
@ -898,7 +902,7 @@ public record MessageEnvelope(
return new MessageEnvelope(source == null return new MessageEnvelope(source == null
? Optional.empty() ? Optional.empty()
: Optional.of(addressResolver.resolveRecipientAddress(source)), : Optional.of(addressResolver.resolveRecipientAddress(source).toApiRecipientAddress()),
sourceDevice, sourceDevice,
envelope.getTimestamp(), envelope.getTimestamp(),
envelope.getServerReceivedTimestamp(), envelope.getServerReceivedTimestamp(),

View file

@ -0,0 +1,153 @@
package org.asamk.signal.manager.api;
import org.asamk.signal.manager.storage.recipients.Contact;
import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import java.util.Objects;
public class Recipient {
private final RecipientId recipientId;
private final RecipientAddress address;
private final Contact contact;
private final ProfileKey profileKey;
private final ExpiringProfileKeyCredential expiringProfileKeyCredential;
private final Profile profile;
public Recipient(
final RecipientId recipientId,
final RecipientAddress address,
final Contact contact,
final ProfileKey profileKey,
final ExpiringProfileKeyCredential expiringProfileKeyCredential,
final Profile profile
) {
this.recipientId = recipientId;
this.address = address;
this.contact = contact;
this.profileKey = profileKey;
this.expiringProfileKeyCredential = expiringProfileKeyCredential;
this.profile = profile;
}
private Recipient(final Builder builder) {
recipientId = builder.recipientId;
address = builder.address;
contact = builder.contact;
profileKey = builder.profileKey;
expiringProfileKeyCredential = builder.expiringProfileKeyCredential1;
profile = builder.profile;
}
public static Builder newBuilder() {
return new Builder();
}
public static Builder newBuilder(final Recipient copy) {
Builder builder = new Builder();
builder.recipientId = copy.getRecipientId();
builder.address = copy.getAddress();
builder.contact = copy.getContact();
builder.profileKey = copy.getProfileKey();
builder.expiringProfileKeyCredential1 = copy.getExpiringProfileKeyCredential();
builder.profile = copy.getProfile();
return builder;
}
public RecipientId getRecipientId() {
return recipientId;
}
public RecipientAddress getAddress() {
return address;
}
public Contact getContact() {
return contact;
}
public ProfileKey getProfileKey() {
return profileKey;
}
public ExpiringProfileKeyCredential getExpiringProfileKeyCredential() {
return expiringProfileKeyCredential;
}
public Profile getProfile() {
return profile;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Recipient recipient = (Recipient) o;
return Objects.equals(recipientId, recipient.recipientId)
&& Objects.equals(address, recipient.address)
&& Objects.equals(contact, recipient.contact)
&& Objects.equals(profileKey, recipient.profileKey)
&& Objects.equals(expiringProfileKeyCredential, recipient.expiringProfileKeyCredential)
&& Objects.equals(profile, recipient.profile);
}
@Override
public int hashCode() {
return Objects.hash(recipientId, address, contact, profileKey, expiringProfileKeyCredential, profile);
}
public static final class Builder {
private RecipientId recipientId;
private RecipientAddress address;
private Contact contact;
private ProfileKey profileKey;
private ExpiringProfileKeyCredential expiringProfileKeyCredential1;
private Profile profile;
private Builder() {
}
public Builder withRecipientId(final RecipientId val) {
recipientId = val;
return this;
}
public Builder withAddress(final RecipientAddress val) {
address = val;
return this;
}
public Builder withContact(final Contact val) {
contact = val;
return this;
}
public Builder withProfileKey(final ProfileKey val) {
profileKey = val;
return this;
}
public Builder withExpiringProfileKeyCredential(final ExpiringProfileKeyCredential val) {
expiringProfileKeyCredential1 = val;
return this;
}
public Builder withProfile(final Profile val) {
profile = val;
return this;
}
public Recipient build() {
return new Recipient(this);
}
}
}

View file

@ -0,0 +1,71 @@
package org.asamk.signal.manager.api;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.Optional;
import java.util.UUID;
public record RecipientAddress(Optional<UUID> uuid, Optional<String> number) {
public static final UUID UNKNOWN_UUID = ServiceId.UNKNOWN.uuid();
/**
* Construct a RecipientAddress.
*
* @param uuid The UUID of the user, if available.
* @param number The phone number of the user, if available.
*/
public RecipientAddress {
uuid = uuid.isPresent() && uuid.get().equals(UNKNOWN_UUID) ? Optional.empty() : uuid;
if (uuid.isEmpty() && number.isEmpty()) {
throw new AssertionError("Must have either a UUID or E164 number!");
}
}
public RecipientAddress(UUID uuid, String e164) {
this(Optional.ofNullable(uuid), Optional.ofNullable(e164));
}
public RecipientAddress(SignalServiceAddress address) {
this(Optional.of(address.getServiceId().uuid()), address.getNumber());
}
public RecipientAddress(UUID uuid) {
this(Optional.of(uuid), Optional.empty());
}
public ServiceId getServiceId() {
return ServiceId.from(uuid.orElse(UNKNOWN_UUID));
}
public String getIdentifier() {
if (uuid.isPresent()) {
return uuid.get().toString();
} else if (number.isPresent()) {
return number.get();
} else {
throw new AssertionError("Given the checks in the constructor, this should not be possible.");
}
}
public String getLegacyIdentifier() {
if (number.isPresent()) {
return number.get();
} else if (uuid.isPresent()) {
return uuid.get().toString();
} 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())) || (
number.isPresent() && other.number.isPresent() && number.get().equals(other.number.get())
);
}
public SignalServiceAddress toSignalServiceAddress() {
return new SignalServiceAddress(getServiceId(), number);
}
}

View file

@ -1,7 +1,6 @@
package org.asamk.signal.manager.api; package org.asamk.signal.manager.api;
import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupId;
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.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;

View file

@ -1,7 +1,6 @@
package org.asamk.signal.manager.api; package org.asamk.signal.manager.api;
import org.asamk.signal.manager.helper.RecipientAddressResolver; import org.asamk.signal.manager.helper.RecipientAddressResolver;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKey;
@ -43,7 +42,7 @@ public record SendMessageResult(
RecipientAddressResolver addressResolver RecipientAddressResolver addressResolver
) { ) {
return new SendMessageResult(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient( return new SendMessageResult(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(
sendMessageResult.getAddress())), sendMessageResult.getAddress())).toApiRecipientAddress(),
sendMessageResult.isSuccess(), sendMessageResult.isSuccess(),
sendMessageResult.isNetworkFailure(), sendMessageResult.isNetworkFailure(),
sendMessageResult.isUnregisteredFailure(), sendMessageResult.isUnregisteredFailure(),

View file

@ -1,7 +1,5 @@
package org.asamk.signal.manager.api; package org.asamk.signal.manager.api;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
public class UnregisteredRecipientException extends Exception { public class UnregisteredRecipientException extends Exception {
private final RecipientAddress sender; private final RecipientAddress sender;

View file

@ -1,7 +1,5 @@
package org.asamk.signal.manager.api; package org.asamk.signal.manager.api;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
public class UntrustedIdentityException extends Exception { public class UntrustedIdentityException extends Exception {
private final RecipientAddress sender; private final RecipientAddress sender;

View file

@ -85,7 +85,7 @@ public class IdentityHelper {
account.getAciIdentityKeyPair().getPublicKey(), account.getAciIdentityKeyPair().getPublicKey(),
address.getServiceId().equals(serviceId) address.getServiceId().equals(serviceId)
? address ? address
: new RecipientAddress(serviceId.uuid(), address.number().orElse(null)), : new RecipientAddress(serviceId, address.number().orElse(null)),
theirIdentityKey); theirIdentityKey);
} }

View file

@ -97,7 +97,8 @@ public final class IncomingMessageHandler {
} catch (ProtocolUntrustedIdentityException e) { } catch (ProtocolUntrustedIdentityException e) {
final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender()); final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender());
final var exception = new UntrustedIdentityException(account.getRecipientAddressResolver() final var exception = new UntrustedIdentityException(account.getRecipientAddressResolver()
.resolveRecipientAddress(recipientId), e.getSenderDevice()); .resolveRecipientAddress(recipientId)
.toApiRecipientAddress(), e.getSenderDevice());
return new Pair<>(List.of(), exception); return new Pair<>(List.of(), exception);
} catch (Exception e) { } catch (Exception e) {
return new Pair<>(List.of(), e); return new Pair<>(List.of(), e);
@ -129,7 +130,8 @@ public final class IncomingMessageHandler {
final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender()); final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender());
actions.add(new RetrieveProfileAction(recipientId)); actions.add(new RetrieveProfileAction(recipientId));
exception = new UntrustedIdentityException(account.getRecipientAddressResolver() exception = new UntrustedIdentityException(account.getRecipientAddressResolver()
.resolveRecipientAddress(recipientId), e.getSenderDevice()); .resolveRecipientAddress(recipientId)
.toApiRecipientAddress(), e.getSenderDevice());
} catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException | } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException |
ProtocolInvalidMessageException e) { ProtocolInvalidMessageException e) {
logger.debug("Failed to decrypt incoming message", e); logger.debug("Failed to decrypt incoming message", e);

View file

@ -212,7 +212,7 @@ public class ReceiveHelper {
if (exception instanceof UntrustedIdentityException) { if (exception instanceof UntrustedIdentityException) {
logger.debug("Keeping message with untrusted identity in message cache"); logger.debug("Keeping message with untrusted identity in message cache");
final var address = ((UntrustedIdentityException) exception).getSender(); final var address = ((UntrustedIdentityException) exception).getSender();
final var recipientId = account.getRecipientResolver().resolveRecipient(address); final var recipientId = account.getRecipientResolver().resolveRecipient(address.getServiceId());
if (!envelope.hasSourceUuid()) { if (!envelope.hasSourceUuid()) {
try { try {
cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId); cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId);
@ -260,7 +260,7 @@ public class ReceiveHelper {
} }
if (!envelope.hasSourceUuid()) { if (!envelope.hasSourceUuid()) {
final var identifier = ((UntrustedIdentityException) exception).getSender(); final var identifier = ((UntrustedIdentityException) exception).getSender();
final var recipientId = account.getRecipientResolver().resolveRecipient(identifier); final var recipientId = account.getRecipientResolver().resolveRecipient(identifier.getServiceId());
try { try {
account.getMessageCache().replaceSender(cachedMessage, recipientId); account.getMessageCache().replaceSender(cachedMessage, recipientId);
} catch (IOException ioException) { } catch (IOException ioException) {

View file

@ -6,7 +6,6 @@ import org.asamk.signal.manager.api.UnregisteredRecipientException;
import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.SignalAccount;
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.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.InvalidKeyException;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -44,7 +43,7 @@ public class RecipientHelper {
public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) { public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) {
final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId); final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId);
if (address.number().isEmpty() || address.uuid().isPresent()) { if (address.number().isEmpty() || address.serviceId().isPresent()) {
return address.toSignalServiceAddress(); return address.toSignalServiceAddress();
} }
@ -127,11 +126,11 @@ public class RecipientHelper {
try { try {
aciMap = getRegisteredUsers(Set.of(number)); aciMap = getRegisteredUsers(Set.of(number));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new UnregisteredRecipientException(new RecipientAddress(null, number)); throw new UnregisteredRecipientException(new org.asamk.signal.manager.api.RecipientAddress(null, number));
} }
final var uuid = aciMap.get(number); final var uuid = aciMap.get(number);
if (uuid == null) { if (uuid == null) {
throw new UnregisteredRecipientException(new RecipientAddress(null, number)); throw new UnregisteredRecipientException(new org.asamk.signal.manager.api.RecipientAddress(null, number));
} }
return uuid; return uuid;
} }

View file

@ -101,8 +101,7 @@ public class StorageHelper {
} }
final var contactRecord = record.getContact().get(); final var contactRecord = record.getContact().get();
final var address = new RecipientAddress(contactRecord.getServiceId().uuid(), final var address = new RecipientAddress(contactRecord.getServiceId(), contactRecord.getNumber().orElse(null));
contactRecord.getNumber().orElse(null));
final var recipientId = account.getRecipientResolver().resolveRecipient(address); final var recipientId = account.getRecipientResolver().resolveRecipient(address);
final var contact = account.getContactStore().getContact(recipientId); final var contact = account.getContactStore().getContact(recipientId);

View file

@ -417,8 +417,8 @@ public class SignalAccount implements Closeable {
getRecipientStore().deleteRecipientData(recipientId); getRecipientStore().deleteRecipientData(recipientId);
getMessageCache().deleteMessages(recipientId); getMessageCache().deleteMessages(recipientId);
final var recipientAddress = getRecipientStore().resolveRecipientAddress(recipientId); final var recipientAddress = getRecipientStore().resolveRecipientAddress(recipientId);
if (recipientAddress.uuid().isPresent()) { if (recipientAddress.serviceId().isPresent()) {
final var serviceId = ServiceId.from(recipientAddress.uuid().get()); final var serviceId = recipientAddress.serviceId().get();
getAciSessionStore().deleteAllSessions(serviceId); getAciSessionStore().deleteAllSessions(serviceId);
getPniSessionStore().deleteAllSessions(serviceId); getPniSessionStore().deleteAllSessions(serviceId);
getIdentityKeyStore().deleteIdentity(serviceId); getIdentityKeyStore().deleteIdentity(serviceId);
@ -791,7 +791,7 @@ public class SignalAccount implements Closeable {
if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) { if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) {
logger.debug("Migrating legacy identity session store."); logger.debug("Migrating legacy identity session store.");
for (var identity : legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentities()) { for (var identity : legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentities()) {
if (identity.getAddress().uuid().isEmpty()) { if (identity.getAddress().serviceId().isEmpty()) {
continue; continue;
} }
final var serviceId = identity.getAddress().getServiceId(); final var serviceId = identity.getAddress().getServiceId();
@ -1324,7 +1324,7 @@ public class SignalAccount implements Closeable {
} }
public RecipientAddress getSelfRecipientAddress() { public RecipientAddress getSelfRecipientAddress() {
return new RecipientAddress(aci == null ? null : aci.uuid(), number); return new RecipientAddress(aci, pni, number);
} }
public RecipientId getSelfRecipientId() { public RecipientId getSelfRecipientId() {

View file

@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.SerializationFeature;
import org.asamk.signal.manager.storage.recipients.RecipientAddress; 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.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.ServiceIdType;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
@ -57,7 +58,7 @@ public class Utils {
public static RecipientAddress getRecipientAddressFromIdentifier(final String identifier) { public static RecipientAddress getRecipientAddressFromIdentifier(final String identifier) {
if (UuidUtil.isUuid(identifier)) { if (UuidUtil.isUuid(identifier)) {
return new RecipientAddress(UuidUtil.parseOrThrow(identifier)); return new RecipientAddress(ServiceId.parseOrThrow(identifier));
} else { } else {
return new RecipientAddress(Optional.empty(), Optional.of(identifier)); return new RecipientAddress(Optional.empty(), Optional.of(identifier));
} }

View file

@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.whispersystems.signalservice.api.push.ServiceId;
import java.util.UUID; import java.util.UUID;
@ -43,6 +44,6 @@ public class LegacyContactInfo {
@JsonIgnore @JsonIgnore
public RecipientAddress getAddress() { public RecipientAddress getAddress() {
return new RecipientAddress(uuid, number); return new RecipientAddress(uuid == null ? null : ServiceId.from(uuid), number);
} }
} }

View file

@ -18,7 +18,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.DistributionId;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.internal.util.Hex; import org.whispersystems.signalservice.internal.util.Hex;
import java.io.File; import java.io.File;
@ -45,7 +45,7 @@ public class LegacyGroupStore {
if (g instanceof Storage.GroupV1 g1) { if (g instanceof Storage.GroupV1 g1) {
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 RecipientAddress(UuidUtil.parseOrNull(m.uuid), return recipientResolver.resolveRecipient(new RecipientAddress(ServiceId.parseOrNull(m.uuid),
m.number)); m.number));
} }

View file

@ -11,7 +11,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.push.ServiceId;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -43,8 +43,8 @@ public class LegacyProfileStore {
if (node.isArray()) { if (node.isArray()) {
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 serviceId = entry.hasNonNull("uuid") ? ServiceId.parseOrNull(entry.get("uuid").asText()) : null;
final var address = new RecipientAddress(uuid, name); final var address = new RecipientAddress(serviceId, 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()));

View file

@ -13,6 +13,7 @@ import org.signal.libsignal.protocol.IdentityKeyPair;
import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.InvalidKeyException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException; import java.io.IOException;
@ -100,7 +101,7 @@ public class LegacyJsonIdentityKeyStore {
: null; : null;
final var address = uuid == null final var address = uuid == null
? Utils.getRecipientAddressFromIdentifier(trustedKeyName) ? Utils.getRecipientAddressFromIdentifier(trustedKeyName)
: new RecipientAddress(uuid, trustedKeyName); : new RecipientAddress(ServiceId.from(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(

View file

@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import org.asamk.signal.manager.storage.Utils; import org.asamk.signal.manager.storage.Utils;
import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException; import java.io.IOException;
@ -47,7 +48,7 @@ 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 address = uuid == null final var address = uuid == null
? Utils.getRecipientAddressFromIdentifier(sessionName) ? Utils.getRecipientAddressFromIdentifier(sessionName)
: new RecipientAddress(uuid, sessionName); : new RecipientAddress(ServiceId.from(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(address, deviceId, record); var sessionInfo = new LegacySessionInfo(address, deviceId, record);

View file

@ -7,7 +7,7 @@ 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.util.UuidUtil; import org.whispersystems.signalservice.api.push.ServiceId;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -36,8 +36,8 @@ public class LegacyRecipientStore {
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 serviceId = ServiceId.parseOrThrow(recipient.get("uuid").asText());
addresses.add(new RecipientAddress(uuid, recipientName)); addresses.add(new RecipientAddress(serviceId, recipientName));
} }
} }

View file

@ -6,7 +6,7 @@ import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential;
import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.push.ServiceId;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -31,7 +31,7 @@ public class LegacyRecipientStore2 {
final var recipients = storage.recipients.stream().map(r -> { final var recipients = storage.recipients.stream().map(r -> {
final var recipientId = new RecipientId(r.id, recipientStore); final var recipientId = new RecipientId(r.id, recipientStore);
final var address = new RecipientAddress(Optional.ofNullable(r.uuid).map(UuidUtil::parseOrThrow), final var address = new RecipientAddress(Optional.ofNullable(r.uuid).map(ServiceId::parseOrThrow),
Optional.ofNullable(r.number)); Optional.ofNullable(r.number));
Contact contact = null; Contact contact = null;

View file

@ -1,47 +1,61 @@
package org.asamk.signal.manager.storage.recipients; package org.asamk.signal.manager.storage.recipients;
import org.whispersystems.signalservice.api.push.PNI;
import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
public record RecipientAddress(Optional<UUID> uuid, Optional<String> number) { public record RecipientAddress(Optional<ServiceId> serviceId, Optional<PNI> pni, Optional<String> number) {
public static final UUID UNKNOWN_UUID = ServiceId.UNKNOWN.uuid();
/** /**
* Construct a RecipientAddress. * Construct a RecipientAddress.
* *
* @param uuid The UUID of the user, if available. * @param serviceId The ACI or PNI of the user, if available.
* @param number The phone number of the user, if available. * @param number The phone number of the user, if available.
*/ */
public RecipientAddress { public RecipientAddress {
uuid = uuid.isPresent() && uuid.get().equals(UNKNOWN_UUID) ? Optional.empty() : uuid; if (serviceId.isPresent() && serviceId.get().equals(ServiceId.UNKNOWN)) {
if (uuid.isEmpty() && number.isEmpty()) { serviceId = Optional.empty();
throw new AssertionError("Must have either a UUID or E164 number!"); }
if (pni.isPresent() && pni.get().equals(ServiceId.UNKNOWN)) {
pni = Optional.empty();
}
if (serviceId.isEmpty() && pni.isPresent()) {
serviceId = Optional.of(pni.get());
}
if (serviceId.isEmpty() && number.isEmpty()) {
throw new AssertionError("Must have either a ServiceId or E164 number!");
} }
} }
public RecipientAddress(UUID uuid, String e164) { public RecipientAddress(Optional<ServiceId> serviceId, Optional<String> number) {
this(Optional.ofNullable(uuid), Optional.ofNullable(e164)); this(serviceId, Optional.empty(), number);
}
public RecipientAddress(ServiceId serviceId, String e164) {
this(Optional.ofNullable(serviceId), Optional.empty(), Optional.ofNullable(e164));
}
public RecipientAddress(ServiceId serviceId, PNI pni, String e164) {
this(Optional.ofNullable(serviceId), Optional.ofNullable(pni), Optional.ofNullable(e164));
} }
public RecipientAddress(SignalServiceAddress address) { public RecipientAddress(SignalServiceAddress address) {
this(Optional.of(address.getServiceId().uuid()), address.getNumber()); this(Optional.of(address.getServiceId()), Optional.empty(), address.getNumber());
} }
public RecipientAddress(UUID uuid) { public RecipientAddress(ServiceId serviceId) {
this(Optional.of(uuid), Optional.empty()); this(Optional.of(serviceId), Optional.empty());
} }
public ServiceId getServiceId() { public ServiceId getServiceId() {
return ServiceId.from(uuid.orElse(UNKNOWN_UUID)); return serviceId.orElse(ServiceId.UNKNOWN);
} }
public String getIdentifier() { public String getIdentifier() {
if (uuid.isPresent()) { if (serviceId.isPresent()) {
return uuid.get().toString(); return serviceId.get().toString();
} else if (number.isPresent()) { } else if (number.isPresent()) {
return number.get(); return number.get();
} else { } else {
@ -52,15 +66,25 @@ public record RecipientAddress(Optional<UUID> uuid, Optional<String> number) {
public String getLegacyIdentifier() { public String getLegacyIdentifier() {
if (number.isPresent()) { if (number.isPresent()) {
return number.get(); return number.get();
} else if (uuid.isPresent()) { } else if (serviceId.isPresent()) {
return uuid.get().toString(); return serviceId.get().toString();
} else { } else {
throw new AssertionError("Given the checks in the constructor, this should not be possible."); throw new AssertionError("Given the checks in the constructor, this should not be possible.");
} }
} }
public boolean matches(RecipientAddress other) { public boolean matches(RecipientAddress other) {
return (uuid.isPresent() && other.uuid.isPresent() && uuid.get().equals(other.uuid.get())) || ( return (serviceId.isPresent() && other.serviceId.isPresent() && serviceId.get().equals(other.serviceId.get()))
|| (
pni.isPresent() && other.serviceId.isPresent() && pni.get().equals(other.serviceId.get())
)
|| (
serviceId.isPresent() && other.pni.isPresent() && serviceId.get().equals(other.pni.get())
)
|| (
pni.isPresent() && other.pni.isPresent() && pni.get().equals(other.pni.get())
)
|| (
number.isPresent() && other.number.isPresent() && number.get().equals(other.number.get()) number.isPresent() && other.number.isPresent() && number.get().equals(other.number.get())
); );
} }
@ -68,4 +92,8 @@ public record RecipientAddress(Optional<UUID> uuid, Optional<String> number) {
public SignalServiceAddress toSignalServiceAddress() { public SignalServiceAddress toSignalServiceAddress() {
return new SignalServiceAddress(getServiceId(), number); return new SignalServiceAddress(getServiceId(), number);
} }
public org.asamk.signal.manager.api.RecipientAddress toApiRecipientAddress() {
return new org.asamk.signal.manager.api.RecipientAddress(serviceId().map(ServiceId::uuid), number());
}
} }

View file

@ -19,6 +19,6 @@ public interface RecipientResolver {
} }
default RecipientId resolveRecipient(ServiceId serviceId) { default RecipientId resolveRecipient(ServiceId serviceId) {
return resolveRecipient(new RecipientAddress(serviceId.uuid())); return resolveRecipient(new RecipientAddress(serviceId));
} }
} }

View file

@ -28,7 +28,6 @@ import java.util.Map;
import java.util.Objects; 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.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -163,13 +162,13 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException("Failed read from recipient store", e); throw new RuntimeException("Failed read from recipient store", e);
} }
if (byNumber.isEmpty() || byNumber.get().address().uuid().isEmpty()) { if (byNumber.isEmpty() || byNumber.get().address().serviceId().isEmpty()) {
final var aci = aciSupplier.get(); final var aci = aciSupplier.get();
if (aci == null) { if (aci == null) {
throw new UnregisteredRecipientException(new RecipientAddress(null, number)); throw new UnregisteredRecipientException(new org.asamk.signal.manager.api.RecipientAddress(null, number));
} }
return resolveRecipient(new RecipientAddress(aci.uuid(), number), false, false); return resolveRecipient(new RecipientAddress(aci, number), false, false);
} }
return byNumber.get().id(); return byNumber.get().id();
} }
@ -399,7 +398,12 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
for (final var recipient : recipients.values()) { for (final var recipient : recipients.values()) {
statement.setLong(1, recipient.getRecipientId().id()); statement.setLong(1, recipient.getRecipientId().id());
statement.setString(2, recipient.getAddress().number().orElse(null)); statement.setString(2, recipient.getAddress().number().orElse(null));
statement.setBytes(3, recipient.getAddress().uuid().map(UuidUtil::toByteArray).orElse(null)); statement.setBytes(3,
recipient.getAddress()
.serviceId()
.map(ServiceId::uuid)
.map(UuidUtil::toByteArray)
.orElse(null));
statement.executeUpdate(); statement.executeUpdate();
} }
} }
@ -576,22 +580,22 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
final var byNumber = address.number().isEmpty() final var byNumber = address.number().isEmpty()
? Optional.<RecipientWithAddress>empty() ? Optional.<RecipientWithAddress>empty()
: findByNumber(connection, address.number().get()); : findByNumber(connection, address.number().get());
final var byUuid = address.uuid().isEmpty() final var byUuid = address.serviceId().isEmpty()
? Optional.<RecipientWithAddress>empty() ? Optional.<RecipientWithAddress>empty()
: findByUuid(connection, address.uuid().get()); : findByServiceId(connection, address.serviceId().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.uuid().isEmpty() || address.number().isEmpty()) { if (isHighTrust || address.serviceId().isEmpty() || address.number().isEmpty()) {
return new Pair<>(addNewRecipient(connection, address), Optional.empty()); return new Pair<>(addNewRecipient(connection, address), Optional.empty());
} }
return new Pair<>(addNewRecipient(connection, new RecipientAddress(address.uuid().get())), return new Pair<>(addNewRecipient(connection, new RecipientAddress(address.serviceId().get())),
Optional.empty()); Optional.empty());
} }
if (!isHighTrust || address.uuid().isEmpty() || address.number().isEmpty() || byNumber.equals(byUuid)) { if (!isHighTrust || address.serviceId().isEmpty() || address.number().isEmpty() || byNumber.equals(byUuid)) {
return new Pair<>(byUuid.or(() -> byNumber).map(RecipientWithAddress::id).get(), Optional.empty()); return new Pair<>(byUuid.or(() -> byNumber).map(RecipientWithAddress::id).get(), Optional.empty());
} }
@ -604,14 +608,14 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
final var byNumberRecipient = byNumber.get(); final var byNumberRecipient = byNumber.get();
if (byUuid.isEmpty()) { if (byUuid.isEmpty()) {
if (byNumberRecipient.address().uuid().isPresent()) { if (byNumberRecipient.address().serviceId().isPresent()) {
logger.debug( logger.debug(
"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",
byNumberRecipient.id()); byNumberRecipient.id());
updateRecipientAddress(connection, updateRecipientAddress(connection,
byNumberRecipient.id(), byNumberRecipient.id(),
new RecipientAddress(byNumberRecipient.address().uuid().get())); new RecipientAddress(byNumberRecipient.address().serviceId().get()));
return new Pair<>(addNewRecipient(connection, address), Optional.empty()); return new Pair<>(addNewRecipient(connection, address), Optional.empty());
} }
@ -623,7 +627,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
final var byUuidRecipient = byUuid.get(); final var byUuidRecipient = byUuid.get();
if (byNumberRecipient.address().uuid().isPresent()) { if (byNumberRecipient.address().serviceId().isPresent()) {
logger.debug( logger.debug(
"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",
byNumberRecipient.id(), byNumberRecipient.id(),
@ -631,7 +635,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
updateRecipientAddress(connection, updateRecipientAddress(connection,
byNumberRecipient.id(), byNumberRecipient.id(),
new RecipientAddress(byNumberRecipient.address().uuid().get())); new RecipientAddress(byNumberRecipient.address().serviceId().get()));
updateRecipientAddress(connection, byUuidRecipient.id(), address); updateRecipientAddress(connection, byUuidRecipient.id(), address);
return new Pair<>(byUuidRecipient.id(), Optional.empty()); return new Pair<>(byUuidRecipient.id(), Optional.empty());
} }
@ -658,7 +662,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
).formatted(TABLE_RECIPIENT); ).formatted(TABLE_RECIPIENT);
try (final var statement = connection.prepareStatement(sql)) { try (final var statement = connection.prepareStatement(sql)) {
statement.setString(1, address.number().orElse(null)); statement.setString(1, address.number().orElse(null));
statement.setBytes(2, address.uuid().map(UuidUtil::toByteArray).orElse(null)); statement.setBytes(2, address.serviceId().map(ServiceId::uuid).map(UuidUtil::toByteArray).orElse(null));
statement.executeUpdate(); statement.executeUpdate();
final var generatedKeys = statement.getGeneratedKeys(); final var generatedKeys = statement.getGeneratedKeys();
if (generatedKeys.next()) { if (generatedKeys.next()) {
@ -697,7 +701,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
).formatted(TABLE_RECIPIENT); ).formatted(TABLE_RECIPIENT);
try (final var statement = connection.prepareStatement(sql)) { try (final var statement = connection.prepareStatement(sql)) {
statement.setString(1, address.number().orElse(null)); statement.setString(1, address.number().orElse(null));
statement.setBytes(2, address.uuid().map(UuidUtil::toByteArray).orElse(null)); statement.setBytes(2, address.serviceId().map(ServiceId::uuid).map(UuidUtil::toByteArray).orElse(null));
statement.setLong(3, recipientId.id()); statement.setLong(3, recipientId.id());
statement.executeUpdate(); statement.executeUpdate();
} }
@ -761,8 +765,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
} }
} }
private Optional<RecipientWithAddress> findByUuid( private Optional<RecipientWithAddress> findByServiceId(
final Connection connection, final UUID uuid final Connection connection, final ServiceId serviceId
) throws SQLException { ) throws SQLException {
final var sql = """ final var sql = """
SELECT r._id, r.number, r.uuid SELECT r._id, r.number, r.uuid
@ -770,7 +774,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
WHERE r.uuid = ? WHERE r.uuid = ?
""".formatted(TABLE_RECIPIENT); """.formatted(TABLE_RECIPIENT);
try (final var statement = connection.prepareStatement(sql)) { try (final var statement = connection.prepareStatement(sql)) {
statement.setBytes(1, UuidUtil.toByteArray(uuid)); statement.setBytes(1, UuidUtil.toByteArray(serviceId.uuid()));
return Utils.executeQueryForOptional(statement, this::getRecipientWithAddressFromResultSet); return Utils.executeQueryForOptional(statement, this::getRecipientWithAddressFromResultSet);
} }
} }
@ -835,9 +839,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
} }
private RecipientAddress getRecipientAddressFromResultSet(ResultSet resultSet) throws SQLException { private RecipientAddress getRecipientAddressFromResultSet(ResultSet resultSet) throws SQLException {
final var uuid = Optional.ofNullable(resultSet.getBytes("uuid")).map(UuidUtil::parseOrNull); final var serviceId = Optional.ofNullable(resultSet.getBytes("uuid")).map(ServiceId::parseOrNull);
final var number = Optional.ofNullable(resultSet.getString("number")); final var number = Optional.ofNullable(resultSet.getString("number"));
return new RecipientAddress(uuid, number); return new RecipientAddress(serviceId, Optional.empty(), number);
} }
private RecipientId getRecipientIdFromResultSet(ResultSet resultSet) throws SQLException { private RecipientId getRecipientIdFromResultSet(ResultSet resultSet) throws SQLException {

View file

@ -8,7 +8,6 @@ import org.signal.libsignal.protocol.InvalidMessageException;
import org.signal.libsignal.protocol.groups.state.SenderKeyRecord; import org.signal.libsignal.protocol.groups.state.SenderKeyRecord;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.ServiceId;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -38,13 +37,11 @@ public class LegacySenderKeyRecordStore {
final var senderKeys = parseFileNames(files, resolver).stream().map(key -> { final var senderKeys = parseFileNames(files, resolver).stream().map(key -> {
final var record = loadSenderKeyLocked(key, senderKeysPath); final var record = loadSenderKeyLocked(key, senderKeysPath);
final var uuid = addressResolver.resolveRecipientAddress(key.recipientId).uuid(); final var serviceId = addressResolver.resolveRecipientAddress(key.recipientId).serviceId();
if (record == null || uuid.isEmpty()) { if (record == null || serviceId.isEmpty()) {
return null; return null;
} }
return new Pair<>(new SenderKeyRecordStore.Key(ServiceId.from(uuid.get()), return new Pair<>(new SenderKeyRecordStore.Key(serviceId.get(), key.deviceId, key.distributionId), record);
key.deviceId,
key.distributionId), record);
}).filter(Objects::nonNull).toList(); }).filter(Objects::nonNull).toList();
senderKeyStore.addLegacySenderKeys(senderKeys); senderKeyStore.addLegacySenderKeys(senderKeys);

View file

@ -7,7 +7,6 @@ import org.asamk.signal.manager.storage.senderKeys.SenderKeySharedStore.SenderKe
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.DistributionId;
import org.whispersystems.signalservice.api.push.ServiceId;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -37,11 +36,11 @@ public class LegacySenderKeySharedStore {
if (recipientId == null) { if (recipientId == null) {
continue; continue;
} }
final var uuid = addressResolver.resolveRecipientAddress(recipientId).uuid(); final var serviceId = addressResolver.resolveRecipientAddress(recipientId).serviceId();
if (uuid.isEmpty()) { if (serviceId.isEmpty()) {
continue; continue;
} }
final var entry = new SenderKeySharedEntry(ServiceId.from(uuid.get()), senderKey.deviceId); final var entry = new SenderKeySharedEntry(serviceId.get(), senderKey.deviceId);
final var distributionId = DistributionId.from(senderKey.distributionId); final var distributionId = DistributionId.from(senderKey.distributionId);
var entries = sharedSenderKeys.get(distributionId); var entries = sharedSenderKeys.get(distributionId);
if (entries == null) { if (entries == null) {

View file

@ -8,7 +8,6 @@ import org.asamk.signal.manager.util.IOUtils;
import org.signal.libsignal.protocol.state.SessionRecord; import org.signal.libsignal.protocol.state.SessionRecord;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.ServiceId;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -34,11 +33,11 @@ public class LegacySessionStore {
final var keys = getKeysLocked(sessionsPath, resolver); final var keys = getKeysLocked(sessionsPath, resolver);
final var sessions = keys.stream().map(key -> { final var sessions = keys.stream().map(key -> {
final var record = loadSessionLocked(key, sessionsPath); final var record = loadSessionLocked(key, sessionsPath);
final var uuid = addressResolver.resolveRecipientAddress(key.recipientId).uuid(); final var serviceId = addressResolver.resolveRecipientAddress(key.recipientId).serviceId();
if (record == null || uuid.isEmpty()) { if (record == null || serviceId.isEmpty()) {
return null; return null;
} }
return new Pair<>(new SessionStore.Key(ServiceId.from(uuid.get()), key.deviceId()), record); return new Pair<>(new SessionStore.Key(serviceId.get(), key.deviceId()), record);
}).filter(Objects::nonNull).toList(); }).filter(Objects::nonNull).toList();
sessionStore.addLegacySessions(sessions); sessionStore.addLegacySessions(sessions);
deleteAllSessions(sessionsPath); deleteAllSessions(sessionsPath);

View file

@ -87,7 +87,7 @@ public class Utils {
version = 1; version = 1;
ownId = ownAddress.number().get().getBytes(); ownId = ownAddress.number().get().getBytes();
theirId = theirAddress.number().get().getBytes(); theirId = theirAddress.number().get().getBytes();
} else if (isUuidCapable && theirAddress.uuid().isPresent()) { } else if (isUuidCapable && theirAddress.serviceId().isPresent()) {
// Version 2: UUID user // Version 2: UUID user
version = 2; version = 2;
ownId = ownAddress.getServiceId().toByteArray(); ownId = ownAddress.getServiceId().toByteArray();

View file

@ -2,10 +2,10 @@ package org.asamk.signal;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.MessageEnvelope;
import org.asamk.signal.manager.api.RecipientAddress;
import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.api.UntrustedIdentityException;
import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.output.PlainTextWriter; import org.asamk.signal.output.PlainTextWriter;
import org.asamk.signal.util.DateUtils; import org.asamk.signal.util.DateUtils;
import org.asamk.signal.util.Hex; import org.asamk.signal.util.Hex;

View file

@ -7,7 +7,7 @@ import net.sourceforge.argparse4j.inf.Subparser;
import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.api.Group; import org.asamk.signal.manager.api.Group;
import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.api.RecipientAddress;
import org.asamk.signal.output.JsonWriter; import org.asamk.signal.output.JsonWriter;
import org.asamk.signal.output.OutputWriter; import org.asamk.signal.output.OutputWriter;
import org.asamk.signal.output.PlainTextWriter; import org.asamk.signal.output.PlainTextWriter;

View file

@ -15,6 +15,8 @@ import org.asamk.signal.manager.api.MessageEnvelope;
import org.asamk.signal.manager.api.NotPrimaryDeviceException; import org.asamk.signal.manager.api.NotPrimaryDeviceException;
import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.Pair;
import org.asamk.signal.manager.api.ReceiveConfig; import org.asamk.signal.manager.api.ReceiveConfig;
import org.asamk.signal.manager.api.Recipient;
import org.asamk.signal.manager.api.RecipientAddress;
import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendGroupMessageResults;
import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.manager.api.SendMessageResults;
@ -34,8 +36,6 @@ import org.asamk.signal.manager.groups.LastGroupAdminException;
import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Contact;
import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.Recipient;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.freedesktop.dbus.DBusMap; import org.freedesktop.dbus.DBusMap;
import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.DBusPath;
import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.connections.impl.DBusConnection;

View file

@ -3,8 +3,8 @@ package org.asamk.signal.dbus;
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.api.MessageEnvelope; import org.asamk.signal.manager.api.MessageEnvelope;
import org.asamk.signal.manager.api.RecipientAddress;
import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.types.Variant; import org.freedesktop.dbus.types.Variant;

View file

@ -11,6 +11,7 @@ import org.asamk.signal.manager.api.InvalidStickerException;
import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.Message;
import org.asamk.signal.manager.api.NotPrimaryDeviceException; import org.asamk.signal.manager.api.NotPrimaryDeviceException;
import org.asamk.signal.manager.api.PendingAdminApprovalException; import org.asamk.signal.manager.api.PendingAdminApprovalException;
import org.asamk.signal.manager.api.RecipientAddress;
import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.SendMessageResult; import org.asamk.signal.manager.api.SendMessageResult;
import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.manager.api.SendMessageResults;
@ -28,7 +29,6 @@ import org.asamk.signal.manager.groups.GroupPermission;
import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.LastGroupAdminException;
import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.util.SendMessageResultUtils; import org.asamk.signal.util.SendMessageResultUtils;
import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.DBusPath;
import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.connections.impl.DBusConnection;

View file

@ -4,9 +4,9 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.MessageEnvelope;
import org.asamk.signal.manager.api.RecipientAddress;
import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.api.UntrustedIdentityException;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import java.util.UUID; import java.util.UUID;

View file

@ -1,6 +1,6 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.api.RecipientAddress;
import java.util.UUID; import java.util.UUID;

View file

@ -3,8 +3,8 @@ package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.MessageEnvelope;
import org.asamk.signal.manager.api.RecipientAddress;
import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import java.util.List; import java.util.List;