package org.asamk.signal;
import org.asamk.signal.manager.Manager;
+import org.asamk.signal.manager.UntrustedIdentityException;
+import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.groups.GroupUtils;
import org.asamk.signal.util.DateUtils;
-import org.asamk.signal.util.Util;
import org.slf4j.helpers.MessageFormatter;
-import org.whispersystems.libsignal.UntrustedIdentityException;
+import org.whispersystems.libsignal.protocol.DecryptionErrorMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import java.io.IOException;
+import java.util.ArrayList;
import java.util.Base64;
import java.util.stream.Collectors;
+import static org.asamk.signal.util.Util.getLegacyIdentifier;
+
public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
final Manager m;
+ final PlainTextWriter writer;
- public ReceiveMessageHandler(Manager m) {
+ public ReceiveMessageHandler(Manager m, final PlainTextWriter writer) {
this.m = m;
+ this.writer = writer;
}
@Override
public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) {
- try {
- printMessage(envelope, content, exception);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- private void printMessage(
- SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception
- ) throws IOException {
- PlainTextWriter writer = new PlainTextWriterImpl(System.out);
-
- if (envelope.hasSource()) {
+ if (envelope.hasSourceUuid()) {
var source = envelope.getSourceAddress();
writer.println("Envelope from: {} (device: {})", formatContact(source), envelope.getSourceDevice());
- if (source.getRelay().isPresent()) {
- writer.println("Relayed by: {}", source.getRelay().get());
- }
} else {
writer.println("Envelope from: unknown source");
}
var e = (UntrustedIdentityException) exception;
writer.println(
"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()));
writer.println(
- "Use 'signal-cli -u {0} listIdentities -n {1}', verify the key and run 'signal-cli -u {0} trust -v \"FINGER_PRINT\" {1}' to mark it as trusted",
- m.getUsername(),
- e.getName());
+ "Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted",
+ m.getSelfNumber(),
+ recipientName,
+ m.getSelfNumber(),
+ recipientName);
writer.println(
"If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification",
- m.getUsername(),
- e.getName());
+ m.getSelfNumber(),
+ recipientName);
} else {
writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName());
}
}
if (content == null) {
- writer.println("Failed to decrypt message.");
+ writer.println("No message content");
} else {
writer.println("Sender: {} (device: {})",
formatContact(content.getSender()),
DateUtils.formatTimestamp(content.getServerReceivedTimestamp()),
DateUtils.formatTimestamp(content.getServerDeliveredTimestamp()));
+ if (content.getSenderKeyDistributionMessage().isPresent()) {
+ final var message = content.getSenderKeyDistributionMessage().get();
+ writer.println("Received a sender key distribution message for distributionId {}",
+ message.getDistributionId());
+ }
+
if (content.getDataMessage().isPresent()) {
var message = content.getDataMessage().get();
printDataMessage(writer, message);
var typingMessage = content.getTypingMessage().get();
printTypingMessage(writer.indentedWriter(), typingMessage);
}
+ if (content.getDecryptionErrorMessage().isPresent()) {
+ writer.println("Received a decryption error message (resend request)");
+ var decryptionErrorMessage = content.getDecryptionErrorMessage().get();
+ printDecryptionErrorMessage(writer.indentedWriter(), decryptionErrorMessage);
+ }
}
} else {
writer.println("Unknown message received.");
private void printDataMessage(
PlainTextWriter writer, SignalServiceDataMessage message
- ) throws IOException {
+ ) {
writer.println("Message timestamp: {}", DateUtils.formatTimestamp(message.getTimestamp()));
if (message.isViewOnce()) {
writer.println("=VIEW ONCE=");
private void printTypingMessage(
final PlainTextWriter writer, final SignalServiceTypingMessage typingMessage
- ) throws IOException {
+ ) {
writer.println("Action: {}", typingMessage.getAction());
writer.println("Timestamp: {}", DateUtils.formatTimestamp(typingMessage.getTimestamp()));
if (typingMessage.getGroupId().isPresent()) {
}
}
+ private void printDecryptionErrorMessage(
+ final PlainTextWriter writer, final DecryptionErrorMessage decryptionErrorMessage
+ ) {
+ writer.println("Device id: {}", decryptionErrorMessage.getDeviceId());
+ writer.println("Timestamp: {}", DateUtils.formatTimestamp(decryptionErrorMessage.getTimestamp()));
+ writer.println("Ratchet key: {}",
+ decryptionErrorMessage.getRatchetKey().isPresent() ? "is present" : "not present");
+ }
+
private void printReceiptMessage(
final PlainTextWriter writer, final SignalServiceReceiptMessage receiptMessage
- ) throws IOException {
+ ) {
writer.println("When: {}", DateUtils.formatTimestamp(receiptMessage.getWhen()));
if (receiptMessage.isDeliveryReceipt()) {
writer.println("Is delivery receipt");
private void printCallMessage(
final PlainTextWriter writer, final SignalServiceCallMessage callMessage
- ) throws IOException {
+ ) {
if (callMessage.getDestinationDeviceId().isPresent()) {
final var deviceId = callMessage.getDestinationDeviceId().get();
writer.println("Destination device id: {}", deviceId);
}
+ if (callMessage.getGroupId().isPresent()) {
+ final var groupId = GroupId.unknownVersion(callMessage.getGroupId().get());
+ writer.println("Destination group id: {}", groupId);
+ }
+ if (callMessage.getTimestamp().isPresent()) {
+ writer.println("Timestamp: {}", DateUtils.formatTimestamp(callMessage.getTimestamp().get()));
+ }
if (callMessage.getAnswerMessage().isPresent()) {
var answerMessage = callMessage.getAnswerMessage().get();
writer.println("Answer message: {}, sdp: {})", answerMessage.getId(), answerMessage.getSdp());
}
if (callMessage.getOpaqueMessage().isPresent()) {
final var opaqueMessage = callMessage.getOpaqueMessage().get();
- writer.println("Opaque message: size {}", opaqueMessage.getOpaque().length);
+ writer.println("Opaque message: size {}, urgency: {}",
+ opaqueMessage.getOpaque().length,
+ opaqueMessage.getUrgency().name());
}
}
private void printSyncMessage(
final PlainTextWriter writer, final SignalServiceSyncMessage syncMessage
- ) throws IOException {
+ ) {
if (syncMessage.getContacts().isPresent()) {
final var contactsMessage = syncMessage.getContacts().get();
var type = contactsMessage.isComplete() ? "complete" : "partial";
DateUtils.formatTimestamp(rm.getTimestamp()));
}
}
+ if (syncMessage.getViewed().isPresent()) {
+ writer.println("Received sync viewed messages list");
+ for (var vm : syncMessage.getViewed().get()) {
+ writer.println("- From: {} Message timestamp: {}",
+ formatContact(vm.getSender()),
+ DateUtils.formatTimestamp(vm.getTimestamp()));
+ }
+ }
if (syncMessage.getRequest().isPresent()) {
String type;
if (syncMessage.getRequest().get().isContactsRequest()) {
writer.println("Blocked numbers:");
final var blockedList = syncMessage.getBlockedList().get();
for (var address : blockedList.getAddresses()) {
- writer.println("- {}", address.getLegacyIdentifier());
+ writer.println("- {}", getLegacyIdentifier(address));
}
}
if (syncMessage.getVerified().isPresent()) {
writer.println("Received sync message with verified identities:");
final var verifiedMessage = syncMessage.getVerified().get();
writer.println("- {}: {}", formatContact(verifiedMessage.getDestination()), verifiedMessage.getVerified());
- var safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(verifiedMessage.getDestination(),
- verifiedMessage.getIdentityKey()));
- writer.indentedWriter().println(safetyNumber);
}
if (syncMessage.getConfiguration().isPresent()) {
writer.println("Received sync message with configuration:");
private void printPreview(
final PlainTextWriter writer, final SignalServiceDataMessage.Preview preview
- ) throws IOException {
+ ) {
writer.println("Title: {}", preview.getTitle());
writer.println("Description: {}", preview.getDescription());
writer.println("Date: {}", DateUtils.formatTimestamp(preview.getDate()));
private void printSticker(
final PlainTextWriter writer, final SignalServiceDataMessage.Sticker sticker
- ) throws IOException {
+ ) {
writer.println("Pack id: {}", Base64.getEncoder().encodeToString(sticker.getPackId()));
writer.println("Pack key: {}", Base64.getEncoder().encodeToString(sticker.getPackKey()));
writer.println("Sticker id: {}", sticker.getStickerId());
private void printReaction(
final PlainTextWriter writer, final SignalServiceDataMessage.Reaction reaction
- ) throws IOException {
+ ) {
writer.println("Emoji: {}", reaction.getEmoji());
- writer.println("Target author: {}", formatContact(m.resolveSignalServiceAddress(reaction.getTargetAuthor())));
+ writer.println("Target author: {}", formatContact(reaction.getTargetAuthor()));
writer.println("Target timestamp: {}", DateUtils.formatTimestamp(reaction.getTargetSentTimestamp()));
writer.println("Is remove: {}", reaction.isRemove());
}
private void printQuote(
final PlainTextWriter writer, final SignalServiceDataMessage.Quote quote
- ) throws IOException {
+ ) {
writer.println("Id: {}", quote.getId());
- writer.println("Author: {}", m.resolveSignalServiceAddress(quote.getAuthor()).getLegacyIdentifier());
+ writer.println("Author: {}", formatContact(quote.getAuthor()));
writer.println("Text: {}", quote.getText());
if (quote.getMentions() != null && quote.getMentions().size() > 0) {
writer.println("Mentions:");
}
}
- private void printSharedContact(final PlainTextWriter writer, final SharedContact contact) throws IOException {
+ private void printSharedContact(final PlainTextWriter writer, final SharedContact contact) {
writer.println("Name:");
var name = contact.getName();
writer.indent(w -> {
private void printGroupContext(
final PlainTextWriter writer, final SignalServiceGroupContext groupContext
- ) throws IOException {
+ ) {
final var groupId = GroupUtils.getGroupId(groupContext);
if (groupContext.getGroupV1().isPresent()) {
var groupInfo = groupContext.getGroupV1().get();
}
}
- private void printGroupInfo(final PlainTextWriter writer, final GroupId groupId) throws IOException {
+ private void printGroupInfo(final PlainTextWriter writer, final GroupId groupId) {
writer.println("Id: {}", groupId.toBase64());
var group = m.getGroup(groupId);
private void printMention(
PlainTextWriter writer, SignalServiceDataMessage.Mention mention
- ) throws IOException {
- final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null));
+ ) {
+ final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid()));
writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength());
}
pointer.getPreview().isPresent() ? " (Preview is available: "
+ pointer.getPreview().get().length
+ " bytes)" : "");
- writer.println("Voice note: {}", pointer.getVoiceNote() ? "yes" : "no");
- writer.println("Borderless: {}", pointer.isBorderless() ? "yes" : "no");
+ final var flags = new ArrayList<String>();
+ if (pointer.getVoiceNote()) {
+ flags.add("voice note");
+ }
+ if (pointer.isBorderless()) {
+ flags.add("borderless");
+ }
+ if (pointer.isGif()) {
+ flags.add("video gif");
+ }
+ if (flags.size() > 0) {
+ writer.println("Flags: {}", String.join(", ", flags));
+ }
if (pointer.getWidth() > 0 || pointer.getHeight() > 0) {
writer.println("Dimensions: {}x{}", pointer.getWidth(), pointer.getHeight());
}
}
private String formatContact(SignalServiceAddress address) {
- final var number = address.getLegacyIdentifier();
- var name = m.getContactOrProfileName(number);
- if (name == null) {
+ address = m.resolveSignalServiceAddress(address);
+ final var number = getLegacyIdentifier(address);
+ final var name = m.getContactOrProfileName(RecipientIdentifier.Single.fromAddress(address));
+ if (name == null || name.isEmpty()) {
return number;
} else {
return MessageFormatter.arrayFormat("“{}” {}", new Object[]{name, number}).getMessage();