From: AsamK Date: Fri, 22 Apr 2016 19:17:02 +0000 (+0200) Subject: Implement a contacts store and contacts sync X-Git-Tag: v0.4.0~12 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/32beb8a0bd51d2bd13b9687168ed08cad3b8eca1?ds=sidebyside Implement a contacts store and contacts sync --- diff --git a/src/main/java/org/asamk/signal/ContactInfo.java b/src/main/java/org/asamk/signal/ContactInfo.java new file mode 100644 index 00000000..89802050 --- /dev/null +++ b/src/main/java/org/asamk/signal/ContactInfo.java @@ -0,0 +1,11 @@ +package org.asamk.signal; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ContactInfo { + @JsonProperty + public String name; + + @JsonProperty + public String number; +} diff --git a/src/main/java/org/asamk/signal/JsonContactsStore.java b/src/main/java/org/asamk/signal/JsonContactsStore.java new file mode 100644 index 00000000..e2807d8f --- /dev/null +++ b/src/main/java/org/asamk/signal/JsonContactsStore.java @@ -0,0 +1,57 @@ +package org.asamk.signal; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class JsonContactsStore { + @JsonProperty("contacts") + @JsonSerialize(using = JsonContactsStore.MapToListSerializer.class) + @JsonDeserialize(using = ContactsDeserializer.class) + private Map contacts = new HashMap<>(); + + private static final ObjectMapper jsonProcessot = new ObjectMapper(); + + void updateContact(ContactInfo contact) { + contacts.put(contact.number, contact); + } + + ContactInfo getContact(String number) { + ContactInfo c = contacts.get(number); + return c; + } + + List getContacts() { + return new ArrayList<>(contacts.values()); + } + + public static class MapToListSerializer extends JsonSerializer> { + @Override + public void serialize(final Map value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException { + jgen.writeObject(value.values()); + } + } + + public static class ContactsDeserializer extends JsonDeserializer> { + @Override + public Map deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + Map contacts = new HashMap<>(); + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + for (JsonNode n : node) { + ContactInfo c = jsonProcessot.treeToValue(n, ContactInfo.class); + contacts.put(c.number, c); + } + + return contacts; + } + } +} diff --git a/src/main/java/org/asamk/signal/Manager.java b/src/main/java/org/asamk/signal/Manager.java index be94f7f5..6295f730 100644 --- a/src/main/java/org/asamk/signal/Manager.java +++ b/src/main/java/org/asamk/signal/Manager.java @@ -90,6 +90,7 @@ class Manager implements Signal { private SignalProtocolStore signalProtocolStore; private SignalServiceAccountManager accountManager; private JsonGroupStore groupStore; + private JsonContactsStore contactStore; public Manager(String username, String settingsPath) { this.username = username; @@ -168,6 +169,14 @@ class Manager implements Signal { if (groupStore == null) { groupStore = new JsonGroupStore(); } + JsonNode contactStoreNode = rootNode.get("contactStore"); + if (contactStoreNode != null) { + contactStore = jsonProcessot.convertValue(contactStoreNode, JsonContactsStore.class); + } + if (contactStore == null) { + contactStore = new JsonContactsStore(); + } + accountManager = new SignalServiceAccountManager(URL, TRUST_STORE, username, password, deviceId, USER_AGENT); try { if (registered && accountManager.getPreKeysCount() < PREKEY_MINIMUM_COUNT) { @@ -193,6 +202,7 @@ class Manager implements Signal { .put("registered", registered) .putPOJO("axolotlStore", signalProtocolStore) .putPOJO("groupStore", groupStore) + .putPOJO("contactStore", contactStore) ; try { jsonProcessot.writeValue(new File(getFileName()), rootNode); @@ -714,7 +724,13 @@ class Manager implements Signal { if (syncMessage.getRequest().isPresent()) { RequestMessage rm = syncMessage.getRequest().get(); if (rm.isContactsRequest()) { - // TODO implement when we have contacts + try { + sendContacts(); + } catch (EncapsulatedExceptions encapsulatedExceptions) { + encapsulatedExceptions.printStackTrace(); + } catch (UntrustedIdentityException e) { + e.printStackTrace(); + } } if (rm.isGroupsRequest()) { try { @@ -759,11 +775,12 @@ class Manager implements Signal { DeviceContactsInputStream s = new DeviceContactsInputStream(retrieveAttachmentAsStream(syncMessage.getContacts().get().asPointer())); DeviceContact c; while ((c = s.read()) != null) { - // TODO implement when we have contact storage + ContactInfo contact = new ContactInfo(); + contact.number = c.getNumber(); if (c.getName().isPresent()) { - c.getName().get(); + contact.name = c.getName().get(); } - c.getNumber(); + contactStore.updateContact(contact); if (c.getAvatar().isPresent()) { byte[] ava = new byte[(int) c.getAvatar().get().getLength()]; @@ -868,20 +885,49 @@ class Manager implements Signal { } private void sendGroups() throws IOException, EncapsulatedExceptions, UntrustedIdentityException { - File contactsFile = File.createTempFile("multidevice-contact-update", ".tmp"); + File groupsFile = File.createTempFile("multidevice-group-update", ".tmp"); try { - DeviceGroupsOutputStream out = new DeviceGroupsOutputStream(new FileOutputStream(contactsFile)); + DeviceGroupsOutputStream out = new DeviceGroupsOutputStream(new FileOutputStream(groupsFile)); try { for (GroupInfo record : groupStore.getGroups()) { out.write(new DeviceGroup(record.groupId, Optional.fromNullable(record.name), - new ArrayList<>(record.members), Optional.of(new SignalServiceAttachmentStream(new FileInputStream("/home/sebastian/Bilder/00026_150512_14-00-18.JPG"), "octet", new File("/home/sebastian/Bilder/00026_150512_14-00-18.JPG").length(), null)), + new ArrayList<>(record.members), Optional.absent(), // TODO record.active)); } } finally { out.close(); } + if (groupsFile.exists() && groupsFile.length() > 0) { + FileInputStream contactsFileStream = new FileInputStream(groupsFile); + SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() + .withStream(contactsFileStream) + .withContentType("application/octet-stream") + .withLength(groupsFile.length()) + .build(); + + sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream)); + } + } finally { + groupsFile.delete(); + } + } + + private void sendContacts() throws IOException, EncapsulatedExceptions, UntrustedIdentityException { + File contactsFile = File.createTempFile("multidevice-contact-update", ".tmp"); + + try { + DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactsFile)); + try { + for (ContactInfo record : contactStore.getContacts()) { + out.write(new DeviceContact(record.number, Optional.fromNullable(record.name), + Optional.absent())); // TODO + } + } finally { + out.close(); + } + if (contactsFile.exists() && contactsFile.length() > 0) { FileInputStream contactsFileStream = new FileInputStream(contactsFile); SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() @@ -890,10 +936,10 @@ class Manager implements Signal { .withLength(contactsFile.length()) .build(); - sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream)); + sendMessage(SignalServiceSyncMessage.forContacts(attachmentStream)); } } finally { - if (contactsFile != null) contactsFile.delete(); + contactsFile.delete(); } } }