* This is used for checking a set of phone numbers for registration on Signal
*
* @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
*/
- 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
- 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 {
return account.getRecipientStore().resolveServiceAddress(recipientId);
}
- public RecipientId canonicalizeAndResolveRecipient(String identifier) throws InvalidNumberException {
- var canonicalizedNumber = UuidUtil.isUuid(identifier)
- ? identifier
- : PhoneNumberFormatter.formatNumber(identifier, account.getUsername());
+ private RecipientId canonicalizeAndResolveRecipient(String identifier) throws InvalidNumberException {
+ var canonicalizedNumber = UuidUtil.isUuid(identifier) ? identifier : canonicalizePhoneNumber(identifier);
return resolveRecipient(canonicalizedNumber);
}
+ private String canonicalizePhoneNumber(final String number) throws InvalidNumberException {
+ return PhoneNumberFormatter.formatNumber(number, account.getUsername());
+ }
+
private RecipientId resolveRecipient(final String identifier) {
var address = Utils.getSignalServiceAddressFromIdentifier(identifier);
import org.asamk.signal.manager.Manager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.whispersystems.libsignal.util.Pair;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
+import java.util.UUID;
import java.util.stream.Collectors;
public class GetUserStatusCommand implements JsonRpcLocalCommand {
final Namespace ns, final Manager m, final OutputWriter outputWriter
) throws CommandException {
// Get a map of registration statuses
- Map<String, Boolean> registered;
+ Map<String, Pair<String, UUID>> registered;
try {
registered = m.areUsersRegistered(new HashSet<>(ns.getList("number")));
} catch (IOException e) {
if (outputWriter instanceof JsonWriter) {
final var jsonWriter = (JsonWriter) outputWriter;
- var jsonUserStatuses = registered.entrySet()
- .stream()
- .map(entry -> new JsonUserStatus(entry.getKey(), entry.getValue()))
- .collect(Collectors.toList());
+ var jsonUserStatuses = registered.entrySet().stream().map(entry -> {
+ final var number = entry.getValue().first();
+ final var uuid = entry.getValue().second();
+ return new JsonUserStatus(entry.getKey(), number, uuid == null ? null : uuid.toString(), uuid != null);
+ }).collect(Collectors.toList());
jsonWriter.write(jsonUserStatuses);
} else {
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.number = number;
+ this.uuid = uuid;
this.isRegistered = isRegistered;
}
}
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;
public class JsonMention {
@JsonProperty
+ @Deprecated
final String name;
+ @JsonProperty
+ final String number;
+
+ @JsonProperty
+ final String uuid;
+
@JsonProperty
final int start;
final int length;
JsonMention(SignalServiceDataMessage.Mention mention, Manager m) {
- this.name = getLegacyIdentifier(m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(),
- null)));
+ final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null));
+ this.name = getLegacyIdentifier(address);
+ this.number = address.getNumber().orNull();
+ this.uuid = address.getUuid().transform(UUID::toString).orNull();
this.start = mention.getStart();
this.length = mention.getLength();
}
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.util.List;
+import java.util.UUID;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class JsonMessageEnvelope {
@JsonProperty
+ @Deprecated
final String source;
+ @JsonProperty
+ final String sourceNumber;
+
+ @JsonProperty
+ final String sourceUuid;
+
@JsonProperty
final String sourceName;
if (!envelope.isUnidentifiedSender() && envelope.hasSource()) {
var source = envelope.getSourceAddress();
this.source = getLegacyIdentifier(source);
+ this.sourceNumber = source.getNumber().orNull();
+ this.sourceUuid = source.getUuid().transform(UUID::toString).orNull();
this.sourceDevice = envelope.getSourceDevice();
this.relay = source.getRelay().orNull();
} 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.relay = null;
} else {
this.source = null;
+ this.sourceNumber = null;
+ this.sourceUuid = null;
this.sourceDevice = null;
this.relay = null;
}
public JsonMessageEnvelope(Signal.MessageReceived messageReceived) {
source = messageReceived.getSender();
+ sourceNumber = null;
+ sourceUuid = null;
sourceName = null;
sourceDevice = null;
relay = null;
public JsonMessageEnvelope(Signal.ReceiptReceived receiptReceived) {
source = receiptReceived.getSender();
+ sourceNumber = null;
+ sourceUuid = null;
sourceName = null;
sourceDevice = null;
relay = null;
public JsonMessageEnvelope(Signal.SyncMessageReceived messageReceived) {
source = messageReceived.getSource();
+ sourceNumber = null;
+ sourceUuid = null;
sourceName = null;
sourceDevice = null;
relay = null;
import java.util.ArrayList;
import java.util.List;
+import java.util.UUID;
import java.util.stream.Collectors;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
final long id;
@JsonProperty
+ @Deprecated
final String author;
+ @JsonProperty
+ final String authorNumber;
+
+ @JsonProperty
+ final String authorUuid;
+
@JsonProperty
final String text;
JsonQuote(SignalServiceDataMessage.Quote quote, Manager m) {
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();
if (quote.getMentions() != null && quote.getMentions().size() > 0) {
import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction;
+import java.util.UUID;
+
import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class JsonReaction {
final String emoji;
@JsonProperty
+ @Deprecated
final String targetAuthor;
+ @JsonProperty
+ final String targetAuthorNumber;
+
+ @JsonProperty
+ final String targetAuthorUuid;
+
@JsonProperty
final long targetSentTimestamp;
JsonReaction(Reaction reaction, Manager m) {
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.isRemove = reaction.isRemove();
}
import org.asamk.Signal;
import org.asamk.signal.manager.Manager;
-import org.asamk.signal.util.Util;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
+import java.util.UUID;
+
+import static org.asamk.signal.util.Util.getLegacyIdentifier;
+
class JsonSyncDataMessage extends JsonDataMessage {
@JsonProperty
+ @Deprecated
final String destination;
+ @JsonProperty
+ final String destinationNumber;
+
+ @JsonProperty
+ final String destinationUuid;
+
JsonSyncDataMessage(SentTranscriptMessage transcriptMessage, Manager 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) {
super(messageReceived);
- destination = messageReceived.getDestination();
+ this.destination = messageReceived.getDestination();
+ this.destinationNumber = null;
+ this.destinationUuid = null;
}
}
import java.util.List;
import java.util.stream.Collectors;
-import static org.asamk.signal.util.Util.getLegacyIdentifier;
-
enum JsonSyncMessageType {
CONTACTS_SYNC,
GROUPS_SYNC,
this.readMessages = syncMessage.getRead()
.get()
.stream()
- .map(message -> new JsonSyncReadMessage(getLegacyIdentifier(message.getSender()),
- message.getTimestamp()))
+ .map(JsonSyncReadMessage::new)
.collect(Collectors.toList());
} else {
this.readMessages = null;
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 {
@JsonProperty
+ @Deprecated
final String sender;
+ @JsonProperty
+ final String senderNumber;
+
+ @JsonProperty
+ final String senderUuid;
+
@JsonProperty
final long timestamp;
- public JsonSyncReadMessage(final String sender, final long timestamp) {
- this.sender = sender;
- this.timestamp = timestamp;
+ public JsonSyncReadMessage(final ReadMessage readMessage) {
+ final var sender = readMessage.getSender();
+ this.sender = getLegacyIdentifier(sender);
+ this.senderNumber = sender.getNumber().orNull();
+ this.senderUuid = sender.getUuid().transform(UUID::toString).orNull();
+ this.timestamp = readMessage.getTimestamp();
}
}