Extend json output with number and uuid fields

This commit is contained in:
AsamK 2021-08-22 11:28:09 +02:00
parent 73e137137d
commit 5bbfd32598
9 changed files with 148 additions and 32 deletions

View file

@ -327,16 +327,32 @@ public class Manager implements Closeable {
* This is used for checking a set of phone numbers for registration on Signal * This is used for checking a set of phone numbers for registration on Signal
* *
* @param numbers The set of phone number in question * @param numbers The set of phone number in question
* @return A map of numbers to booleans. True if registered, false otherwise. Should never be null * @return A map of numbers to canonicalized number and uuid. If a number is not registered the uuid is null.
* @throws IOException if its unable to get the contacts to check if they're registered * @throws IOException if its unable to get the contacts to check if they're registered
*/ */
public Map<String, Boolean> 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 -> {
try {
return canonicalizePhoneNumber(n);
} catch (InvalidNumberException e) {
return "";
}
}));
// Note "contactDetails" has no optionals. It only gives us info on users who are registered // Note "contactDetails" has no optionals. It only gives us info on users who are registered
var contactDetails = getRegisteredUsers(numbers); var contactDetails = getRegisteredUsers(canonicalizedNumbers.values()
.stream()
.filter(s -> !s.isEmpty())
.collect(Collectors.toSet()));
var registeredUsers = contactDetails.keySet(); // Store numbers as recipients so we have the number/uuid association
contactDetails.forEach((number, uuid) -> resolveRecipientTrusted(new SignalServiceAddress(uuid, number)));
return numbers.stream().collect(Collectors.toMap(x -> x, registeredUsers::contains)); return numbers.stream().collect(Collectors.toMap(n -> n, n -> {
final var number = canonicalizedNumbers.get(n);
final var uuid = contactDetails.get(number);
return new Pair<>(number.isEmpty() ? null : number, uuid);
}));
} }
public void updateAccountAttributes() throws IOException { public void updateAccountAttributes() throws IOException {
@ -2724,14 +2740,16 @@ public class Manager implements Closeable {
return account.getRecipientStore().resolveServiceAddress(recipientId); return account.getRecipientStore().resolveServiceAddress(recipientId);
} }
public RecipientId canonicalizeAndResolveRecipient(String identifier) throws InvalidNumberException { private RecipientId canonicalizeAndResolveRecipient(String identifier) throws InvalidNumberException {
var canonicalizedNumber = UuidUtil.isUuid(identifier) var canonicalizedNumber = UuidUtil.isUuid(identifier) ? identifier : canonicalizePhoneNumber(identifier);
? identifier
: PhoneNumberFormatter.formatNumber(identifier, account.getUsername());
return resolveRecipient(canonicalizedNumber); return resolveRecipient(canonicalizedNumber);
} }
private String canonicalizePhoneNumber(final String number) throws InvalidNumberException {
return PhoneNumberFormatter.formatNumber(number, account.getUsername());
}
private RecipientId resolveRecipient(final String identifier) { private RecipientId resolveRecipient(final String identifier) {
var address = Utils.getSignalServiceAddressFromIdentifier(identifier); var address = Utils.getSignalServiceAddressFromIdentifier(identifier);

View file

@ -11,10 +11,12 @@ import org.asamk.signal.commands.exceptions.IOErrorException;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.util.Pair;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class GetUserStatusCommand implements JsonRpcLocalCommand { public class GetUserStatusCommand implements JsonRpcLocalCommand {
@ -37,7 +39,7 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand {
final Namespace ns, final Manager m, final OutputWriter outputWriter final Namespace ns, final Manager m, final OutputWriter outputWriter
) throws CommandException { ) throws CommandException {
// Get a map of registration statuses // Get a map of registration statuses
Map<String, Boolean> registered; Map<String, Pair<String, UUID>> registered;
try { try {
registered = m.areUsersRegistered(new HashSet<>(ns.getList("number"))); registered = m.areUsersRegistered(new HashSet<>(ns.getList("number")));
} catch (IOException e) { } catch (IOException e) {
@ -49,10 +51,11 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand {
if (outputWriter instanceof JsonWriter) { if (outputWriter instanceof JsonWriter) {
final var jsonWriter = (JsonWriter) outputWriter; final var jsonWriter = (JsonWriter) outputWriter;
var jsonUserStatuses = registered.entrySet() var jsonUserStatuses = registered.entrySet().stream().map(entry -> {
.stream() final var number = entry.getValue().first();
.map(entry -> new JsonUserStatus(entry.getKey(), entry.getValue())) final var uuid = entry.getValue().second();
.collect(Collectors.toList()); return new JsonUserStatus(entry.getKey(), number, uuid == null ? null : uuid.toString(), uuid != null);
}).collect(Collectors.toList());
jsonWriter.write(jsonUserStatuses); jsonWriter.write(jsonUserStatuses);
} else { } else {
@ -66,12 +69,18 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand {
private static final class JsonUserStatus { private static final class JsonUserStatus {
public String name; public final String name;
public boolean isRegistered; public final String number;
public JsonUserStatus(String name, boolean isRegistered) { public final String uuid;
public final boolean isRegistered;
public JsonUserStatus(String name, String number, String uuid, boolean isRegistered) {
this.name = name; this.name = name;
this.number = number;
this.uuid = uuid;
this.isRegistered = isRegistered; this.isRegistered = isRegistered;
} }
} }

View file

@ -6,13 +6,22 @@ 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 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;
public class JsonMention { public class JsonMention {
@JsonProperty @JsonProperty
@Deprecated
final String name; final String name;
@JsonProperty
final String number;
@JsonProperty
final String uuid;
@JsonProperty @JsonProperty
final int start; final int start;
@ -20,8 +29,10 @@ public class JsonMention {
final int length; final int length;
JsonMention(SignalServiceDataMessage.Mention mention, Manager m) { JsonMention(SignalServiceDataMessage.Mention mention, Manager m) {
this.name = getLegacyIdentifier(m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null));
null))); this.name = getLegacyIdentifier(address);
this.number = address.getNumber().orNull();
this.uuid = address.getUuid().transform(UUID::toString).orNull();
this.start = mention.getStart(); this.start = mention.getStart();
this.length = mention.getLength(); this.length = mention.getLength();
} }

View file

@ -10,14 +10,22 @@ 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;
public class JsonMessageEnvelope { public class JsonMessageEnvelope {
@JsonProperty @JsonProperty
@Deprecated
final String source; final String source;
@JsonProperty
final String sourceNumber;
@JsonProperty
final String sourceUuid;
@JsonProperty @JsonProperty
final String sourceName; final String sourceName;
@ -55,14 +63,21 @@ public class JsonMessageEnvelope {
if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { if (!envelope.isUnidentifiedSender() && envelope.hasSource()) {
var source = envelope.getSourceAddress(); var source = envelope.getSourceAddress();
this.source = getLegacyIdentifier(source); this.source = getLegacyIdentifier(source);
this.sourceNumber = source.getNumber().orNull();
this.sourceUuid = source.getUuid().transform(UUID::toString).orNull();
this.sourceDevice = envelope.getSourceDevice(); this.sourceDevice = envelope.getSourceDevice();
this.relay = source.getRelay().orNull(); this.relay = source.getRelay().orNull();
} else if (envelope.isUnidentifiedSender() && content != null) { } else if (envelope.isUnidentifiedSender() && content != null) {
this.source = getLegacyIdentifier(content.getSender()); final var source = content.getSender();
this.source = getLegacyIdentifier(source);
this.sourceNumber = source.getNumber().orNull();
this.sourceUuid = source.getUuid().transform(UUID::toString).orNull();
this.sourceDevice = content.getSenderDevice(); this.sourceDevice = content.getSenderDevice();
this.relay = null; this.relay = null;
} else { } else {
this.source = null; this.source = null;
this.sourceNumber = null;
this.sourceUuid = null;
this.sourceDevice = null; this.sourceDevice = null;
this.relay = null; this.relay = null;
} }
@ -98,6 +113,8 @@ public class JsonMessageEnvelope {
public JsonMessageEnvelope(Signal.MessageReceived messageReceived) { public JsonMessageEnvelope(Signal.MessageReceived messageReceived) {
source = messageReceived.getSender(); source = messageReceived.getSender();
sourceNumber = null;
sourceUuid = null;
sourceName = null; sourceName = null;
sourceDevice = null; sourceDevice = null;
relay = null; relay = null;
@ -111,6 +128,8 @@ public class JsonMessageEnvelope {
public JsonMessageEnvelope(Signal.ReceiptReceived receiptReceived) { public JsonMessageEnvelope(Signal.ReceiptReceived receiptReceived) {
source = receiptReceived.getSender(); source = receiptReceived.getSender();
sourceNumber = null;
sourceUuid = null;
sourceName = null; sourceName = null;
sourceDevice = null; sourceDevice = null;
relay = null; relay = null;
@ -124,6 +143,8 @@ public class JsonMessageEnvelope {
public JsonMessageEnvelope(Signal.SyncMessageReceived messageReceived) { public JsonMessageEnvelope(Signal.SyncMessageReceived messageReceived) {
source = messageReceived.getSource(); source = messageReceived.getSource();
sourceNumber = null;
sourceUuid = null;
sourceName = null; sourceName = null;
sourceDevice = null; sourceDevice = null;
relay = null; relay = null;

View file

@ -8,6 +8,7 @@ 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;
@ -18,8 +19,15 @@ public class JsonQuote {
final long id; final long id;
@JsonProperty @JsonProperty
@Deprecated
final String author; final String author;
@JsonProperty
final String authorNumber;
@JsonProperty
final String authorUuid;
@JsonProperty @JsonProperty
final String text; final String text;
@ -32,7 +40,10 @@ public class JsonQuote {
JsonQuote(SignalServiceDataMessage.Quote quote, Manager m) { JsonQuote(SignalServiceDataMessage.Quote quote, Manager m) {
this.id = quote.getId(); this.id = quote.getId();
this.author = getLegacyIdentifier(m.resolveSignalServiceAddress(quote.getAuthor())); final var address = m.resolveSignalServiceAddress(quote.getAuthor());
this.author = getLegacyIdentifier(address);
this.authorNumber = address.getNumber().orNull();
this.authorUuid = address.getUuid().transform(UUID::toString).orNull();
this.text = quote.getText(); this.text = quote.getText();
if (quote.getMentions() != null && quote.getMentions().size() > 0) { if (quote.getMentions() != null && quote.getMentions().size() > 0) {

View file

@ -5,6 +5,8 @@ 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 {
@ -13,8 +15,15 @@ public class JsonReaction {
final String emoji; final String emoji;
@JsonProperty @JsonProperty
@Deprecated
final String targetAuthor; final String targetAuthor;
@JsonProperty
final String targetAuthorNumber;
@JsonProperty
final String targetAuthorUuid;
@JsonProperty @JsonProperty
final long targetSentTimestamp; final long targetSentTimestamp;
@ -23,7 +32,10 @@ public class JsonReaction {
JsonReaction(Reaction reaction, Manager m) { JsonReaction(Reaction reaction, Manager m) {
this.emoji = reaction.getEmoji(); this.emoji = reaction.getEmoji();
this.targetAuthor = getLegacyIdentifier(m.resolveSignalServiceAddress(reaction.getTargetAuthor())); final var address = m.resolveSignalServiceAddress(reaction.getTargetAuthor());
this.targetAuthor = getLegacyIdentifier(address);
this.targetAuthorNumber = address.getNumber().orNull();
this.targetAuthorUuid = address.getUuid().transform(UUID::toString).orNull();
this.targetSentTimestamp = reaction.getTargetSentTimestamp(); this.targetSentTimestamp = reaction.getTargetSentTimestamp();
this.isRemove = reaction.isRemove(); this.isRemove = reaction.isRemove();
} }

View file

@ -4,22 +4,43 @@ 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.util.Util;
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;
class JsonSyncDataMessage extends JsonDataMessage { class JsonSyncDataMessage extends JsonDataMessage {
@JsonProperty @JsonProperty
@Deprecated
final String destination; final String destination;
@JsonProperty
final String destinationNumber;
@JsonProperty
final String destinationUuid;
JsonSyncDataMessage(SentTranscriptMessage transcriptMessage, Manager m) { JsonSyncDataMessage(SentTranscriptMessage transcriptMessage, Manager m) {
super(transcriptMessage.getMessage(), m); super(transcriptMessage.getMessage(), m);
this.destination = transcriptMessage.getDestination().transform(Util::getLegacyIdentifier).orNull(); if (transcriptMessage.getDestination().isPresent()) {
final var address = transcriptMessage.getDestination().get();
this.destination = getLegacyIdentifier(address);
this.destinationNumber = address.getNumber().orNull();
this.destinationUuid = address.getUuid().transform(UUID::toString).orNull();
} else {
this.destination = null;
this.destinationNumber = null;
this.destinationUuid = null;
}
} }
JsonSyncDataMessage(Signal.SyncMessageReceived messageReceived) { JsonSyncDataMessage(Signal.SyncMessageReceived messageReceived) {
super(messageReceived); super(messageReceived);
destination = messageReceived.getDestination(); this.destination = messageReceived.getDestination();
this.destinationNumber = null;
this.destinationUuid = null;
} }
} }

View file

@ -12,8 +12,6 @@ import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
enum JsonSyncMessageType { enum JsonSyncMessageType {
CONTACTS_SYNC, CONTACTS_SYNC,
GROUPS_SYNC, GROUPS_SYNC,
@ -68,8 +66,7 @@ class JsonSyncMessage {
this.readMessages = syncMessage.getRead() this.readMessages = syncMessage.getRead()
.get() .get()
.stream() .stream()
.map(message -> new JsonSyncReadMessage(getLegacyIdentifier(message.getSender()), .map(JsonSyncReadMessage::new)
message.getTimestamp()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} else { } else {
this.readMessages = null; this.readMessages = null;

View file

@ -2,16 +2,32 @@ package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
import java.util.UUID;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
class JsonSyncReadMessage { class JsonSyncReadMessage {
@JsonProperty @JsonProperty
@Deprecated
final String sender; final String sender;
@JsonProperty
final String senderNumber;
@JsonProperty
final String senderUuid;
@JsonProperty @JsonProperty
final long timestamp; final long timestamp;
public JsonSyncReadMessage(final String sender, final long timestamp) { public JsonSyncReadMessage(final ReadMessage readMessage) {
this.sender = sender; final var sender = readMessage.getSender();
this.timestamp = timestamp; this.sender = getLegacyIdentifier(sender);
this.senderNumber = sender.getNumber().orNull();
this.senderUuid = sender.getUuid().transform(UUID::toString).orNull();
this.timestamp = readMessage.getTimestamp();
} }
} }