mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 02:20:39 +00:00
Update libsignal-service-java
This commit is contained in:
parent
85c5caeaca
commit
8bcd8d87d2
52 changed files with 692 additions and 551 deletions
|
@ -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")
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 -> {
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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 user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message.");
|
"The user’s 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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() + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue