From 382d8d22d0ccad109106b140616638799cc646f6 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 14 Nov 2021 14:42:17 +0100 Subject: [PATCH] Make send behavior more deterministic if there are unregistered recipients Fixes #803 --- lib/build.gradle.kts | 2 +- .../org/asamk/signal/manager/Manager.java | 12 +- .../org/asamk/signal/manager/ManagerImpl.java | 54 +++++-- .../manager/UntrustedIdentityException.java | 10 +- .../signal/manager/helper/SendHelper.java | 152 +++++++++--------- .../asamk/signal/commands/SendCommand.java | 4 +- .../signal/commands/SendReceiptCommand.java | 24 ++- .../signal/commands/SendTypingCommand.java | 20 ++- .../asamk/signal/dbus/DbusManagerImpl.java | 32 ++-- .../org/asamk/signal/dbus/DbusSignalImpl.java | 18 +-- 10 files changed, 192 insertions(+), 136 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index ae1d88a0..54c23a0d 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa:signal-service-java:2.15.3_unofficial_32") + implementation("com.github.turasa:signal-service-java:2.15.3_unofficial_33") api("com.fasterxml.jackson.core", "jackson-databind", "2.13.0") implementation("com.google.protobuf:protobuf-javalite:3.11.4") implementation("org.bouncycastle:bcprov-jdk15on:1.69") 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 2ffb5337..61f6e1d6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -144,17 +144,17 @@ public interface Manager extends Closeable { GroupInviteLinkUrl inviteLinkUrl ) throws IOException, InactiveGroupLinkException; - void sendTypingMessage( + SendMessageResults sendTypingMessage( TypingAction action, Set recipients - ) throws IOException, UntrustedIdentityException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException; + ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException; - void sendReadReceipt( + SendMessageResults sendReadReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException, UntrustedIdentityException; + ) throws IOException; - void sendViewedReceipt( + SendMessageResults sendViewedReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException, UntrustedIdentityException; + ) throws IOException; SendMessageResults sendMessage( Message message, Set recipients diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 81c18683..93296a1c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -620,50 +620,74 @@ public class ManagerImpl implements Manager { return new SendMessageResults(timestamp, results); } - private void sendTypingMessage( + private SendMessageResults sendTypingMessage( SignalServiceTypingMessage.Action action, Set recipients - ) throws IOException, UntrustedIdentityException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { + ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { + var results = new HashMap>(); final var timestamp = System.currentTimeMillis(); for (var recipient : recipients) { if (recipient instanceof RecipientIdentifier.Single) { final var message = new SignalServiceTypingMessage(action, timestamp, Optional.absent()); final var recipientId = resolveRecipient((RecipientIdentifier.Single) recipient); - sendHelper.sendTypingMessage(message, recipientId); + final var result = sendHelper.sendTypingMessage(message, recipientId); + results.put(recipient, + List.of(SendMessageResult.from(result, + account.getRecipientStore(), + account.getRecipientStore()::resolveRecipientAddress))); } else if (recipient instanceof RecipientIdentifier.Group) { final var groupId = ((RecipientIdentifier.Group) recipient).groupId(); final var message = new SignalServiceTypingMessage(action, timestamp, Optional.of(groupId.serialize())); - sendHelper.sendGroupTypingMessage(message, groupId); + final var result = sendHelper.sendGroupTypingMessage(message, groupId); + results.put(recipient, + result.stream() + .map(r -> SendMessageResult.from(r, + account.getRecipientStore(), + account.getRecipientStore()::resolveRecipientAddress)) + .collect(Collectors.toList())); } } + return new SendMessageResults(timestamp, results); } @Override - public void sendTypingMessage( + public SendMessageResults sendTypingMessage( TypingAction action, Set recipients - ) throws IOException, UntrustedIdentityException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { - sendTypingMessage(action.toSignalService(), recipients); + ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { + return sendTypingMessage(action.toSignalService(), recipients); } @Override - public void sendReadReceipt( + public SendMessageResults sendReadReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException, UntrustedIdentityException { + ) throws IOException { + final var timestamp = System.currentTimeMillis(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, - System.currentTimeMillis()); + timestamp); - sendHelper.sendReceiptMessage(receiptMessage, resolveRecipient(sender)); + final var result = sendHelper.sendReceiptMessage(receiptMessage, resolveRecipient(sender)); + return new SendMessageResults(timestamp, + Map.of(sender, + List.of(SendMessageResult.from(result, + account.getRecipientStore(), + account.getRecipientStore()::resolveRecipientAddress)))); } @Override - public void sendViewedReceipt( + public SendMessageResults sendViewedReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException, UntrustedIdentityException { + ) throws IOException { + final var timestamp = System.currentTimeMillis(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.VIEWED, messageIds, - System.currentTimeMillis()); + timestamp); - sendHelper.sendReceiptMessage(receiptMessage, resolveRecipient(sender)); + final var result = sendHelper.sendReceiptMessage(receiptMessage, resolveRecipient(sender)); + return new SendMessageResults(timestamp, + Map.of(sender, + List.of(SendMessageResult.from(result, + account.getRecipientStore(), + account.getRecipientStore()::resolveRecipientAddress)))); } @Override diff --git a/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java b/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java index e7bb5436..318afd26 100644 --- a/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java +++ b/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java @@ -5,13 +5,9 @@ import org.asamk.signal.manager.storage.recipients.RecipientAddress; public class UntrustedIdentityException extends Exception { private final RecipientAddress sender; - private final Integer senderDevice; + private final int senderDevice; - public UntrustedIdentityException(final RecipientAddress sender) { - this(sender, null); - } - - public UntrustedIdentityException(final RecipientAddress sender, final Integer senderDevice) { + public UntrustedIdentityException(final RecipientAddress sender, final int senderDevice) { super("Untrusted identity: " + sender.getIdentifier()); this.sender = sender; this.senderDevice = senderDevice; @@ -21,7 +17,7 @@ public class UntrustedIdentityException extends Exception { return sender; } - public Integer getSenderDevice() { + public int getSenderDevice() { return senderDevice; } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index dc912634..237f813d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java @@ -1,7 +1,6 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.SignalDependencies; -import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; @@ -17,12 +16,14 @@ import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.ContentHint; +import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException; import org.whispersystems.signalservice.api.push.exceptions.RateLimitException; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; @@ -68,8 +69,8 @@ public class SendHelper { } /** - * Send a single message to one or multiple recipients. - * The message is extended with the current expiration timer for each recipient. + * Send a single message to one recipient. + * The message is extended with the current expiration timer. */ public SendMessageResult sendMessage( final SignalServiceDataMessage.Builder messageBuilder, final RecipientId recipientId @@ -135,67 +136,46 @@ public class SendHelper { return result; } - public void sendDeliveryReceipt( + public SendMessageResult sendDeliveryReceipt( RecipientId recipientId, List messageIds - ) throws IOException, UntrustedIdentityException { + ) { var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, messageIds, System.currentTimeMillis()); - sendReceiptMessage(receiptMessage, recipientId); + return sendReceiptMessage(receiptMessage, recipientId); } - public void sendReceiptMessage( + public SendMessageResult sendReceiptMessage( final SignalServiceReceiptMessage receiptMessage, final RecipientId recipientId - ) throws IOException, UntrustedIdentityException { - final var messageSender = dependencies.getMessageSender(); - final var address = addressResolver.resolveSignalServiceAddress(recipientId); - try { - messageSender.sendReceipt(address, unidentifiedAccessHelper.getAccessFor(recipientId), receiptMessage); - } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { - throw new UntrustedIdentityException(account.getRecipientStore().resolveRecipientAddress(recipientId)); - } + ) { + return handleSendMessage(recipientId, + (messageSender, address, unidentifiedAccess) -> messageSender.sendReceipt(address, + unidentifiedAccess, + receiptMessage)); } - public void sendRetryReceipt( + public SendMessageResult sendRetryReceipt( DecryptionErrorMessage errorMessage, RecipientId recipientId, Optional groupId - ) throws IOException, UntrustedIdentityException { - var messageSender = dependencies.getMessageSender(); - final var address = addressResolver.resolveSignalServiceAddress(recipientId); + ) { logger.debug("Sending retry receipt for {} to {}, device: {}", errorMessage.getTimestamp(), recipientId, errorMessage.getDeviceId()); - try { - messageSender.sendRetryReceipt(address, - unidentifiedAccessHelper.getAccessFor(recipientId), - groupId.transform(GroupId::serialize), - errorMessage); - } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { - throw new UntrustedIdentityException(account.getRecipientStore().resolveRecipientAddress(recipientId)); - } + return handleSendMessage(recipientId, + (messageSender, address, unidentifiedAccess) -> messageSender.sendRetryReceipt(address, + unidentifiedAccess, + groupId.transform(GroupId::serialize), + errorMessage)); } - public SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException { - var messageSender = dependencies.getMessageSender(); - - final var address = addressResolver.resolveSignalServiceAddress(recipientId); - try { - try { - return messageSender.sendNullMessage(address, unidentifiedAccessHelper.getAccessFor(recipientId)); - } catch (UnregisteredUserException e) { - final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId); - final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId); - return messageSender.sendNullMessage(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId)); - } - } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { - return SendMessageResult.identityFailure(address, e.getIdentityKey()); - } + public SendMessageResult sendNullMessage(RecipientId recipientId) { + return handleSendMessage(recipientId, SignalServiceMessageSender::sendNullMessage); } public SendMessageResult sendSelfMessage( SignalServiceDataMessage.Builder messageBuilder - ) throws IOException { + ) { final var recipientId = account.getSelfRecipientId(); final var contact = account.getContactStore().getContact(recipientId); final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0; @@ -205,35 +185,40 @@ public class SendHelper { return sendSelfMessage(message); } - public SendMessageResult sendSyncMessage(SignalServiceSyncMessage message) throws IOException { + public SendMessageResult sendSyncMessage(SignalServiceSyncMessage message) { var messageSender = dependencies.getMessageSender(); try { return messageSender.sendSyncMessage(message, unidentifiedAccessHelper.getAccessForSync()); + } catch (UnregisteredUserException e) { + var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId()); + return SendMessageResult.unregisteredFailure(address); + } catch (ProofRequiredException e) { + var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId()); + return SendMessageResult.proofRequiredFailure(address, e); + } catch (RateLimitException e) { + var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId()); + logger.warn("Sending failed due to rate limiting from the signal server: {}", e.getMessage()); + return SendMessageResult.networkFailure(address); } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId()); return SendMessageResult.identityFailure(address, e.getIdentityKey()); + } catch (IOException e) { + var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId()); + logger.warn("Failed to send message due to IO exception: {}", e.getMessage()); + return SendMessageResult.networkFailure(address); } } - public void sendTypingMessage( + public SendMessageResult sendTypingMessage( SignalServiceTypingMessage message, RecipientId recipientId - ) throws IOException, UntrustedIdentityException { - var messageSender = dependencies.getMessageSender(); - final var address = addressResolver.resolveSignalServiceAddress(recipientId); - try { - try { - messageSender.sendTyping(address, unidentifiedAccessHelper.getAccessFor(recipientId), message); - } catch (UnregisteredUserException e) { - final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId); - final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId); - messageSender.sendTyping(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId), message); - } - } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { - throw new UntrustedIdentityException(account.getRecipientStore().resolveRecipientAddress(recipientId)); - } + ) { + return handleSendMessage(recipientId, + (messageSender, address, unidentifiedAccess) -> messageSender.sendTyping(address, + unidentifiedAccess, + message)); } - public void sendGroupTypingMessage( + public List sendGroupTypingMessage( SignalServiceTypingMessage message, GroupId groupId ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { final var g = getGroupForSending(groupId); @@ -245,7 +230,10 @@ public class SendHelper { final var addresses = recipientIdList.stream() .map(addressResolver::resolveSignalServiceAddress) .collect(Collectors.toList()); - messageSender.sendTyping(addresses, unidentifiedAccessHelper.getAccessFor(recipientIdList), message, null); + return messageSender.sendTyping(addresses, + unidentifiedAccessHelper.getAccessFor(recipientIdList), + message, + null); } private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { @@ -285,25 +273,29 @@ public class SendHelper { private SendMessageResult sendMessage( SignalServiceDataMessage message, RecipientId recipientId - ) throws IOException { + ) { + return handleSendMessage(recipientId, + (messageSender, address, unidentifiedAccess) -> messageSender.sendDataMessage(address, + unidentifiedAccess, + ContentHint.DEFAULT, + message, + SignalServiceMessageSender.IndividualSendEvents.EMPTY)); + } + + private SendMessageResult handleSendMessage(RecipientId recipientId, SenderHandler s) { var messageSender = dependencies.getMessageSender(); - final var address = addressResolver.resolveSignalServiceAddress(recipientId); + var address = addressResolver.resolveSignalServiceAddress(recipientId); try { try { - return messageSender.sendDataMessage(address, - unidentifiedAccessHelper.getAccessFor(recipientId), - ContentHint.DEFAULT, - message, - SignalServiceMessageSender.IndividualSendEvents.EMPTY); + return s.send(messageSender, address, unidentifiedAccessHelper.getAccessFor(recipientId)); } catch (UnregisteredUserException e) { final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId); - return messageSender.sendDataMessage(addressResolver.resolveSignalServiceAddress(newRecipientId), - unidentifiedAccessHelper.getAccessFor(newRecipientId), - ContentHint.DEFAULT, - message, - SignalServiceMessageSender.IndividualSendEvents.EMPTY); + address = addressResolver.resolveSignalServiceAddress(newRecipientId); + return s.send(messageSender, address, unidentifiedAccessHelper.getAccessFor(newRecipientId)); } + } catch (UnregisteredUserException e) { + return SendMessageResult.unregisteredFailure(address); } catch (ProofRequiredException e) { return SendMessageResult.proofRequiredFailure(address, e); } catch (RateLimitException e) { @@ -311,10 +303,13 @@ public class SendHelper { return SendMessageResult.networkFailure(address); } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { return SendMessageResult.identityFailure(address, e.getIdentityKey()); + } catch (IOException e) { + logger.warn("Failed to send message due to IO exception: {}", e.getMessage()); + return SendMessageResult.networkFailure(address); } } - private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException { + private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) { var address = account.getSelfAddress(); var transcript = new SentTranscriptMessage(Optional.of(address), message.getTimestamp(), @@ -333,4 +328,13 @@ public class SendHelper { identityFailureHandler.handleIdentityFailure(recipientId, r.getIdentityFailure()); } } + + interface SenderHandler { + + SendMessageResult send( + SignalServiceMessageSender messageSender, + SignalServiceAddress address, + Optional unidentifiedAccess + ) throws IOException, UnregisteredUserException, ProofRequiredException, RateLimitException, org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; + } } diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 8d863d54..a7d29b17 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -83,7 +83,9 @@ public class SendCommand implements JsonRpcLocalCommand { } try { - m.sendEndSessionMessage(singleRecipients); + final var results = m.sendEndSessionMessage(singleRecipients); + outputResult(outputWriter, results.timestamp()); + ErrorUtils.handleSendMessageResults(results.results()); return; } catch (IOException e) { throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() diff --git a/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java b/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java index 5dd29682..266e3b8f 100644 --- a/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java @@ -3,14 +3,18 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputWriter; +import org.asamk.signal.PlainTextWriter; import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.UntrustedIdentityException; +import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.util.CommandUtil; +import org.asamk.signal.util.ErrorUtils; import java.io.IOException; +import java.util.Map; public class SendReceiptCommand implements JsonRpcLocalCommand { @@ -43,16 +47,28 @@ public class SendReceiptCommand implements JsonRpcLocalCommand { final var type = ns.getString("type"); try { + final SendMessageResults results; if (type == null || "read".equals(type)) { - m.sendReadReceipt(recipient, targetTimestamps); + results = m.sendReadReceipt(recipient, targetTimestamps); } else if ("viewed".equals(type)) { - m.sendViewedReceipt(recipient, targetTimestamps); + results = m.sendViewedReceipt(recipient, targetTimestamps); } else { throw new UserErrorException("Unknown receipt type: " + type); } - } catch (IOException | UntrustedIdentityException e) { + outputResult(outputWriter, results.timestamp()); + ErrorUtils.handleSendMessageResults(results.results()); + } catch (IOException e) { throw new UserErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() .getSimpleName() + ")"); } } + + private void outputResult(final OutputWriter outputWriter, final long timestamp) { + if (outputWriter instanceof PlainTextWriter writer) { + writer.println("{}", timestamp); + } else { + final var writer = (JsonWriter) outputWriter; + writer.write(Map.of("timestamp", timestamp)); + } + } } diff --git a/src/main/java/org/asamk/signal/commands/SendTypingCommand.java b/src/main/java/org/asamk/signal/commands/SendTypingCommand.java index ba062b70..9ca55c79 100644 --- a/src/main/java/org/asamk/signal/commands/SendTypingCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendTypingCommand.java @@ -4,20 +4,23 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputWriter; +import org.asamk.signal.PlainTextWriter; import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.util.CommandUtil; +import org.asamk.signal.util.ErrorUtils; import java.io.IOException; import java.util.HashSet; +import java.util.Map; public class SendTypingCommand implements JsonRpcLocalCommand { @@ -57,12 +60,23 @@ public class SendTypingCommand implements JsonRpcLocalCommand { } try { - m.sendTypingMessage(action, recipientIdentifiers); - } catch (IOException | UntrustedIdentityException e) { + final var results = m.sendTypingMessage(action, recipientIdentifiers); + outputResult(outputWriter, results.timestamp()); + ErrorUtils.handleSendMessageResults(results.results()); + } catch (IOException e) { throw new UserErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() .getSimpleName() + ")"); } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new UserErrorException("Failed to send to group: " + e.getMessage()); } } + + private void outputResult(final OutputWriter outputWriter, final long timestamp) { + if (outputWriter instanceof PlainTextWriter writer) { + writer.println("{}", timestamp); + } else { + final var writer = (JsonWriter) outputWriter; + writer.write(Map.of("timestamp", timestamp)); + } + } } diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index b61ca135..bde5113a 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -6,7 +6,6 @@ import org.asamk.signal.manager.AttachmentInvalidException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.NotMasterDeviceException; import org.asamk.signal.manager.StickerPackInvalidException; -import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.api.Configuration; import org.asamk.signal.manager.api.Device; import org.asamk.signal.manager.api.Group; @@ -298,31 +297,34 @@ public class DbusManagerImpl implements Manager { } @Override - public void sendTypingMessage( + public SendMessageResults sendTypingMessage( final TypingAction action, final Set recipients - ) throws IOException, UntrustedIdentityException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { - for (final var recipient : recipients) { - if (recipient instanceof RecipientIdentifier.Single) { - signal.sendTyping(((RecipientIdentifier.Single) recipient).getIdentifier(), - action == TypingAction.STOP); - } else if (recipient instanceof RecipientIdentifier.Group) { - throw new UnsupportedOperationException(); - } - } + ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { + return handleMessage(recipients, numbers -> { + numbers.forEach(n -> signal.sendTyping(n, action == TypingAction.STOP)); + return 0L; + }, () -> { + signal.sendTyping(signal.getSelfNumber(), action == TypingAction.STOP); + return 0L; + }, groupId -> { + throw new UnsupportedOperationException(); + }); } @Override - public void sendReadReceipt( + public SendMessageResults sendReadReceipt( final RecipientIdentifier.Single sender, final List messageIds - ) throws IOException, UntrustedIdentityException { + ) { signal.sendReadReceipt(sender.getIdentifier(), messageIds); + return new SendMessageResults(0, Map.of()); } @Override - public void sendViewedReceipt( + public SendMessageResults sendViewedReceipt( final RecipientIdentifier.Single sender, final List messageIds - ) throws IOException, UntrustedIdentityException { + ) { signal.sendViewedReceipt(sender.getIdentifier(), messageIds); + return new SendMessageResults(0, Map.of()); } @Override diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 812110b3..7f0c3b31 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -7,7 +7,6 @@ import org.asamk.signal.manager.AttachmentInvalidException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.NotMasterDeviceException; import org.asamk.signal.manager.StickerPackInvalidException; -import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.api.Identity; import org.asamk.signal.manager.api.InactiveGroupLinkException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; @@ -305,16 +304,15 @@ public class DbusSignalImpl implements Signal { try { var recipients = new ArrayList(1); recipients.add(recipient); - m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START, + final var results = m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START, getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream() .map(RecipientIdentifier.class::cast) .collect(Collectors.toSet())); + checkSendMessageResults(results.timestamp(), results.results()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new Error.GroupNotFound(e.getMessage()); - } catch (UntrustedIdentityException e) { - throw new Error.UntrustedIdentity(e.getMessage()); } } @@ -323,11 +321,11 @@ public class DbusSignalImpl implements Signal { final String recipient, final List messageIds ) throws Error.Failure, Error.UntrustedIdentity { try { - m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), messageIds); + final var results = m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), + messageIds); + checkSendMessageResults(results.timestamp(), results.results()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); - } catch (UntrustedIdentityException e) { - throw new Error.UntrustedIdentity(e.getMessage()); } } @@ -336,11 +334,11 @@ public class DbusSignalImpl implements Signal { final String recipient, final List messageIds ) throws Error.Failure, Error.UntrustedIdentity { try { - m.sendViewedReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), messageIds); + final var results = m.sendViewedReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), + messageIds); + checkSendMessageResults(results.timestamp(), results.results()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); - } catch (UntrustedIdentityException e) { - throw new Error.UntrustedIdentity(e.getMessage()); } } -- 2.50.1