From: Atomic-Bean <75401809+Atomic-Bean@users.noreply.github.com> Date: Fri, 19 Feb 2021 17:03:15 +0000 (+1030) Subject: Output "SharedContacts" field from a SignalDataMessage (#529) X-Git-Tag: v0.8.1~16 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/237abe431bb77436ef7a23d32339804fff592cc7?ds=sidebyside Output "SharedContacts" field from a SignalDataMessage (#529) * Initial version of SharedContacts from data message. Need to change location of avatar downloaded and fix plain text mode * Made empty strings for json null and fixed plaintext output * Removed old comments, simplified if-statement and added a 'leadingSpaces' field to the print attachments/mentions functions * Added AsamK's changes --- diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 9661b12c..80ff4e71 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -122,6 +122,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptM import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; +import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -1553,9 +1554,18 @@ public class Manager implements Closeable { } } } - if (message.getAttachments().isPresent() && !ignoreAttachments) { - for (SignalServiceAttachment attachment : message.getAttachments().get()) { - downloadAttachment(attachment); + if (!ignoreAttachments) { + if (message.getAttachments().isPresent()) { + for (SignalServiceAttachment attachment : message.getAttachments().get()) { + downloadAttachment(attachment); + } + } + if (message.getSharedContacts().isPresent()) { + for (SharedContact contact : message.getSharedContacts().get()) { + if (contact.getAvatar().isPresent()) { + downloadAttachment(contact.getAvatar().get().getAttachment()); + } + } } } if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) { diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 07dbfd1d..9ab752cf 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -117,11 +117,11 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } else { System.out.println("Received sync contacts"); } - printAttachment(contactsMessage.getContactsStream()); + printAttachment(contactsMessage.getContactsStream(), 0); } if (syncMessage.getGroups().isPresent()) { System.out.println("Received sync groups"); - printAttachment(syncMessage.getGroups().get()); + printAttachment(syncMessage.getGroups().get(), 0); } if (syncMessage.getRead().isPresent()) { System.out.println("Received sync read messages list"); @@ -393,7 +393,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } if (groupInfo.getAvatar().isPresent()) { System.out.println(" Avatar:"); - printAttachment(groupInfo.getAvatar().get()); + printAttachment(groupInfo.getAvatar().get(), 2); } } else if (groupContext.getGroupV2().isPresent()) { final SignalServiceGroupV2 groupInfo = groupContext.getGroupV2().get(); @@ -421,7 +421,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { System.out.println(" - Title: " + preview.getTitle()); System.out.println(" - Url: " + preview.getUrl()); if (preview.getImage().isPresent()) { - printAttachment(preview.getImage().get()); + printAttachment(preview.getImage().get(), 1); } } } @@ -429,8 +429,106 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { final List sharedContacts = message.getSharedContacts().get(); System.out.println("Contacts:"); for (SharedContact contact : sharedContacts) { - System.out.println(" - Name: " + contact.getName()); - // TODO show or store rest of the contact info + System.out.println(" - Name:"); + SharedContact.Name name = contact.getName(); + if (name.getDisplay().isPresent() && !name.getDisplay().get().isBlank()) { + System.out.println(" - Display name: " + name.getDisplay().get()); + } + if (name.getGiven().isPresent() && !name.getGiven().get().isBlank()) { + System.out.println(" - First name: " + name.getGiven().get()); + } + if (name.getMiddle().isPresent() && !name.getMiddle().get().isBlank()) { + System.out.println(" - Middle name: " + name.getMiddle().get()); + } + if (name.getFamily().isPresent() && !name.getFamily().get().isBlank()) { + System.out.println(" - Family name: " + name.getFamily().get()); + } + if (name.getPrefix().isPresent() && !name.getPrefix().get().isBlank()) { + System.out.println(" - Prefix name: " + name.getPrefix().get()); + } + if (name.getSuffix().isPresent() && !name.getSuffix().get().isBlank()) { + System.out.println(" - Suffix name: " + name.getSuffix().get()); + } + + if (contact.getAvatar().isPresent()) { + SharedContact.Avatar avatar = contact.getAvatar().get(); + System.out.println(" - Avatar:"); + printAttachment(avatar.getAttachment(), 3); + if (avatar.isProfile()) { + System.out.println(" - Is profile"); + } else { + System.out.println(" - Is not a profile"); + } + } + + if (contact.getPhone().isPresent()) { + System.out.println(" - Phone details:"); + for (SharedContact.Phone phone : contact.getPhone().get()) { + System.out.println(" - Phone:"); + if (phone.getValue() != null) { + System.out.println(" - Number: " + phone.getValue()); + } + if (phone.getType() != null) { + System.out.println(" - Type: " + phone.getType()); + } + if (phone.getLabel().isPresent() && !phone.getLabel().get().isBlank()) { + System.out.println(" - Label: " + phone.getLabel().get()); + } + } + } + + if (contact.getEmail().isPresent()) { + System.out.println(" - Email details:"); + for (SharedContact.Email email : contact.getEmail().get()) { + System.out.println(" - Email:"); + if (email.getValue() != null) { + System.out.println(" - Value: " + email.getValue()); + } + if (email.getType() != null) { + System.out.println(" - Type: " + email.getType()); + } + if (email.getLabel().isPresent() && !email.getLabel().get().isBlank()) { + System.out.println(" - Label: " + email.getLabel().get()); + } + } + } + + if (contact.getAddress().isPresent()) { + System.out.println(" - Address details:"); + for (SharedContact.PostalAddress address : contact.getAddress().get()) { + System.out.println(" - Address:"); + if (address.getType() != null) { + System.out.println(" - Type: " + address.getType()); + } + if (address.getLabel().isPresent() && !address.getLabel().get().isBlank()) { + System.out.println(" - Label: " + address.getLabel().get()); + } + if (address.getStreet().isPresent() && !address.getStreet().get().isBlank()) { + System.out.println(" - Street: " + address.getStreet().get()); + } + if (address.getPobox().isPresent() && !address.getPobox().get().isBlank()) { + System.out.println(" - Pobox: " + address.getPobox().get()); + } + if (address.getNeighborhood().isPresent() && !address.getNeighborhood().get().isBlank()) { + System.out.println(" - Neighbourhood: " + address.getNeighborhood().get()); + } + if (address.getCity().isPresent() && !address.getCity().get().isBlank()) { + System.out.println(" - City: " + address.getCity().get()); + } + if (address.getRegion().isPresent() && !address.getRegion().get().isBlank()) { + System.out.println(" - Region: " + address.getRegion().get()); + } + if (address.getPostcode().isPresent() && !address.getPostcode().get().isBlank()) { + System.out.println(" - Postcode: " + address.getPostcode().get()); + } + if (address.getCountry().isPresent() && !address.getCountry().get().isBlank()) { + System.out.println(" - Country: " + address.getCountry().get()); + } + } + } + + System.out.println(" - Organisation: " + + (contact.getOrganization().isPresent() ? contact.getOrganization().get() : "-")); } } if (message.getSticker().isPresent()) { @@ -472,7 +570,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (quote.getMentions() != null && quote.getMentions().size() > 0) { System.out.println(" Mentions: "); for (SignalServiceDataMessage.Mention mention : quote.getMentions()) { - printMention(mention, m); + printMention(mention, m, 1); } } if (quote.getAttachments().size() > 0) { @@ -482,7 +580,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { System.out.println(" Type: " + attachment.getContentType()); System.out.println(" Thumbnail:"); if (attachment.getThumbnail() != null) { - printAttachment(attachment.getThumbnail()); + printAttachment(attachment.getThumbnail(), 3); } } } @@ -495,45 +593,60 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (message.getMentions().isPresent()) { System.out.println("Mentions: "); for (SignalServiceDataMessage.Mention mention : message.getMentions().get()) { - printMention(mention, m); + printMention(mention, m, 0); } } if (message.getAttachments().isPresent()) { System.out.println("Attachments: "); for (SignalServiceAttachment attachment : message.getAttachments().get()) { - printAttachment(attachment); + printAttachment(attachment, 0); } } } - private void printMention(SignalServiceDataMessage.Mention mention, Manager m) { - System.out.println("- " + m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null)) + /** + * Prints the Signal mention information + * + * @param mention is the Signal mention to print + * @param m is the Manager. used to resolve UUIDs into phone numbers if possible + * @param leadingSpaces is the number of spaces you want the message to be indented by + */ + private void printMention(SignalServiceDataMessage.Mention mention, Manager m, int leadingSpaces) { + String spaces = " ".repeat(leadingSpaces); + System.out.println(spaces + "- " + m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null)) .getLegacyIdentifier() + ": " + mention.getStart() + " (length: " + mention.getLength() + ")"); } - private void printAttachment(SignalServiceAttachment attachment) { - System.out.println("- " + attachment.getContentType() + " (" + (attachment.isPointer() ? "Pointer" : "") + ( + /** + * Prints the Signal attachment information + * + * @param attachment is the Signal attachment to print + * @param leadingSpaces is the number of spaces you want the message to be indented by + */ + private void printAttachment(SignalServiceAttachment attachment, int leadingSpaces) { + String spaces = " ".repeat(leadingSpaces); + System.out.println(spaces + "- " + attachment.getContentType() + " (" + (attachment.isPointer() ? "Pointer" : "") + ( attachment.isStream() ? "Stream" : "" ) + ")"); if (attachment.isPointer()) { final SignalServiceAttachmentPointer pointer = attachment.asPointer(); - System.out.println(" Id: " + pointer.getRemoteId() + " Key length: " + pointer.getKey().length); - System.out.println(" Filename: " + ( + System.out.println(spaces + " Id: " + pointer.getRemoteId() + " Key length: " + pointer.getKey().length); + System.out.println(spaces + " Filename: " + ( pointer.getFileName().isPresent() ? pointer.getFileName().get() : "-" )); - System.out.println(" Size: " + ( + System.out.println(spaces + " Size: " + ( pointer.getSize().isPresent() ? pointer.getSize().get() + " bytes" : "" ) + ( pointer.getPreview().isPresent() ? " (Preview is available: " + pointer.getPreview().get().length + " bytes)" : "" )); - System.out.println(" Voice note: " + (pointer.getVoiceNote() ? "yes" : "no")); - System.out.println(" Dimensions: " + pointer.getWidth() + "x" + pointer.getHeight()); + System.out.println(spaces + " Voice note: " + (pointer.getVoiceNote() ? "yes" : "no")); + System.out.println(spaces + " Dimensions: " + pointer.getWidth() + "x" + pointer.getHeight()); File file = m.getAttachmentFile(pointer.getRemoteId()); if (file.exists()) { - System.out.println(" Stored plaintext in: " + file); + System.out.println(spaces + " Stored plaintext in: " + file); } } } diff --git a/src/main/java/org/asamk/signal/json/JsonContactAddress.java b/src/main/java/org/asamk/signal/json/JsonContactAddress.java new file mode 100644 index 00000000..712dd4f3 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContactAddress.java @@ -0,0 +1,48 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.asamk.signal.util.Util; +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +public class JsonContactAddress { + + @JsonProperty + private final SharedContact.PostalAddress.Type type; + + @JsonProperty + private final String label; + + @JsonProperty + private final String street; + + @JsonProperty + private final String pobox; + + @JsonProperty + private final String neighborhood; + + @JsonProperty + private final String city; + + @JsonProperty + private final String region; + + @JsonProperty + private final String postcode; + + @JsonProperty + private final String country; + + public JsonContactAddress(SharedContact.PostalAddress address) { + type = address.getType(); + label = Util.getStringIfNotBlank(address.getLabel()); + street = Util.getStringIfNotBlank(address.getStreet()); + pobox = Util.getStringIfNotBlank(address.getPobox()); + neighborhood = Util.getStringIfNotBlank(address.getNeighborhood()); + city = Util.getStringIfNotBlank(address.getCity()); + region = Util.getStringIfNotBlank(address.getRegion()); + postcode = Util.getStringIfNotBlank(address.getPostcode()); + country = Util.getStringIfNotBlank(address.getCountry()); + } +} diff --git a/src/main/java/org/asamk/signal/json/JsonContactAvatar.java b/src/main/java/org/asamk/signal/json/JsonContactAvatar.java new file mode 100644 index 00000000..3ed55f6f --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContactAvatar.java @@ -0,0 +1,19 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +public class JsonContactAvatar { + + @JsonProperty + private final JsonAttachment attachment; + + @JsonProperty + private final boolean isProfile; + + public JsonContactAvatar(SharedContact.Avatar avatar) { + attachment = new JsonAttachment(avatar.getAttachment()); + isProfile = avatar.isProfile(); + } +} diff --git a/src/main/java/org/asamk/signal/json/JsonContactEmail.java b/src/main/java/org/asamk/signal/json/JsonContactEmail.java new file mode 100644 index 00000000..070cfb72 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContactEmail.java @@ -0,0 +1,24 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.asamk.signal.util.Util; +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +public class JsonContactEmail { + + @JsonProperty + private final String value; + + @JsonProperty + private final SharedContact.Email.Type type; + + @JsonProperty + private final String label; + + public JsonContactEmail(SharedContact.Email email) { + value = email.getValue(); + type = email.getType(); + label = Util.getStringIfNotBlank(email.getLabel()); + } +} diff --git a/src/main/java/org/asamk/signal/json/JsonContactName.java b/src/main/java/org/asamk/signal/json/JsonContactName.java new file mode 100644 index 00000000..9da27825 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContactName.java @@ -0,0 +1,36 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.asamk.signal.util.Util; +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +public class JsonContactName { + + @JsonProperty + private final String display; + + @JsonProperty + private final String given; + + @JsonProperty + private final String family; + + @JsonProperty + private final String prefix; + + @JsonProperty + private final String suffix; + + @JsonProperty + private final String middle; + + public JsonContactName(SharedContact.Name name) { + display = Util.getStringIfNotBlank(name.getDisplay()); + given = Util.getStringIfNotBlank(name.getGiven()); + family = Util.getStringIfNotBlank(name.getFamily()); + prefix = Util.getStringIfNotBlank(name.getPrefix()); + suffix = Util.getStringIfNotBlank(name.getSuffix()); + middle = Util.getStringIfNotBlank(name.getMiddle()); + } +} diff --git a/src/main/java/org/asamk/signal/json/JsonContactPhone.java b/src/main/java/org/asamk/signal/json/JsonContactPhone.java new file mode 100644 index 00000000..fce75843 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContactPhone.java @@ -0,0 +1,24 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.asamk.signal.util.Util; +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +public class JsonContactPhone { + + @JsonProperty + private final String value; + + @JsonProperty + private final SharedContact.Phone.Type type; + + @JsonProperty + private final String label; + + public JsonContactPhone(SharedContact.Phone phone) { + value = phone.getValue(); + type = phone.getType(); + label = Util.getStringIfNotBlank(phone.getLabel()); + } +} diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index 1c927b40..57facc99 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -52,6 +52,10 @@ class JsonDataMessage { @JsonInclude(JsonInclude.Include.NON_NULL) final JsonRemoteDelete remoteDelete; + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List contacts; + @JsonProperty @JsonInclude(JsonInclude.Include.NON_NULL) final JsonGroupInfo groupInfo; @@ -100,6 +104,16 @@ class JsonDataMessage { this.attachments = List.of(); } this.sticker = dataMessage.getSticker().isPresent() ? new JsonSticker(dataMessage.getSticker().get()) : null; + + if (dataMessage.getSharedContacts().isPresent()) { + this.contacts = dataMessage.getSharedContacts() + .get() + .stream() + .map(JsonSharedContact::new) + .collect(Collectors.toList()); + } else { + this.contacts = List.of(); + } } public JsonDataMessage(Signal.MessageReceived messageReceived) { @@ -109,10 +123,11 @@ class JsonDataMessage { expiresInSeconds = null; viewOnce = null; remoteDelete = null; - reaction = null; // TODO Replace these 4 with the proper commands + reaction = null; // TODO Replace these 5 with the proper commands quote = null; mentions = null; sticker = null; + contacts = null; attachments = messageReceived.getAttachments().stream().map(JsonAttachment::new).collect(Collectors.toList()); } @@ -123,10 +138,11 @@ class JsonDataMessage { expiresInSeconds = null; viewOnce = null; remoteDelete = null; - reaction = null; // TODO Replace these 4 with the proper commands + reaction = null; // TODO Replace these 5 with the proper commands quote = null; mentions = null; sticker = null; + contacts = null; attachments = messageReceived.getAttachments().stream().map(JsonAttachment::new).collect(Collectors.toList()); } } diff --git a/src/main/java/org/asamk/signal/json/JsonSharedContact.java b/src/main/java/org/asamk/signal/json/JsonSharedContact.java new file mode 100644 index 00000000..b1b7718c --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonSharedContact.java @@ -0,0 +1,75 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +import java.util.List; +import java.util.stream.Collectors; + +public class JsonSharedContact { + + @JsonProperty + final JsonContactName name; + + @JsonProperty + final JsonContactAvatar avatar; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List phone; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List email; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List address; + + @JsonProperty + final String organization; + + + public JsonSharedContact(SharedContact contact) { + name = new JsonContactName(contact.getName()); + if (contact.getAvatar().isPresent()) { + avatar = new JsonContactAvatar(contact.getAvatar().get()); + } else { + avatar = null; + } + + if (contact.getPhone().isPresent()) { + phone = contact.getPhone() + .get() + .stream() + .map(JsonContactPhone::new) + .collect(Collectors.toList()); + } else { + phone = null; + } + + if (contact.getEmail().isPresent()) { + email = contact.getEmail() + .get() + .stream() + .map(JsonContactEmail::new) + .collect(Collectors.toList()); + } else { + email = null; + } + + if (contact.getAddress().isPresent()) { + address = contact.getAddress() + .get() + .stream() + .map(JsonContactAddress::new) + .collect(Collectors.toList()); + } else { + address = null; + } + + organization = contact.getOrganization().orNull(); + } +} diff --git a/src/main/java/org/asamk/signal/util/Util.java b/src/main/java/org/asamk/signal/util/Util.java index 92bfae7b..e798d537 100644 --- a/src/main/java/org/asamk/signal/util/Util.java +++ b/src/main/java/org/asamk/signal/util/Util.java @@ -2,12 +2,21 @@ package org.asamk.signal.util; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; +import org.whispersystems.libsignal.util.guava.Optional; public class Util { private Util() { } + public static String getStringIfNotBlank(Optional value) { + String string = value.orNull(); + if (string == null || string.isBlank()) { + return null; + } + return string; + } + public static String formatSafetyNumber(String digits) { final int partCount = 12; int partSize = digits.length() / partCount;