From: AsamK Date: Mon, 22 Feb 2021 19:43:08 +0000 (+0100) Subject: Add CommandException to abstract cli return codes for errors X-Git-Tag: v0.8.1~11 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/221d937eecca5c9b09a3bee7df812203b9927a56 Add CommandException to abstract cli return codes for errors --- diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 5f0f1cdf..5b2c91c6 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -14,6 +14,9 @@ import org.asamk.signal.commands.LocalCommand; import org.asamk.signal.commands.MultiLocalCommand; import org.asamk.signal.commands.ProvisioningCommand; import org.asamk.signal.commands.RegistrationCommand; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.NotRegisteredException; import org.asamk.signal.manager.ProvisioningManager; @@ -29,9 +32,8 @@ import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; public class App { @@ -79,18 +81,16 @@ public class App { this.ns = ns; } - public int init() { + public void init() throws CommandException { var commandKey = ns.getString("command"); var command = Commands.getCommand(commandKey); if (command == null) { - logger.error("Command not implemented!"); - return 1; + throw new UserErrorException("Command not implemented!"); } OutputType outputType = ns.get("output"); if (!command.getSupportedOutputTypes().contains(outputType)) { - logger.error("Command doesn't support output type {}", outputType.toString()); - return 1; + throw new UserErrorException("Command doesn't support output type " + outputType.toString()); } var username = ns.getString("username"); @@ -99,7 +99,8 @@ public class App { final boolean useDbusSystem = ns.getBoolean("dbus_system"); if (useDbus || useDbusSystem) { // If username is null, it will connect to the default object path - return initDbusClient(command, username, useDbusSystem); + initDbusClient(command, username, useDbusSystem); + return; } final File dataPath; @@ -118,111 +119,102 @@ public class App { } if (!ServiceConfig.isSignalClientAvailable()) { - logger.error("Missing required native library dependency: libsignal-client"); - return 1; + throw new UserErrorException("Missing required native library dependency: libsignal-client"); } if (command instanceof ProvisioningCommand) { if (username != null) { - System.err.println("You cannot specify a username (phone number) when linking"); - return 1; + throw new UserErrorException("You cannot specify a username (phone number) when linking"); } - return handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceEnvironment); + handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceEnvironment); + return; } if (username == null) { var usernames = Manager.getAllLocalUsernames(dataPath); if (usernames.size() == 0) { - System.err.println("No local users found, you first need to register or link an account"); - return 1; + throw new UserErrorException("No local users found, you first need to register or link an account"); } if (command instanceof MultiLocalCommand) { - return handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceEnvironment, usernames); + handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceEnvironment, usernames); + return; } if (usernames.size() > 1) { - System.err.println("Multiple users found, you need to specify a username (phone number) with -u"); - return 1; + throw new UserErrorException( + "Multiple users found, you need to specify a username (phone number) with -u"); } username = usernames.get(0); } else if (!PhoneNumberFormatter.isValidNumber(username, null)) { - System.err.println("Invalid username (phone number), make sure you include the country code."); - return 1; + throw new UserErrorException("Invalid username (phone number), make sure you include the country code."); } if (command instanceof RegistrationCommand) { - return handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceEnvironment); + handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceEnvironment); + return; } if (!(command instanceof LocalCommand)) { - System.err.println("Command only works via dbus"); - return 1; + throw new UserErrorException("Command only works via dbus"); } - return handleLocalCommand((LocalCommand) command, username, dataPath, serviceEnvironment); + handleLocalCommand((LocalCommand) command, username, dataPath, serviceEnvironment); } - private int handleProvisioningCommand( + private void handleProvisioningCommand( final ProvisioningCommand command, final File dataPath, final ServiceEnvironment serviceEnvironment - ) { + ) throws CommandException { var pm = ProvisioningManager.init(dataPath, serviceEnvironment, BaseConfig.USER_AGENT); - return command.handleCommand(ns, pm); + command.handleCommand(ns, pm); } - private int handleRegistrationCommand( + private void handleRegistrationCommand( final RegistrationCommand command, final String username, final File dataPath, final ServiceEnvironment serviceEnvironment - ) { + ) throws CommandException { final RegistrationManager manager; try { manager = RegistrationManager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); } catch (Throwable e) { - logger.error("Error loading or creating state file: {}", e.getMessage()); - return 2; + throw new UnexpectedErrorException("Error loading or creating state file: " + e.getMessage()); } try (var m = manager) { - return command.handleCommand(ns, m); + command.handleCommand(ns, m); } catch (IOException e) { - logger.error("Cleanup failed", e); - return 2; + logger.warn("Cleanup failed", e); } } - private int handleLocalCommand( + private void handleLocalCommand( final LocalCommand command, final String username, final File dataPath, final ServiceEnvironment serviceEnvironment - ) { + ) throws CommandException { try (var m = loadManager(username, dataPath, serviceEnvironment)) { - if (m == null) { - return 2; - } - - return command.handleCommand(ns, m); + command.handleCommand(ns, m); } catch (IOException e) { - logger.error("Cleanup failed", e); - return 2; + logger.warn("Cleanup failed", e); } } - private int handleMultiLocalCommand( + private void handleMultiLocalCommand( final MultiLocalCommand command, final File dataPath, final ServiceEnvironment serviceEnvironment, final List usernames - ) { - final var managers = usernames.stream() - .map(u -> loadManager(u, dataPath, serviceEnvironment)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + ) throws CommandException { + final var managers = new ArrayList(); + for (String u : usernames) { + managers.add(loadManager(u, dataPath, serviceEnvironment)); + } - var result = command.handleCommand(ns, managers); + command.handleCommand(ns, managers); for (var m : managers) { try { @@ -231,34 +223,32 @@ public class App { logger.warn("Cleanup failed", e); } } - return result; } private Manager loadManager( final String username, final File dataPath, final ServiceEnvironment serviceEnvironment - ) { + ) throws CommandException { Manager manager; try { manager = Manager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); } catch (NotRegisteredException e) { - logger.error("User " + username + " is not registered."); - return null; + throw new UserErrorException("User " + username + " is not registered."); } catch (Throwable e) { - logger.error("Error loading state file for user " + username + ": {}", e.getMessage()); - return null; + throw new UnexpectedErrorException("Error loading state file for user " + username + ": " + e.getMessage()); } try { manager.checkAccountState(); } catch (IOException e) { - logger.error("Error while checking account " + username + ": {}", e.getMessage()); - return null; + throw new UnexpectedErrorException("Error while checking account " + username + ": " + e.getMessage()); } return manager; } - private int initDbusClient(final Command command, final String username, final boolean systemBus) { + private void initDbusClient( + final Command command, final String username, final boolean systemBus + ) throws CommandException { try { DBusConnection.DBusBusType busType; if (systemBus) { @@ -271,22 +261,21 @@ public class App { DbusConfig.getObjectPath(username), Signal.class); - return handleCommand(command, ts, dBusConn); + handleCommand(command, ts, dBusConn); } } catch (DBusException | IOException e) { logger.error("Dbus client failed", e); - return 2; + throw new UnexpectedErrorException("Dbus client failed"); } } - private int handleCommand(Command command, Signal ts, DBusConnection dBusConn) { + private void handleCommand(Command command, Signal ts, DBusConnection dBusConn) throws CommandException { if (command instanceof ExtendedDbusCommand) { - return ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn); + ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn); } else if (command instanceof DbusCommand) { - return ((DbusCommand) command).handleCommand(ns, ts); + ((DbusCommand) command).handleCommand(ns, ts); } else { - System.err.println("Command is not yet implemented via dbus"); - return 1; + throw new UserErrorException("Command is not yet implemented via dbus"); } } diff --git a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java index 38657f5e..c8e1c24f 100644 --- a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java @@ -8,7 +8,6 @@ import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import java.io.IOException; import java.util.HashMap; public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler { @@ -32,10 +31,7 @@ public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler if (envelope != null) { object.put("envelope", new JsonMessageEnvelope(envelope, content, m)); } - try { - jsonWriter.write(object); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - } + + jsonWriter.write(object); } } diff --git a/src/main/java/org/asamk/signal/JsonWriter.java b/src/main/java/org/asamk/signal/JsonWriter.java index 8aed4487..e7549adf 100644 --- a/src/main/java/org/asamk/signal/JsonWriter.java +++ b/src/main/java/org/asamk/signal/JsonWriter.java @@ -26,14 +26,18 @@ public class JsonWriter { objectMapper.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); } - public void write(final Object object) throws IOException { + public void write(final Object object) { try { - objectMapper.writeValue(writer, object); - } catch (JsonProcessingException e) { - // Some issue with json serialization, probably caused by a bug + try { + objectMapper.writeValue(writer, object); + } catch (JsonProcessingException e) { + // Some issue with json serialization, probably caused by a bug + throw new AssertionError(e); + } + writer.write(System.lineSeparator()); + writer.flush(); + } catch (IOException e) { throw new AssertionError(e); } - writer.write(System.lineSeparator()); - writer.flush(); } } diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 88ae06ca..775b5223 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -21,6 +21,11 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.ArgumentParserException; import net.sourceforge.argparse4j.inf.Namespace; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UntrustedKeyErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.LibSignalLogger; import org.asamk.signal.util.SecurityProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -39,8 +44,14 @@ public class Main { var ns = parser.parseArgsOrFail(args); - var res = new App(ns).init(); - System.exit(res); + int status = 0; + try { + new App(ns).init(); + } catch (CommandException e) { + System.err.println(e.getMessage()); + status = getStatusForError(e); + } + System.exit(status); } private static void installSecurityProviderWorkaround() { @@ -78,4 +89,18 @@ public class Main { System.setProperty("org.slf4j.simpleLogger.showDateTime", "false"); } } + + private static int getStatusForError(final CommandException e) { + if (e instanceof UserErrorException) { + return 1; + } else if (e instanceof UnexpectedErrorException) { + return 2; + } else if (e instanceof IOErrorException) { + return 3; + } else if (e instanceof UntrustedKeyErrorException) { + return 4; + } else { + return 2; + } + } } diff --git a/src/main/java/org/asamk/signal/PlainTextWriter.java b/src/main/java/org/asamk/signal/PlainTextWriter.java index 91a4dbba..de738de9 100644 --- a/src/main/java/org/asamk/signal/PlainTextWriter.java +++ b/src/main/java/org/asamk/signal/PlainTextWriter.java @@ -1,23 +1,21 @@ package org.asamk.signal; -import java.io.IOException; - public interface PlainTextWriter { - void println(String format, Object... args) throws IOException; + void println(String format, Object... args); PlainTextWriter indentedWriter(); - default void println() throws IOException { + default void println() { println(""); } - default void indent(final WriterConsumer subWriter) throws IOException { + default void indent(final WriterConsumer subWriter) { subWriter.consume(indentedWriter()); } interface WriterConsumer { - void consume(PlainTextWriter writer) throws IOException; + void consume(PlainTextWriter writer); } } diff --git a/src/main/java/org/asamk/signal/PlainTextWriterImpl.java b/src/main/java/org/asamk/signal/PlainTextWriterImpl.java index d6536481..bb18b7f3 100644 --- a/src/main/java/org/asamk/signal/PlainTextWriterImpl.java +++ b/src/main/java/org/asamk/signal/PlainTextWriterImpl.java @@ -19,12 +19,16 @@ public final class PlainTextWriterImpl implements PlainTextWriter { } @Override - public void println(String format, Object... args) throws IOException { + public void println(String format, Object... args) { final var message = MessageFormatter.arrayFormat(format, args).getMessage(); - writer.write(message); - writer.write(System.lineSeparator()); - writer.flush(); + try { + writer.write(message); + writer.write(System.lineSeparator()); + writer.flush(); + } catch (IOException e) { + throw new AssertionError(e); + } } @Override @@ -51,8 +55,12 @@ public final class PlainTextWriterImpl implements PlainTextWriter { } @Override - public void println(final String format, final Object... args) throws IOException { - writer.write(spaces); + public void println(final String format, final Object... args) { + try { + writer.write(spaces); + } catch (IOException e) { + throw new AssertionError(e); + } plainTextWriter.println(format, args); } diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 8c75fcba..6df283f3 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -630,7 +630,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength()); } - private void printAttachment(PlainTextWriter writer, SignalServiceAttachment attachment) throws IOException { + private void printAttachment(PlainTextWriter writer, SignalServiceAttachment attachment) { writer.println("Content-Type: {}", attachment.getContentType()); writer.println("Type: {}", attachment.isPointer() ? "Pointer" : attachment.isStream() ? "Stream" : ""); if (attachment.isPointer()) { diff --git a/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java b/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java index c5d18ab1..cf993e6d 100644 --- a/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java +++ b/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java @@ -3,7 +3,13 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidKeyException; import java.io.IOException; @@ -14,6 +20,8 @@ import static org.asamk.signal.util.ErrorUtils.handleAssertionError; public class AddDeviceCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(AddDeviceCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("--uri") @@ -22,19 +30,20 @@ public class AddDeviceCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { m.addDeviceLink(new URI(ns.getString("uri"))); - return 0; } catch (IOException e) { - e.printStackTrace(); - return 3; - } catch (InvalidKeyException | URISyntaxException e) { - e.printStackTrace(); - return 2; + logger.error("Add device link failed", e); + throw new IOErrorException("Add device link failed"); + } catch (URISyntaxException e) { + throw new UserErrorException("Device link uri has invalid format: {}" + e.getMessage()); + } catch (InvalidKeyException e) { + logger.error("Add device link failed", e); + throw new UnexpectedErrorException("Add device link failed."); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } } } diff --git a/src/main/java/org/asamk/signal/commands/BlockCommand.java b/src/main/java/org/asamk/signal/commands/BlockCommand.java index 9dff25c6..98fce667 100644 --- a/src/main/java/org/asamk/signal/commands/BlockCommand.java +++ b/src/main/java/org/asamk/signal/commands/BlockCommand.java @@ -7,10 +7,14 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.util.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.util.InvalidNumberException; public class BlockCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(BlockCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("contact").help("Contact number").nargs("*"); @@ -19,12 +23,12 @@ public class BlockCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) { for (var contact_number : ns.getList("contact")) { try { m.setContactBlocked(contact_number, true); } catch (InvalidNumberException e) { - System.err.println(e.getMessage()); + logger.warn("Invalid number {}: {}", contact_number, e.getMessage()); } } @@ -34,11 +38,9 @@ public class BlockCommand implements LocalCommand { var groupId = Util.decodeGroupId(groupIdString); m.setGroupBlocked(groupId, true); } catch (GroupIdFormatException | GroupNotFoundException e) { - System.err.println(e.getMessage()); + logger.warn("Invalid group id {}: {}", groupIdString, e.getMessage()); } } } - - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 6ee4f316..8d26e452 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -8,6 +8,8 @@ import org.asamk.signal.DbusConfig; import org.asamk.signal.DbusReceiveMessageHandler; import org.asamk.signal.JsonDbusReceiveMessageHandler; import org.asamk.signal.OutputType; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.dbus.DbusSignalImpl; import org.asamk.signal.manager.Manager; import org.freedesktop.dbus.connections.impl.DBusConnection; @@ -44,7 +46,7 @@ public class DaemonCommand implements MultiLocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -71,15 +73,14 @@ public class DaemonCommand implements MultiLocalCommand { t.join(); } catch (InterruptedException ignored) { } - return 0; } catch (DBusException | IOException e) { logger.error("Dbus command failed", e); - return 2; + throw new UnexpectedErrorException("Dbus command failed"); } } @Override - public int handleCommand(final Namespace ns, final List managers) { + public void handleCommand(final Namespace ns, final List managers) throws CommandException { var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -112,10 +113,9 @@ public class DaemonCommand implements MultiLocalCommand { } catch (InterruptedException ignored) { } } - return 0; } catch (DBusException | IOException e) { logger.error("Dbus command failed", e); - return 2; + throw new UnexpectedErrorException("Dbus command failed"); } } diff --git a/src/main/java/org/asamk/signal/commands/DbusCommand.java b/src/main/java/org/asamk/signal/commands/DbusCommand.java index 1b3a0268..e4c78c84 100644 --- a/src/main/java/org/asamk/signal/commands/DbusCommand.java +++ b/src/main/java/org/asamk/signal/commands/DbusCommand.java @@ -3,14 +3,15 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import org.asamk.Signal; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.dbus.DbusSignalImpl; import org.asamk.signal.manager.Manager; public interface DbusCommand extends LocalCommand { - int handleCommand(Namespace ns, Signal signal); + void handleCommand(Namespace ns, Signal signal) throws CommandException; - default int handleCommand(final Namespace ns, final Manager m) { - return handleCommand(ns, new DbusSignalImpl(m)); + default void handleCommand(final Namespace ns, final Manager m) throws CommandException { + handleCommand(ns, new DbusSignalImpl(m)); } } diff --git a/src/main/java/org/asamk/signal/commands/ExtendedDbusCommand.java b/src/main/java/org/asamk/signal/commands/ExtendedDbusCommand.java index f9cd9de8..1d454f4d 100644 --- a/src/main/java/org/asamk/signal/commands/ExtendedDbusCommand.java +++ b/src/main/java/org/asamk/signal/commands/ExtendedDbusCommand.java @@ -3,9 +3,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import org.asamk.Signal; +import org.asamk.signal.commands.exceptions.CommandException; import org.freedesktop.dbus.connections.impl.DBusConnection; public interface ExtendedDbusCommand extends Command { - int handleCommand(Namespace ns, Signal signal, DBusConnection dbusconnection); + void handleCommand(Namespace ns, Signal signal, DBusConnection dbusconnection) throws CommandException; } diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 07de8321..69140b23 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -7,6 +7,8 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +38,7 @@ public class GetUserStatusCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { // Setup the json object mapper var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); @@ -50,8 +52,8 @@ public class GetUserStatusCommand implements LocalCommand { try { registered = m.areUsersRegistered(new HashSet<>(ns.getList("number"))); } catch (IOException e) { - System.err.println("Unable to check if users are registered"); - return 3; + logger.debug("Failed to check registered users", e); + throw new IOErrorException("Unable to check if users are registered"); } // Output @@ -63,26 +65,14 @@ public class GetUserStatusCommand implements LocalCommand { .map(entry -> new JsonUserStatus(entry.getKey(), entry.getValue())) .collect(Collectors.toList()); - try { - jsonWriter.write(jsonUserStatuses); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - return 3; - } + jsonWriter.write(jsonUserStatuses); } else { final var writer = new PlainTextWriterImpl(System.out); - try { - for (var entry : registered.entrySet()) { - writer.println("{}: {}", entry.getKey(), entry.getValue()); - } - } catch (IOException e) { - e.printStackTrace(); - return 3; + for (var entry : registered.entrySet()) { + writer.println("{}: {}", entry.getKey(), entry.getValue()); } } - - return 0; } private static final class JsonUserStatus { diff --git a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java index 7f1cb0b0..e59ecec6 100644 --- a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java @@ -4,46 +4,42 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.freedesktop.dbus.exceptions.DBusExecutionException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException; import java.io.IOException; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleIOException; import static org.asamk.signal.util.ErrorUtils.handleTimestampAndSendMessageResults; public class JoinGroupCommand implements LocalCommand { - private final static Logger logger = LoggerFactory.getLogger(JoinGroupCommand.class); - @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("--uri").required(true).help("Specify the uri with the group invitation link."); } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { final GroupInviteLinkUrl linkUrl; var uri = ns.getString("uri"); try { linkUrl = GroupInviteLinkUrl.fromUri(uri); } catch (GroupInviteLinkUrl.InvalidGroupLinkException e) { - System.err.println("Group link is invalid: " + e.getMessage()); - return 1; + throw new UserErrorException("Group link is invalid: " + e.getMessage()); } catch (GroupInviteLinkUrl.UnknownGroupLinkVersionException e) { - System.err.println("Group link was created with an incompatible version: " + e.getMessage()); - return 1; + throw new UserErrorException("Group link was created with an incompatible version: " + e.getMessage()); } if (linkUrl == null) { - System.err.println("Link is not a signal group invitation link"); - return 1; + throw new UserErrorException("Link is not a signal group invitation link"); } try { @@ -56,23 +52,18 @@ public class JoinGroupCommand implements LocalCommand { } else { writer.println("Joined group \"{}\"", newGroupId.toBase64()); } - return handleTimestampAndSendMessageResults(writer, 0, results.second()); + handleTimestampAndSendMessageResults(writer, 0, results.second()); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (GroupPatchNotAcceptedException e) { - System.err.println("Failed to join group, maybe already a member"); - return 1; + throw new UserErrorException("Failed to join group, maybe already a member"); } catch (IOException e) { - e.printStackTrace(); - handleIOException(e); - return 3; + throw new IOErrorException("Failed to send message: " + e.getMessage()); } catch (DBusExecutionException e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 2; + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); } catch (GroupLinkNotActiveException e) { - System.err.println("Group link is not valid: " + e.getMessage()); - return 1; + throw new UserErrorException("Group link is not valid: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/LinkCommand.java b/src/main/java/org/asamk/signal/commands/LinkCommand.java index fe580916..34775773 100644 --- a/src/main/java/org/asamk/signal/commands/LinkCommand.java +++ b/src/main/java/org/asamk/signal/commands/LinkCommand.java @@ -4,6 +4,10 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.UserAlreadyExists; import org.slf4j.Logger; @@ -25,7 +29,7 @@ public class LinkCommand implements ProvisioningCommand { } @Override - public int handleCommand(final Namespace ns, final ProvisioningManager m) { + public void handleCommand(final Namespace ns, final ProvisioningManager m) throws CommandException { final var writer = new PlainTextWriterImpl(System.out); var deviceName = ns.getString("name"); @@ -37,25 +41,21 @@ public class LinkCommand implements ProvisioningCommand { var username = m.finishDeviceLink(deviceName); writer.println("Associated with: {}", username); } catch (TimeoutException e) { - System.err.println("Link request timed out, please try again."); - return 3; + throw new UserErrorException("Link request timed out, please try again."); } catch (IOException e) { - System.err.println("Link request error: " + e.getMessage()); - return 3; + throw new IOErrorException("Link request error: " + e.getMessage()); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (InvalidKeyException e) { - e.printStackTrace(); - return 2; + logger.debug("Finish device link failed", e); + throw new UnexpectedErrorException("Invalid key: " + e.getMessage()); } catch (UserAlreadyExists e) { - System.err.println("The user " + throw new UserErrorException("The user " + e.getUsername() + " already exists\nDelete \"" + e.getFileName() + "\" before trying again."); - return 1; } - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index 6609ec60..4b27e17d 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -6,8 +6,6 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; -import java.io.IOException; - public class ListContactsCommand implements LocalCommand { @Override @@ -15,18 +13,12 @@ public class ListContactsCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) { final var writer = new PlainTextWriterImpl(System.out); var contacts = m.getContacts(); - try { - for (var c : contacts) { - writer.println("Number: {} Name: {} Blocked: {}", c.number, c.name, c.blocked); - } - } catch (IOException e) { - e.printStackTrace(); - return 3; + for (var c : contacts) { + writer.println("Number: {} Name: {} Blocked: {}", c.number, c.name, c.blocked); } - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java index f2037239..7165d07c 100644 --- a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java @@ -4,34 +4,44 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.util.DateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; import java.io.IOException; +import java.util.List; public class ListDevicesCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(ListDevicesCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { final var writer = new PlainTextWriterImpl(System.out); + + List devices; try { - var devices = m.getLinkedDevices(); - for (var d : devices) { - writer.println("- Device {}{}:", d.getId(), (d.getId() == m.getDeviceId() ? " (this device)" : "")); - writer.indent(w -> { - w.println("Name: {}", d.getName()); - w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated())); - w.println("Last seen: {}", DateUtils.formatTimestamp(d.getLastSeen())); - }); - } - return 0; + devices = m.getLinkedDevices(); } catch (IOException e) { - e.printStackTrace(); - return 3; + logger.debug("Failed to get linked devices", e); + throw new IOErrorException("Failed to get linked devices: " + e.getMessage()); + } + + for (var d : devices) { + writer.println("- Device {}{}:", d.getId(), (d.getId() == m.getDeviceId() ? " (this device)" : "")); + writer.indent(w -> { + w.println("Name: {}", d.getName()); + w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated())); + w.println("Last seen: {}", DateUtils.formatTimestamp(d.getLastSeen())); + }); } } } diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 477d56a1..a547cc15 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -8,13 +8,13 @@ import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.PlainTextWriter; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import java.io.IOException; import java.util.ArrayList; import java.util.Set; import java.util.stream.Collectors; @@ -32,7 +32,7 @@ public class ListGroupsCommand implements LocalCommand { private static void printGroupPlainText( PlainTextWriter writer, Manager m, GroupInfo group, boolean detailed - ) throws IOException { + ) { if (detailed) { final var groupInviteLink = group.getGroupInviteLink(); @@ -70,7 +70,7 @@ public class ListGroupsCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { if (ns.get("output") == OutputType.JSON) { final var jsonWriter = new JsonWriter(System.out); @@ -88,28 +88,14 @@ public class ListGroupsCommand implements LocalCommand { groupInviteLink == null ? null : groupInviteLink.getUrl())); } - try { - jsonWriter.write(jsonGroups); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - return 3; - } - - return 0; + jsonWriter.write(jsonGroups); } else { final var writer = new PlainTextWriterImpl(System.out); boolean detailed = ns.getBoolean("detailed"); - try { - for (var group : m.getGroups()) { - printGroupPlainText(writer, m, group, detailed); - } - } catch (IOException e) { - e.printStackTrace(); - return 3; + for (var group : m.getGroups()) { + printGroupPlainText(writer, m, group, detailed); } } - - return 0; } private static final class JsonGroup { diff --git a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java index 1f1b6258..dc2d92fb 100644 --- a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java @@ -5,6 +5,8 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriter; import org.asamk.signal.PlainTextWriterImpl; +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.storage.protocol.IdentityInfo; import org.asamk.signal.util.Hex; @@ -13,7 +15,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.util.InvalidNumberException; -import java.io.IOException; +import java.util.List; public class ListIdentitiesCommand implements LocalCommand { @@ -21,16 +23,12 @@ public class ListIdentitiesCommand implements LocalCommand { private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, IdentityInfo theirId) { var digits = Util.formatSafetyNumber(m.computeSafetyNumber(theirId.getAddress(), theirId.getIdentityKey())); - try { - writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}", - theirId.getAddress().getNumber().orNull(), - theirId.getTrustLevel(), - theirId.getDateAdded(), - Hex.toString(theirId.getFingerprint()), - digits); - } catch (IOException e) { - e.printStackTrace(); - } + writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}", + theirId.getAddress().getNumber().orNull(), + theirId.getTrustLevel(), + theirId.getDateAdded(), + Hex.toString(theirId.getFingerprint()), + digits); } @Override @@ -39,24 +37,27 @@ public class ListIdentitiesCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { final var writer = new PlainTextWriterImpl(System.out); - if (ns.get("number") == null) { + var number = ns.getString("number"); + + if (number == null) { for (var identity : m.getIdentities()) { printIdentityFingerprint(writer, m, identity); } - } else { - var number = ns.getString("number"); - try { - var identities = m.getIdentities(number); - for (var id : identities) { - printIdentityFingerprint(writer, m, id); - } - } catch (InvalidNumberException e) { - System.err.println("Invalid number: " + e.getMessage()); - } + return; + } + + List identities; + try { + identities = m.getIdentities(number); + } catch (InvalidNumberException e) { + throw new UserErrorException("Invalid number: " + e.getMessage()); + } + + for (var id : identities) { + printIdentityFingerprint(writer, m, id); } - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/LocalCommand.java b/src/main/java/org/asamk/signal/commands/LocalCommand.java index 7bcb1a4b..a7c64dc1 100644 --- a/src/main/java/org/asamk/signal/commands/LocalCommand.java +++ b/src/main/java/org/asamk/signal/commands/LocalCommand.java @@ -2,9 +2,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; public interface LocalCommand extends Command { - int handleCommand(Namespace ns, Manager m); + void handleCommand(Namespace ns, Manager m) throws CommandException; } diff --git a/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java b/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java index e8ee8e1d..2a8457bd 100644 --- a/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java +++ b/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java @@ -2,16 +2,17 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; import java.util.List; public interface MultiLocalCommand extends LocalCommand { - int handleCommand(Namespace ns, List m); + void handleCommand(Namespace ns, List m) throws CommandException; @Override - default int handleCommand(final Namespace ns, final Manager m) { - return handleCommand(ns, List.of(m)); + default void handleCommand(final Namespace ns, final Manager m) throws CommandException { + handleCommand(ns, List.of(m)); } } diff --git a/src/main/java/org/asamk/signal/commands/ProvisioningCommand.java b/src/main/java/org/asamk/signal/commands/ProvisioningCommand.java index 12a612ff..354e4af3 100644 --- a/src/main/java/org/asamk/signal/commands/ProvisioningCommand.java +++ b/src/main/java/org/asamk/signal/commands/ProvisioningCommand.java @@ -2,9 +2,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.ProvisioningManager; public interface ProvisioningCommand extends Command { - int handleCommand(Namespace ns, ProvisioningManager m); + void handleCommand(Namespace ns, ProvisioningManager m) throws CommandException; } diff --git a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java index cdbccf19..d8a86585 100644 --- a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java @@ -4,7 +4,11 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; @@ -13,10 +17,6 @@ import org.asamk.signal.util.Util; import java.io.IOException; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; -import static org.asamk.signal.util.ErrorUtils.handleGroupNotFoundException; -import static org.asamk.signal.util.ErrorUtils.handleIOException; -import static org.asamk.signal.util.ErrorUtils.handleNotAGroupMemberException; import static org.asamk.signal.util.ErrorUtils.handleTimestampAndSendMessageResults; public class QuitGroupCommand implements LocalCommand { @@ -27,28 +27,28 @@ public class QuitGroupCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { + final var writer = new PlainTextWriterImpl(System.out); + + final GroupId groupId; try { - final var writer = new PlainTextWriterImpl(System.out); + groupId = Util.decodeGroupId(ns.getString("group")); + } catch (GroupIdFormatException e) { + throw new UserErrorException("Invalid group id:" + e.getMessage()); + } - final var groupId = Util.decodeGroupId(ns.getString("group")); + try { final var results = m.sendQuitGroupMessage(groupId); - return handleTimestampAndSendMessageResults(writer, results.first(), results.second()); + handleTimestampAndSendMessageResults(writer, results.first(), results.second()); } catch (IOException e) { - handleIOException(e); - return 3; + throw new IOErrorException("Failed to send message: " + e.getMessage()); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (GroupNotFoundException e) { - handleGroupNotFoundException(e); - return 1; + throw new UserErrorException("Failed to send to group: " + e.getMessage()); } catch (NotAGroupMemberException e) { - handleNotAGroupMemberException(e); - return 1; - } catch (GroupIdFormatException e) { - handleGroupIdFormatException(e); - return 1; + throw new UserErrorException("Failed to send to group: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 834bc5eb..8612a71b 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -10,6 +10,9 @@ import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.ReceiveMessageHandler; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.json.JsonMessageEnvelope; import org.asamk.signal.manager.Manager; import org.asamk.signal.util.DateUtils; @@ -48,7 +51,9 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { return Set.of(OutputType.PLAIN_TEXT, OutputType.JSON); } - public int handleCommand(final Namespace ns, final Signal signal, DBusConnection dbusconnection) { + public void handleCommand( + final Namespace ns, final Signal signal, DBusConnection dbusconnection + ) throws CommandException { var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -63,106 +68,81 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { dbusconnection.addSigHandler(Signal.MessageReceived.class, messageReceived -> { var envelope = new JsonMessageEnvelope(messageReceived); final var object = Map.of("envelope", envelope); - try { - jsonWriter.write(object); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - } + jsonWriter.write(object); }); dbusconnection.addSigHandler(Signal.ReceiptReceived.class, receiptReceived -> { var envelope = new JsonMessageEnvelope(receiptReceived); final var object = Map.of("envelope", envelope); - try { - jsonWriter.write(object); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - } + jsonWriter.write(object); }); dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, syncReceived -> { var envelope = new JsonMessageEnvelope(syncReceived); final var object = Map.of("envelope", envelope); - try { - jsonWriter.write(object); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - } + jsonWriter.write(object); }); } else { final var writer = new PlainTextWriterImpl(System.out); dbusconnection.addSigHandler(Signal.MessageReceived.class, messageReceived -> { - try { - writer.println("Envelope from: {}", messageReceived.getSender()); - writer.println("Timestamp: {}", DateUtils.formatTimestamp(messageReceived.getTimestamp())); - writer.println("Body: {}", messageReceived.getMessage()); - if (messageReceived.getGroupId().length > 0) { - writer.println("Group info:"); - writer.indentedWriter() - .println("Id: {}", - Base64.getEncoder().encodeToString(messageReceived.getGroupId())); - } - if (messageReceived.getAttachments().size() > 0) { - writer.println("Attachments:"); - for (var attachment : messageReceived.getAttachments()) { - writer.println("- Stored plaintext in: {}", attachment); - } + writer.println("Envelope from: {}", messageReceived.getSender()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(messageReceived.getTimestamp())); + writer.println("Body: {}", messageReceived.getMessage()); + if (messageReceived.getGroupId().length > 0) { + writer.println("Group info:"); + writer.indentedWriter() + .println("Id: {}", Base64.getEncoder().encodeToString(messageReceived.getGroupId())); + } + if (messageReceived.getAttachments().size() > 0) { + writer.println("Attachments:"); + for (var attachment : messageReceived.getAttachments()) { + writer.println("- Stored plaintext in: {}", attachment); } - writer.println(); - } catch (IOException e) { - e.printStackTrace(); } + writer.println(); }); dbusconnection.addSigHandler(Signal.ReceiptReceived.class, receiptReceived -> { - try { - writer.println("Receipt from: {}", receiptReceived.getSender()); - writer.println("Timestamp: {}", DateUtils.formatTimestamp(receiptReceived.getTimestamp())); - } catch (IOException e) { - e.printStackTrace(); - } + writer.println("Receipt from: {}", receiptReceived.getSender()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(receiptReceived.getTimestamp())); }); dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, syncReceived -> { - try { - writer.println("Sync Envelope from: {} to: {}", - syncReceived.getSource(), - syncReceived.getDestination()); - writer.println("Timestamp: {}", DateUtils.formatTimestamp(syncReceived.getTimestamp())); - writer.println("Body: {}", syncReceived.getMessage()); - if (syncReceived.getGroupId().length > 0) { - writer.println("Group info:"); - writer.indentedWriter() - .println("Id: {}", Base64.getEncoder().encodeToString(syncReceived.getGroupId())); - } - if (syncReceived.getAttachments().size() > 0) { - writer.println("Attachments:"); - for (var attachment : syncReceived.getAttachments()) { - writer.println("- Stored plaintext in: {}", attachment); - } + writer.println("Sync Envelope from: {} to: {}", + syncReceived.getSource(), + syncReceived.getDestination()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(syncReceived.getTimestamp())); + writer.println("Body: {}", syncReceived.getMessage()); + if (syncReceived.getGroupId().length > 0) { + writer.println("Group info:"); + writer.indentedWriter() + .println("Id: {}", Base64.getEncoder().encodeToString(syncReceived.getGroupId())); + } + if (syncReceived.getAttachments().size() > 0) { + writer.println("Attachments:"); + for (var attachment : syncReceived.getAttachments()) { + writer.println("- Stored plaintext in: {}", attachment); } - writer.println(); - } catch (IOException e) { - e.printStackTrace(); } + writer.println(); }); } } catch (DBusException e) { - e.printStackTrace(); - return 2; + logger.error("Dbus client failed", e); + throw new UnexpectedErrorException("Dbus client failed"); } while (true) { try { Thread.sleep(10000); - } catch (InterruptedException e) { - return 0; + } catch (InterruptedException ignored) { + return; } } } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -187,13 +167,11 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { returnOnTimeout, ignoreAttachments, handler); - return 0; } catch (IOException e) { - System.err.println("Error while receiving messages: " + e.getMessage()); - return 3; + throw new IOErrorException("Error while receiving messages: " + e.getMessage()); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } } } diff --git a/src/main/java/org/asamk/signal/commands/RegisterCommand.java b/src/main/java/org/asamk/signal/commands/RegisterCommand.java index d656fe0e..a4c613af 100644 --- a/src/main/java/org/asamk/signal/commands/RegisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegisterCommand.java @@ -4,6 +4,9 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.RegistrationManager; import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException; @@ -21,26 +24,25 @@ public class RegisterCommand implements RegistrationCommand { } @Override - public int handleCommand(final Namespace ns, final RegistrationManager m) { + public void handleCommand(final Namespace ns, final RegistrationManager m) throws CommandException { final boolean voiceVerification = ns.getBoolean("voice"); final var captcha = ns.getString("captcha"); try { m.register(voiceVerification, captcha); - return 0; } catch (CaptchaRequiredException e) { + String message; if (captcha == null) { - System.err.println("Captcha required for verification, use --captcha CAPTCHA"); - System.err.println("To get the token, go to https://signalcaptchas.org/registration/generate.html"); - System.err.println("Check the developer tools (F12) console for a failed redirect to signalcaptcha://"); - System.err.println("Everything after signalcaptcha:// is the captcha token."); + message = "Captcha required for verification, use --captcha CAPTCHA\n" + + "To get the token, go to https://signalcaptchas.org/registration/generate.html\n" + + "Check the developer tools (F12) console for a failed redirect to signalcaptcha://\n" + + "Everything after signalcaptcha:// is the captcha token."; } else { - System.err.println("Invalid captcha given."); + message = "Invalid captcha given."; } - return 1; + throw new UserErrorException(message); } catch (IOException e) { - System.err.println("Request verify error: " + e.getMessage()); - return 3; + throw new IOErrorException("Request verify error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/RegistrationCommand.java b/src/main/java/org/asamk/signal/commands/RegistrationCommand.java index 8683570f..425ac71d 100644 --- a/src/main/java/org/asamk/signal/commands/RegistrationCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegistrationCommand.java @@ -2,9 +2,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.RegistrationManager; public interface RegistrationCommand extends Command { - int handleCommand(Namespace ns, RegistrationManager m); + void handleCommand(Namespace ns, RegistrationManager m) throws CommandException; } diff --git a/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java b/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java index 78d14bbd..c9be92e8 100644 --- a/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java @@ -3,6 +3,8 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import java.io.IOException; @@ -18,14 +20,12 @@ public class RemoveDeviceCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { int deviceId = ns.getInt("deviceId"); m.removeLinkedDevices(deviceId); - return 0; } catch (IOException e) { - e.printStackTrace(); - return 3; + throw new IOErrorException("Error while removing device: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java index 52b111ea..03d8d7cb 100644 --- a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java @@ -3,6 +3,9 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.manager.Manager; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; @@ -16,16 +19,13 @@ public class RemovePinCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { m.setRegistrationLockPin(Optional.absent()); - return 0; } catch (UnauthenticatedResponseException e) { - System.err.println("Remove pin error: " + e.getMessage()); - return 2; + throw new UnexpectedErrorException("Remove pin failed with unauthenticated response: " + e.getMessage()); } catch (IOException e) { - System.err.println("Remove pin error: " + e.getMessage()); - return 3; + throw new IOErrorException("Remove pin error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 1976fb60..23d74165 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -6,6 +6,10 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UntrustedKeyErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.Util; @@ -19,7 +23,6 @@ import java.nio.charset.Charset; import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; public class SendCommand implements DbusCommand { @@ -42,7 +45,7 @@ public class SendCommand implements DbusCommand { } @Override - public int handleCommand(final Namespace ns, final Signal signal) { + public void handleCommand(final Namespace ns, final Signal signal) throws CommandException { final List recipients = ns.getList("recipient"); final var isEndSession = ns.getBoolean("endsession"); final var groupIdString = ns.getString("group"); @@ -50,32 +53,27 @@ public class SendCommand implements DbusCommand { final var noRecipients = recipients == null || recipients.isEmpty(); if ((noRecipients && isEndSession) || (noRecipients && groupIdString == null && !isNoteToSelf)) { - System.err.println("No recipients given"); - System.err.println("Aborting sending."); - return 1; + throw new UserErrorException("No recipients given"); } if (!noRecipients && groupIdString != null) { - System.err.println("You cannot specify recipients by phone number and groups at the same time"); - return 1; + throw new UserErrorException("You cannot specify recipients by phone number and groups at the same time"); } if (!noRecipients && isNoteToSelf) { - System.err.println("You cannot specify recipients by phone number and not to self at the same time"); - return 1; + throw new UserErrorException( + "You cannot specify recipients by phone number and not to self at the same time"); } if (isEndSession) { try { signal.sendEndSessionMessage(recipients); - return 0; + return; } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (Signal.Error.UntrustedIdentity e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 4; + throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage()); } catch (DBusExecutionException e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 2; + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); } } @@ -84,9 +82,7 @@ public class SendCommand implements DbusCommand { try { messageText = IOUtils.readAll(System.in, Charset.defaultCharset()); } catch (IOException e) { - System.err.println("Failed to read message from stdin: " + e.getMessage()); - System.err.println("Aborting sending."); - return 1; + throw new UserErrorException("Failed to read message from stdin: " + e.getMessage()); } } @@ -102,23 +98,18 @@ public class SendCommand implements DbusCommand { try { groupId = Util.decodeGroupId(groupIdString).serialize(); } catch (GroupIdFormatException e) { - handleGroupIdFormatException(e); - return 1; + throw new UserErrorException("Invalid group id:" + e.getMessage()); } try { var timestamp = signal.sendGroupMessage(messageText, attachments, groupId); writer.println("{}", timestamp); - return 0; + return; } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (DBusExecutionException e) { - System.err.println("Failed to send group message: " + e.getMessage()); - return 2; - } catch (IOException e) { - e.printStackTrace(); - return 3; + throw new UnexpectedErrorException("Failed to send group message: " + e.getMessage()); } } @@ -126,41 +117,29 @@ public class SendCommand implements DbusCommand { try { var timestamp = signal.sendNoteToSelfMessage(messageText, attachments); writer.println("{}", timestamp); - return 0; + return; } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (Signal.Error.UntrustedIdentity e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 4; + throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage()); } catch (DBusExecutionException e) { - System.err.println("Failed to send note to self message: " + e.getMessage()); - return 2; - } catch (IOException e) { - e.printStackTrace(); - return 3; + throw new UnexpectedErrorException("Failed to send note to self message: " + e.getMessage()); } } try { var timestamp = signal.sendMessage(messageText, attachments, recipients); writer.println("{}", timestamp); - return 0; } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (UnknownObject e) { - System.err.println("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); - return 1; + throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); } catch (Signal.Error.UntrustedIdentity e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 4; + throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage()); } catch (DBusExecutionException e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 2; - } catch (IOException e) { - e.printStackTrace(); - return 3; + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java index f5eacf81..176f1bb9 100644 --- a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java @@ -3,6 +3,9 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UntrustedKeyErrorException; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; @@ -16,16 +19,13 @@ public class SendContactsCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { m.sendContacts(); - return 0; } catch (UntrustedIdentityException e) { - System.err.println("SendContacts error: " + e.getMessage()); - return 2; + throw new UntrustedKeyErrorException("SendContacts error: " + e.getMessage()); } catch (IOException e) { - System.err.println("SendContacts error: " + e.getMessage()); - return 3; + throw new IOErrorException("SendContacts error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index bcae9d41..b9cbc047 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -5,7 +5,11 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; @@ -18,11 +22,6 @@ import java.io.IOException; import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; -import static org.asamk.signal.util.ErrorUtils.handleGroupNotFoundException; -import static org.asamk.signal.util.ErrorUtils.handleIOException; -import static org.asamk.signal.util.ErrorUtils.handleInvalidNumberException; -import static org.asamk.signal.util.ErrorUtils.handleNotAGroupMemberException; import static org.asamk.signal.util.ErrorUtils.handleTimestampAndSendMessageResults; public class SendReactionCommand implements LocalCommand { @@ -46,19 +45,16 @@ public class SendReactionCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { final List recipients = ns.getList("recipient"); final var groupIdString = ns.getString("group"); final var noRecipients = recipients == null || recipients.isEmpty(); if (noRecipients && groupIdString == null) { - System.err.println("No recipients given"); - System.err.println("Aborting sending."); - return 1; + throw new UserErrorException("No recipients given"); } if (!noRecipients && groupIdString != null) { - System.err.println("You cannot specify recipients by phone number and groups at the same time"); - return 1; + throw new UserErrorException("You cannot specify recipients by phone number and groups at the same time"); } final var emoji = ns.getString("emoji"); @@ -66,35 +62,37 @@ public class SendReactionCommand implements LocalCommand { final var targetAuthor = ns.getString("target_author"); final long targetTimestamp = ns.getLong("target_timestamp"); - try { - final var writer = new PlainTextWriterImpl(System.out); + final var writer = new PlainTextWriterImpl(System.out); + + final Pair> results; + + GroupId groupId = null; + if (groupId != null) { + try { + groupId = Util.decodeGroupId(groupIdString); + } catch (GroupIdFormatException e) { + throw new UserErrorException("Invalid group id:" + e.getMessage()); + } + } - final Pair> results; - if (groupIdString != null) { - var groupId = Util.decodeGroupId(groupIdString); + try { + if (groupId != null) { results = m.sendGroupMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, groupId); } else { results = m.sendMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, recipients); } - return handleTimestampAndSendMessageResults(writer, results.first(), results.second()); + handleTimestampAndSendMessageResults(writer, results.first(), results.second()); } catch (IOException e) { - handleIOException(e); - return 3; + throw new IOErrorException("Failed to send message: " + e.getMessage()); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (GroupNotFoundException e) { - handleGroupNotFoundException(e); - return 1; + throw new UserErrorException("Failed to send to group: " + e.getMessage()); } catch (NotAGroupMemberException e) { - handleNotAGroupMemberException(e); - return 1; - } catch (GroupIdFormatException e) { - handleGroupIdFormatException(e); - return 1; + throw new UserErrorException("Failed to send to group: " + e.getMessage()); } catch (InvalidNumberException e) { - handleInvalidNumberException(e); - return 1; + throw new UserErrorException("Invalid number: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/SetPinCommand.java b/src/main/java/org/asamk/signal/commands/SetPinCommand.java index fff105be..56b4b8a4 100644 --- a/src/main/java/org/asamk/signal/commands/SetPinCommand.java +++ b/src/main/java/org/asamk/signal/commands/SetPinCommand.java @@ -3,6 +3,9 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.manager.Manager; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; @@ -18,17 +21,14 @@ public class SetPinCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { var registrationLockPin = ns.getString("registrationLockPin"); m.setRegistrationLockPin(Optional.of(registrationLockPin)); - return 0; } catch (UnauthenticatedResponseException e) { - System.err.println("Set pin error: " + e.getMessage()); - return 2; + throw new UnexpectedErrorException("Set pin error failed with unauthenticated response: " + e.getMessage()); } catch (IOException e) { - System.err.println("Set pin error: " + e.getMessage()); - return 3; + throw new IOErrorException("Set pin error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/TrustCommand.java b/src/main/java/org/asamk/signal/commands/TrustCommand.java index 277291eb..08fe6a41 100644 --- a/src/main/java/org/asamk/signal/commands/TrustCommand.java +++ b/src/main/java/org/asamk/signal/commands/TrustCommand.java @@ -4,8 +4,9 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; -import org.asamk.signal.util.ErrorUtils; import org.asamk.signal.util.Hex; import org.whispersystems.signalservice.api.util.InvalidNumberException; @@ -25,13 +26,12 @@ public class TrustCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { var number = ns.getString("number"); if (ns.getBoolean("trust_all_known_keys")) { var res = m.trustIdentityAllKeys(number); if (!res) { - System.err.println("Failed to set the trust for this number, make sure the number is correct."); - return 1; + throw new UserErrorException("Failed to set the trust for this number, make sure the number is correct."); } } else { var safetyNumber = ns.getString("verified_safety_number"); @@ -42,46 +42,38 @@ public class TrustCommand implements LocalCommand { try { fingerprintBytes = Hex.toByteArray(safetyNumber.toLowerCase(Locale.ROOT)); } catch (Exception e) { - System.err.println( + throw new UserErrorException( "Failed to parse the fingerprint, make sure the fingerprint is a correctly encoded hex string without additional characters."); - return 1; } boolean res; try { res = m.trustIdentityVerified(number, fingerprintBytes); } catch (InvalidNumberException e) { - ErrorUtils.handleInvalidNumberException(e); - return 1; + throw new UserErrorException("Failed to parse recipient: " + e.getMessage()); } if (!res) { - System.err.println( + throw new UserErrorException( "Failed to set the trust for the fingerprint of this number, make sure the number and the fingerprint are correct."); - return 1; } } else if (safetyNumber.length() == 60) { boolean res; try { res = m.trustIdentityVerifiedSafetyNumber(number, safetyNumber); } catch (InvalidNumberException e) { - ErrorUtils.handleInvalidNumberException(e); - return 1; + throw new UserErrorException("Failed to parse recipient: " + e.getMessage()); } if (!res) { - System.err.println( + throw new UserErrorException( "Failed to set the trust for the safety number of this phone number, make sure the phone number and the safety number are correct."); - return 1; } } else { - System.err.println( + throw new UserErrorException( "Safety number has invalid format, either specify the old hex fingerprint or the new safety number"); - return 1; } } else { - System.err.println( + throw new UserErrorException( "You need to specify the fingerprint/safety number you have verified with -v SAFETY_NUMBER"); - return 1; } } - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/UnblockCommand.java b/src/main/java/org/asamk/signal/commands/UnblockCommand.java index b9d6e849..6e067ee5 100644 --- a/src/main/java/org/asamk/signal/commands/UnblockCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnblockCommand.java @@ -3,14 +3,19 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.util.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.util.InvalidNumberException; public class UnblockCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(UnblockCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("contact").help("Contact number").nargs("*"); @@ -19,12 +24,12 @@ public class UnblockCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { - for (var contact_number : ns.getList("contact")) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { + for (var contactNumber : ns.getList("contact")) { try { - m.setContactBlocked(contact_number, false); + m.setContactBlocked(contactNumber, false); } catch (InvalidNumberException e) { - System.err.println(e.getMessage()); + logger.warn("Invalid number: {}", contactNumber); } } @@ -33,12 +38,12 @@ public class UnblockCommand implements LocalCommand { try { var groupId = Util.decodeGroupId(groupIdString); m.setGroupBlocked(groupId, false); - } catch (GroupIdFormatException | GroupNotFoundException e) { - System.err.println(e.getMessage()); + } catch (GroupIdFormatException e) { + logger.warn("Invalid group id: {}", groupIdString); + } catch (GroupNotFoundException e) { + logger.warn("Unknown group id: {}", groupIdString); } } } - - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/UnregisterCommand.java b/src/main/java/org/asamk/signal/commands/UnregisterCommand.java index 079070e6..1846eba1 100644 --- a/src/main/java/org/asamk/signal/commands/UnregisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnregisterCommand.java @@ -3,6 +3,8 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import java.io.IOException; @@ -15,13 +17,11 @@ public class UnregisterCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { m.unregister(); - return 0; } catch (IOException e) { - System.err.println("Unregister error: " + e.getMessage()); - return 3; + throw new IOErrorException("Unregister error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java b/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java index 8211e190..13723f09 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java @@ -3,6 +3,8 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import java.io.IOException; @@ -15,13 +17,11 @@ public class UpdateAccountCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { m.updateAccountAttributes(); - return 0; } catch (IOException e) { - System.err.println("UpdateAccount error: " + e.getMessage()); - return 3; + throw new IOErrorException("UpdateAccount error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java index c2b994d6..c8ad613b 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java @@ -3,6 +3,9 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.util.InvalidNumberException; @@ -22,7 +25,7 @@ public class UpdateContactCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { var number = ns.getString("number"); var name = ns.getString("name"); @@ -34,13 +37,9 @@ public class UpdateContactCommand implements LocalCommand { m.setExpirationTimer(number, expiration); } } catch (InvalidNumberException e) { - System.err.println("Invalid contact number: " + e.getMessage()); - return 1; + throw new UserErrorException("Invalid contact number: " + e.getMessage()); } catch (IOException e) { - System.err.println("Update contact error: " + e.getMessage()); - return 3; + throw new IOErrorException("Update contact error: " + e.getMessage()); } - - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index fdb23ea1..204dcfe2 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -5,19 +5,20 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.util.Util; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; import java.util.ArrayList; import java.util.Base64; import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; public class UpdateGroupCommand implements DbusCommand { @@ -32,15 +33,14 @@ public class UpdateGroupCommand implements DbusCommand { } @Override - public int handleCommand(final Namespace ns, final Signal signal) { + public void handleCommand(final Namespace ns, final Signal signal) throws CommandException { final var writer = new PlainTextWriterImpl(System.out); byte[] groupId = null; if (ns.getString("group") != null) { try { groupId = Util.decodeGroupId(ns.getString("group")).serialize(); } catch (GroupIdFormatException e) { - handleGroupIdFormatException(e); - return 1; + throw new UserErrorException("Invalid group id:" + e.getMessage()); } } if (groupId == null) { @@ -65,23 +65,15 @@ public class UpdateGroupCommand implements DbusCommand { try { var newGroupId = signal.updateGroup(groupId, groupName, groupMembers, groupAvatar); if (groupId.length != newGroupId.length) { - try { - writer.println("Creating new group \"{}\" …", Base64.getEncoder().encodeToString(newGroupId)); - } catch (IOException e) { - e.printStackTrace(); - return 3; - } + writer.println("Created new group: \"{}\"", Base64.getEncoder().encodeToString(newGroupId)); } - return 0; } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (Signal.Error.AttachmentInvalid e) { - System.err.println("Failed to add avatar attachment for group\": " + e.getMessage()); - return 1; + throw new UserErrorException("Failed to add avatar attachment for group\": " + e.getMessage()); } catch (DBusExecutionException e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 2; + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index b8c7b08c..c3fc2e88 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -4,6 +4,8 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import org.whispersystems.libsignal.util.guava.Optional; @@ -26,23 +28,21 @@ public class UpdateProfileCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { var name = ns.getString("name"); var about = ns.getString("about"); var aboutEmoji = ns.getString("about_emoji"); var avatarPath = ns.getString("avatar"); boolean removeAvatar = ns.getBoolean("remove_avatar"); + Optional avatarFile = removeAvatar + ? Optional.absent() + : avatarPath == null ? null : Optional.of(new File(avatarPath)); + try { - Optional avatarFile = removeAvatar - ? Optional.absent() - : avatarPath == null ? null : Optional.of(new File(avatarPath)); m.setProfile(name, about, aboutEmoji, avatarFile); } catch (IOException e) { - System.err.println("Update profile error: " + e.getMessage()); - return 3; + throw new IOErrorException("Update profile error: " + e.getMessage()); } - - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java index 9d72a6c5..1ed14e21 100644 --- a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java +++ b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java @@ -4,6 +4,9 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.StickerPackInvalidException; import org.slf4j.Logger; @@ -23,19 +26,17 @@ public class UploadStickerPackCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { final var writer = new PlainTextWriterImpl(System.out); + var path = new File(ns.getString("path")); + try { - var path = new File(ns.getString("path")); var url = m.uploadStickerPack(path); writer.println("{}", url); - return 0; } catch (IOException e) { - System.err.println("Upload error: " + e.getMessage()); - return 3; + throw new IOErrorException("Upload error: " + e.getMessage()); } catch (StickerPackInvalidException e) { - System.err.println("Invalid sticker pack: " + e.getMessage()); - return 1; + throw new UserErrorException("Invalid sticker pack: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/VerifyCommand.java b/src/main/java/org/asamk/signal/commands/VerifyCommand.java index a244581d..c26c1d3a 100644 --- a/src/main/java/org/asamk/signal/commands/VerifyCommand.java +++ b/src/main/java/org/asamk/signal/commands/VerifyCommand.java @@ -3,6 +3,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.RegistrationManager; import org.whispersystems.signalservice.api.KeyBackupServicePinException; import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; @@ -19,26 +23,23 @@ public class VerifyCommand implements RegistrationCommand { } @Override - public int handleCommand(final Namespace ns, final RegistrationManager m) { + public void handleCommand(final Namespace ns, final RegistrationManager m) throws CommandException { + var verificationCode = ns.getString("verificationCode"); + var pin = ns.getString("pin"); + try { - var verificationCode = ns.getString("verificationCode"); - var pin = ns.getString("pin"); m.verifyAccount(verificationCode, pin); - return 0; } catch (LockedException e) { - System.err.println("Verification failed! This number is locked with a pin. Hours remaining until reset: " - + (e.getTimeRemaining() / 1000 / 60 / 60)); - System.err.println("Use '--pin PIN_CODE' to specify the registration lock PIN"); - return 1; + throw new UserErrorException( + "Verification failed! This number is locked with a pin. Hours remaining until reset: " + + (e.getTimeRemaining() / 1000 / 60 / 60) + + "\nUse '--pin PIN_CODE' to specify the registration lock PIN"); } catch (KeyBackupServicePinException e) { - System.err.println("Verification failed! Invalid pin, tries remaining: " + e.getTriesRemaining()); - return 1; + throw new UserErrorException("Verification failed! Invalid pin, tries remaining: " + e.getTriesRemaining()); } catch (KeyBackupSystemNoDataException e) { - System.err.println("Verification failed! No KBS data."); - return 3; + throw new UnexpectedErrorException("Verification failed! No KBS data."); } catch (IOException e) { - System.err.println("Verify error: " + e.getMessage()); - return 3; + throw new IOErrorException("Verify error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/exceptions/CommandException.java b/src/main/java/org/asamk/signal/commands/exceptions/CommandException.java new file mode 100644 index 00000000..c82ef542 --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/exceptions/CommandException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.commands.exceptions; + +public class CommandException extends Exception { + + public CommandException(final String message) { + super(message); + } +} diff --git a/src/main/java/org/asamk/signal/commands/exceptions/IOErrorException.java b/src/main/java/org/asamk/signal/commands/exceptions/IOErrorException.java new file mode 100644 index 00000000..e405600c --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/exceptions/IOErrorException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.commands.exceptions; + +public final class IOErrorException extends CommandException { + + public IOErrorException(final String message) { + super(message); + } +} diff --git a/src/main/java/org/asamk/signal/commands/exceptions/UnexpectedErrorException.java b/src/main/java/org/asamk/signal/commands/exceptions/UnexpectedErrorException.java new file mode 100644 index 00000000..b6f231df --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/exceptions/UnexpectedErrorException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.commands.exceptions; + +public final class UnexpectedErrorException extends CommandException { + + public UnexpectedErrorException(final String message) { + super(message); + } +} diff --git a/src/main/java/org/asamk/signal/commands/exceptions/UntrustedKeyErrorException.java b/src/main/java/org/asamk/signal/commands/exceptions/UntrustedKeyErrorException.java new file mode 100644 index 00000000..c215f414 --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/exceptions/UntrustedKeyErrorException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.commands.exceptions; + +public final class UntrustedKeyErrorException extends CommandException { + + public UntrustedKeyErrorException(final String message) { + super(message); + } +} diff --git a/src/main/java/org/asamk/signal/commands/exceptions/UserErrorException.java b/src/main/java/org/asamk/signal/commands/exceptions/UserErrorException.java new file mode 100644 index 00000000..84e957cc --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/exceptions/UserErrorException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.commands.exceptions; + +public final class UserErrorException extends CommandException { + + public UserErrorException(final String message) { + super(message); + } +} diff --git a/src/main/java/org/asamk/signal/util/ErrorUtils.java b/src/main/java/org/asamk/signal/util/ErrorUtils.java index 149b16c0..595509a6 100644 --- a/src/main/java/org/asamk/signal/util/ErrorUtils.java +++ b/src/main/java/org/asamk/signal/util/ErrorUtils.java @@ -1,38 +1,34 @@ package org.asamk.signal.util; import org.asamk.signal.PlainTextWriter; -import org.asamk.signal.manager.groups.GroupIdFormatException; -import org.asamk.signal.manager.groups.GroupNotFoundException; -import org.asamk.signal.manager.groups.NotAGroupMemberException; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.messages.SendMessageResult; -import org.whispersystems.signalservice.api.util.InvalidNumberException; -import java.io.IOException; import java.util.ArrayList; import java.util.List; public class ErrorUtils { + private final static Logger logger = LoggerFactory.getLogger(ErrorUtils.class); + private ErrorUtils() { } public static void handleAssertionError(AssertionError e) { - System.err.println("Failed to send/receive message (Assertion): " + e.getMessage()); - e.printStackTrace(); - System.err.println( - "If you use an Oracle JRE please check if you have unlimited strength crypto enabled, see README"); + logger.warn("If you use an Oracle JRE please check if you have unlimited strength crypto enabled, see README"); } - public static int handleTimestampAndSendMessageResults( - PlainTextWriter writer, - long timestamp, - List results - ) throws IOException { + public static void handleTimestampAndSendMessageResults( + PlainTextWriter writer, long timestamp, List results + ) throws CommandException { if (timestamp != 0) { writer.println("{}", timestamp); } var errors = getErrorMessagesFromSendMessageResults(results); - return handleSendMessageResultErrors(errors); + handleSendMessageResultErrors(errors); } public static List getErrorMessagesFromSendMessageResults(List results) { @@ -58,39 +54,15 @@ public class ErrorUtils { return null; } - private static int handleSendMessageResultErrors(List errors) { + private static void handleSendMessageResultErrors(List errors) throws CommandException { if (errors.size() == 0) { - return 0; + return; } - System.err.println("Failed to send (some) messages:"); + var message = new StringBuilder(); + message.append("Failed to send (some) messages:\n"); for (var error : errors) { - System.err.println(error); + message.append(error).append("\n"); } - return 3; - } - - public static void handleIOException(IOException e) { - System.err.println("Failed to send message: " + e.getMessage()); - } - - public static void handleGroupNotFoundException(GroupNotFoundException e) { - System.err.println("Failed to send to group: " + e.getMessage()); - System.err.println("Aborting sending."); - } - - public static void handleNotAGroupMemberException(NotAGroupMemberException e) { - System.err.println("Failed to send to group: " + e.getMessage()); - System.err.println("Update the group on another device to readd the user to this group."); - System.err.println("Aborting sending."); - } - - public static void handleGroupIdFormatException(GroupIdFormatException e) { - System.err.println(e.getMessage()); - System.err.println("Aborting sending."); - } - - public static void handleInvalidNumberException(InvalidNumberException e) { - System.err.println("Failed to parse recipient: " + e.getMessage()); - System.err.println("Aborting sending."); + throw new IOErrorException(message.toString()); } }