X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/aa8a23aceb6cd9cb63723f4d783c7ef07e365610..33956bde62d4fc7de5325bf3bb7be3b323863442:/src/main/java/org/asamk/signal/Manager.java diff --git a/src/main/java/org/asamk/signal/Manager.java b/src/main/java/org/asamk/signal/Manager.java index 2fc54504..d442224a 100644 --- a/src/main/java/org/asamk/signal/Manager.java +++ b/src/main/java/org/asamk/signal/Manager.java @@ -49,6 +49,9 @@ import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; @@ -72,6 +75,7 @@ class Manager implements Signal { private final ObjectMapper jsonProcessot = new ObjectMapper(); private String username; + int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; private String password; private String signalingKey; private int preKeyIdOffset; @@ -95,12 +99,19 @@ class Manager implements Signal { jsonProcessot.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); } + public String getUsername() { + return username; + } + public String getFileName() { new File(dataPath).mkdirs(); return dataPath + "/" + username; } public boolean userExists() { + if (username == null) { + return false; + } File f = new File(getFileName()); return !(!f.exists() || f.isDirectory()); } @@ -121,6 +132,10 @@ class Manager implements Signal { public void load() throws IOException, InvalidKeyException { JsonNode rootNode = jsonProcessot.readTree(new File(getFileName())); + JsonNode node = rootNode.get("deviceId"); + if (node != null) { + deviceId = node.asInt(); + } username = getNotNullNode(rootNode, "username").asText(); password = getNotNullNode(rootNode, "password").asText(); if (rootNode.has("signalingKey")) { @@ -145,7 +160,7 @@ class Manager implements Signal { if (groupStore == null) { groupStore = new JsonGroupStore(); } - accountManager = new SignalServiceAccountManager(URL, TRUST_STORE, username, password, USER_AGENT); + accountManager = new SignalServiceAccountManager(URL, TRUST_STORE, username, password, deviceId, USER_AGENT); try { if (registered && accountManager.getPreKeysCount() < PREKEY_MINIMUM_COUNT) { refreshPreKeys(); @@ -159,6 +174,7 @@ class Manager implements Signal { private void save() { ObjectNode rootNode = jsonProcessot.createObjectNode(); rootNode.put("username", username) + .put("deviceId", deviceId) .put("password", password) .put("signalingKey", signalingKey) .put("preKeyIdOffset", preKeyIdOffset) @@ -201,6 +217,36 @@ class Manager implements Signal { save(); } + public URI getDeviceLinkUri() throws TimeoutException, IOException { + password = Util.getSecret(18); + + accountManager = new SignalServiceAccountManager(URL, TRUST_STORE, username, password, USER_AGENT); + String uuid = accountManager.getNewDeviceUuid(); + + registered = false; + try { + return new URI("tsdevice:/?uuid=" + URLEncoder.encode(uuid, "utf-8") + "&pub_key=" + URLEncoder.encode(Base64.encodeBytesWithoutPadding(signalProtocolStore.getIdentityKeyPair().getPublicKey().serialize()), "utf-8")); + } catch (URISyntaxException e) { + // Shouldn't happen + return null; + } + } + + public void finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists { + signalingKey = Util.getSecret(52); + SignalServiceAccountManager.NewDeviceRegistrationReturn ret = accountManager.finishNewDeviceRegistration(signalProtocolStore.getIdentityKeyPair(), signalingKey, false, true, signalProtocolStore.getLocalRegistrationId(), deviceName); + deviceId = ret.getDeviceId(); + username = ret.getNumber(); + // TODO do this check before actually registering + if (userExists()) { + throw new UserAlreadyExists(username, getFileName()); + } + signalProtocolStore = new JsonSignalProtocolStore(ret.getIdentity(), signalProtocolStore.getLocalRegistrationId()); + + registered = true; + refreshPreKeys(); + } + private List generatePreKeys() { List records = new LinkedList<>(); @@ -300,7 +346,7 @@ class Manager implements Signal { @Override public void sendGroupMessage(String messageText, List attachments, byte[] groupId) - throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException { + throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, UntrustedIdentityException { final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText); if (attachments != null) { messageBuilder.withAttachments(getSignalServiceAttachments(attachments)); @@ -316,7 +362,7 @@ class Manager implements Signal { sendMessage(message, groupStore.getGroup(groupId).members); } - public void sendQuitGroupMessage(byte[] groupId) throws GroupNotFoundException, IOException, EncapsulatedExceptions { + public void sendQuitGroupMessage(byte[] groupId) throws GroupNotFoundException, IOException, EncapsulatedExceptions, UntrustedIdentityException { SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT) .withId(groupId) .build(); @@ -328,7 +374,7 @@ class Manager implements Signal { sendMessage(message, groupStore.getGroup(groupId).members); } - public byte[] sendUpdateGroupMessage(byte[] groupId, String name, Collection members, String avatarFile) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException { + public byte[] sendUpdateGroupMessage(byte[] groupId, String name, Collection members, String avatarFile) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, UntrustedIdentityException { GroupInfo g; if (groupId == null) { // Create new group @@ -381,7 +427,7 @@ class Manager implements Signal { @Override public void sendMessage(String message, List attachments, String recipient) - throws EncapsulatedExceptions, AttachmentInvalidException, IOException { + throws EncapsulatedExceptions, AttachmentInvalidException, IOException, UntrustedIdentityException { List recipients = new ArrayList<>(1); recipients.add(recipient); sendMessage(message, attachments, recipients); @@ -390,7 +436,7 @@ class Manager implements Signal { @Override public void sendMessage(String messageText, List attachments, List recipients) - throws IOException, EncapsulatedExceptions, AttachmentInvalidException { + throws IOException, EncapsulatedExceptions, AttachmentInvalidException, UntrustedIdentityException { final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText); if (attachments != null) { messageBuilder.withAttachments(getSignalServiceAttachments(attachments)); @@ -401,7 +447,7 @@ class Manager implements Signal { } @Override - public void sendEndSessionMessage(List recipients) throws IOException, EncapsulatedExceptions { + public void sendEndSessionMessage(List recipients) throws IOException, EncapsulatedExceptions, UntrustedIdentityException { SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder() .asEndSessionMessage() .build(); @@ -410,9 +456,9 @@ class Manager implements Signal { } private void sendMessage(SignalServiceDataMessage message, Collection recipients) - throws IOException, EncapsulatedExceptions { + throws IOException, EncapsulatedExceptions, UntrustedIdentityException { SignalServiceMessageSender messageSender = new SignalServiceMessageSender(URL, TRUST_STORE, username, password, - signalProtocolStore, USER_AGENT, Optional.absent()); + deviceId, signalProtocolStore, USER_AGENT, Optional.absent()); Set recipientsTS = new HashSet<>(recipients.size()); for (String recipient : recipients) { @@ -426,7 +472,14 @@ class Manager implements Signal { } } - messageSender.sendMessage(new ArrayList<>(recipientsTS), message); + if (message.getGroupInfo().isPresent()) { + messageSender.sendMessage(new ArrayList<>(recipientsTS), message); + } else { + // Send to all individually, so sync messages are sent correctly + for (SignalServiceAddress address : recipientsTS) { + messageSender.sendMessage(address, message); + } + } if (message.isEndSession()) { for (SignalServiceAddress recipient : recipientsTS) { @@ -523,7 +576,7 @@ class Manager implements Signal { } public void receiveMessages(int timeoutSeconds, boolean returnOnTimeout, ReceiveMessageHandler handler) throws IOException { - final SignalServiceMessageReceiver messageReceiver = new SignalServiceMessageReceiver(URL, TRUST_STORE, username, password, signalingKey, USER_AGENT); + final SignalServiceMessageReceiver messageReceiver = new SignalServiceMessageReceiver(URL, TRUST_STORE, username, password, deviceId, signalingKey, USER_AGENT); SignalServiceMessagePipe messagePipe = null; try { @@ -577,7 +630,7 @@ class Manager implements Signal { } private File retrieveAttachment(SignalServiceAttachmentPointer pointer) throws IOException, InvalidMessageException { - final SignalServiceMessageReceiver messageReceiver = new SignalServiceMessageReceiver(URL, TRUST_STORE, username, password, signalingKey, USER_AGENT); + final SignalServiceMessageReceiver messageReceiver = new SignalServiceMessageReceiver(URL, TRUST_STORE, username, password, deviceId, signalingKey, USER_AGENT); File tmpFile = File.createTempFile("ts_attach_" + pointer.getId(), ".tmp"); InputStream input = messageReceiver.retrieveAttachment(pointer, tmpFile);