X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/5bbfd3259891e18a11cb878e14a9c17990b13d79..6dd1a216062baa6503d158dcaac5ee1a2cd1b43e:/lib/src/main/java/org/asamk/signal/manager/Manager.java 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 8aa7ed18..80c5fbbb 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -81,6 +81,9 @@ import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.libsignal.fingerprint.Fingerprint; +import org.whispersystems.libsignal.fingerprint.FingerprintParsingException; +import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Pair; @@ -1191,11 +1194,31 @@ public class Manager implements Closeable { return sendHelper.sendGroupMessage(messageBuilder.build(), Set.of(resolveRecipient(recipient))); } - void sendReceipt( - SignalServiceAddress remoteAddress, long messageId + public void sendReadReceipt( + String sender, List messageIds + ) throws IOException, UntrustedIdentityException, InvalidNumberException { + var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, + messageIds, + System.currentTimeMillis()); + + sendHelper.sendReceiptMessage(receiptMessage, canonicalizeAndResolveRecipient(sender)); + } + + public void sendViewedReceipt( + String sender, List messageIds + ) throws IOException, UntrustedIdentityException, InvalidNumberException { + var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.VIEWED, + messageIds, + System.currentTimeMillis()); + + sendHelper.sendReceiptMessage(receiptMessage, canonicalizeAndResolveRecipient(sender)); + } + + void sendDeliveryReceipt( + SignalServiceAddress remoteAddress, List messageIds ) throws IOException, UntrustedIdentityException { var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, - List.of(messageId), + messageIds, System.currentTimeMillis()); sendHelper.sendReceiptMessage(receiptMessage, resolveRecipient(remoteAddress)); @@ -1767,16 +1790,7 @@ public class Manager implements Closeable { queuedActions.addAll(actions); } } - for (var action : queuedActions) { - try { - action.execute(this); - } catch (Throwable e) { - if (e instanceof AssertionError && e.getCause() instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - logger.warn("Message action failed.", e); - } - } + handleQueuedActions(queuedActions); } private List retryFailedReceivedMessage( @@ -1820,10 +1834,10 @@ public class Manager implements Closeable { boolean returnOnTimeout, boolean ignoreAttachments, ReceiveMessageHandler handler - ) throws IOException, InterruptedException { + ) throws IOException { retryFailedReceivedMessages(handler, ignoreAttachments); - Set queuedActions = null; + Set queuedActions = new HashSet<>(); final var signalWebSocket = dependencies.getSignalWebSocket(); signalWebSocket.connect(); @@ -1852,27 +1866,16 @@ public class Manager implements Closeable { // Received indicator that server queue is empty hasCaughtUpWithOldMessages = true; - if (queuedActions != null) { - for (var action : queuedActions) { - try { - action.execute(this); - } catch (Throwable e) { - if (e instanceof AssertionError && e.getCause() instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - logger.warn("Message action failed.", e); - } - } - queuedActions.clear(); - queuedActions = null; - } + handleQueuedActions(queuedActions); + queuedActions.clear(); // Continue to wait another timeout for new messages continue; } } catch (AssertionError e) { if (e.getCause() instanceof InterruptedException) { - throw (InterruptedException) e.getCause(); + Thread.currentThread().interrupt(); + break; } else { throw e; } @@ -1919,9 +1922,6 @@ public class Manager implements Closeable { } } } else { - if (queuedActions == null) { - queuedActions = new HashSet<>(); - } queuedActions.addAll(actions); } } @@ -1953,6 +1953,20 @@ public class Manager implements Closeable { } } } + handleQueuedActions(queuedActions); + } + + private void handleQueuedActions(final Set queuedActions) { + for (var action : queuedActions) { + try { + action.execute(this); + } catch (Throwable e) { + if (e instanceof AssertionError && e.getCause() instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + logger.warn("Message action failed.", e); + } + } } private boolean isMessageBlocked( @@ -2654,6 +2668,25 @@ public class Manager implements Closeable { TrustLevel.TRUSTED_VERIFIED); } + /** + * Trust this the identity with this scannable safety number + * + * @param name username of the identity + * @param safetyNumber Scannable safety number + */ + public boolean trustIdentityVerifiedSafetyNumber(String name, byte[] safetyNumber) throws InvalidNumberException { + var recipientId = canonicalizeAndResolveRecipient(name); + var address = account.getRecipientStore().resolveServiceAddress(recipientId); + return trustIdentity(recipientId, identityKey -> { + final var fingerprint = computeSafetyNumberFingerprint(address, identityKey); + try { + return fingerprint != null && fingerprint.getScannableFingerprint().compareTo(safetyNumber); + } catch (FingerprintVersionMismatchException | FingerprintParsingException e) { + return false; + } + }, TrustLevel.TRUSTED_VERIFIED); + } + /** * Trust all keys of this identity without verification * @@ -2703,21 +2736,23 @@ public class Manager implements Closeable { } public String computeSafetyNumber(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey) { - final var fingerprint = Utils.computeSafetyNumber(capabilities.isUuid(), - account.getSelfAddress(), - getIdentityKeyPair().getPublicKey(), - theirAddress, - theirIdentityKey); + final Fingerprint fingerprint = computeSafetyNumberFingerprint(theirAddress, theirIdentityKey); return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText(); } public byte[] computeSafetyNumberForScanning(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey) { - final var fingerprint = Utils.computeSafetyNumber(capabilities.isUuid(), + final Fingerprint fingerprint = computeSafetyNumberFingerprint(theirAddress, theirIdentityKey); + return fingerprint == null ? null : fingerprint.getScannableFingerprint().getSerialized(); + } + + private Fingerprint computeSafetyNumberFingerprint( + final SignalServiceAddress theirAddress, final IdentityKey theirIdentityKey + ) { + return Utils.computeSafetyNumber(capabilities.isUuid(), account.getSelfAddress(), getIdentityKeyPair().getPublicKey(), theirAddress, theirIdentityKey); - return fingerprint == null ? null : fingerprint.getScannableFingerprint().getSerialized(); } @Deprecated