X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/2044a7d7a58ada7ca1e67a80012e3ffdaf86c88c..81a11dc9776672e3468ee9a8eed556889fb2e070:/src/main/java/org/asamk/signal/App.java diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 4aa510d6..01af3178 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -8,8 +8,6 @@ import net.sourceforge.argparse4j.inf.Namespace; import org.asamk.Signal; import org.asamk.signal.commands.Command; import org.asamk.signal.commands.Commands; -import org.asamk.signal.commands.DbusCommand; -import org.asamk.signal.commands.ExtendedDbusCommand; import org.asamk.signal.commands.LocalCommand; import org.asamk.signal.commands.MultiLocalCommand; import org.asamk.signal.commands.ProvisioningCommand; @@ -19,6 +17,7 @@ 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.dbus.DbusManagerImpl; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.NotRegisteredException; import org.asamk.signal.manager.ProvisioningManager; @@ -29,14 +28,19 @@ import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.util.IOUtils; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; +import java.io.BufferedWriter; import java.io.File; import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; import static net.sourceforge.argparse4j.DefaultSettings.VERSION_0_9_0_DEFAULT_SETTINGS; @@ -64,8 +68,11 @@ public class App { parser.addArgument("-u", "--username").help("Specify your phone number, that will be your identifier."); var mut = parser.addMutuallyExclusiveGroup(); - mut.addArgument("--dbus").help("Make request via user dbus.").action(Arguments.storeTrue()); - mut.addArgument("--dbus-system").help("Make request via system dbus.").action(Arguments.storeTrue()); + mut.addArgument("--dbus").dest("global-dbus").help("Make request via user dbus.").action(Arguments.storeTrue()); + mut.addArgument("--dbus-system") + .dest("global-dbus-system") + .help("Make request via system dbus.") + .action(Arguments.storeTrue()); parser.addArgument("-o", "--output") .help("Choose to output in plain text or JSON") @@ -106,9 +113,10 @@ public class App { var outputType = outputTypeInput == null ? command.getSupportedOutputTypes().stream().findFirst().orElse(null) : outputTypeInput; + var writer = new BufferedWriter(new OutputStreamWriter(System.out, Charset.defaultCharset())); var outputWriter = outputType == null ? null - : outputType == OutputType.JSON ? new JsonWriterImpl(System.out) : new PlainTextWriterImpl(System.out); + : outputType == OutputType.JSON ? new JsonWriterImpl(writer) : new PlainTextWriterImpl(writer); if (outputWriter != null && !command.getSupportedOutputTypes().contains(outputType)) { throw new UserErrorException("Command doesn't support output type " + outputType); @@ -116,8 +124,8 @@ public class App { var username = ns.getString("username"); - final var useDbus = ns.getBoolean("dbus"); - final var useDbusSystem = ns.getBoolean("dbus-system"); + final var useDbus = Boolean.TRUE.equals(ns.getBoolean("global-dbus")); + final var useDbusSystem = Boolean.TRUE.equals(ns.getBoolean("global-dbus-system")); if (useDbus || useDbusSystem) { // If username is null, it will connect to the default object path initDbusClient(command, username, useDbusSystem, outputWriter); @@ -132,7 +140,7 @@ public class App { dataPath = getDefaultDataPath(); } - if (!ServiceConfig.getCapabilities().isGv2()) { + if (!ServiceConfig.isZkgroupAvailable()) { logger.warn("WARNING: Support for new group V2 is disabled," + " because the required native library dependency is missing: libzkgroup"); } @@ -151,20 +159,20 @@ public class App { ? TrustNewIdentity.ON_FIRST_USE : trustNewIdentityCli == TrustNewIdentityCli.ALWAYS ? TrustNewIdentity.ALWAYS : TrustNewIdentity.NEVER; - if (command instanceof ProvisioningCommand) { + if (command instanceof ProvisioningCommand provisioningCommand) { if (username != null) { throw new UserErrorException("You cannot specify a username (phone number) when linking"); } - handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceEnvironment, outputWriter); + handleProvisioningCommand(provisioningCommand, dataPath, serviceEnvironment, outputWriter); return; } if (username == null) { - var usernames = Manager.getAllLocalUsernames(dataPath); + var usernames = Manager.getAllLocalNumbers(dataPath); - if (command instanceof MultiLocalCommand) { - handleMultiLocalCommand((MultiLocalCommand) command, + if (command instanceof MultiLocalCommand multiLocalCommand) { + handleMultiLocalCommand(multiLocalCommand, dataPath, serviceEnvironment, usernames, @@ -181,12 +189,12 @@ public class App { } username = usernames.get(0); - } else if (!PhoneNumberFormatter.isValidNumber(username, null)) { + } else if (!Manager.isValidNumber(username, null)) { throw new UserErrorException("Invalid username (phone number), make sure you include the country code."); } - if (command instanceof RegistrationCommand) { - handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceEnvironment); + if (command instanceof RegistrationCommand registrationCommand) { + handleRegistrationCommand(registrationCommand, username, dataPath, serviceEnvironment); return; } @@ -259,31 +267,72 @@ public class App { final TrustNewIdentity trustNewIdentity ) throws CommandException { final var managers = new ArrayList(); - for (String u : usernames) { - try { - managers.add(loadManager(u, dataPath, serviceEnvironment, trustNewIdentity)); - } catch (CommandException e) { - logger.warn("Ignoring {}: {}", u, e.getMessage()); - } - } - - command.handleCommand(ns, managers, new SignalCreator() { - @Override - public ProvisioningManager getNewProvisioningManager() { - return ProvisioningManager.init(dataPath, serviceEnvironment, BaseConfig.USER_AGENT); - } - - @Override - public RegistrationManager getNewRegistrationManager(String username) throws IOException { - return RegistrationManager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); + try { + for (String u : usernames) { + try { + managers.add(loadManager(u, dataPath, serviceEnvironment, trustNewIdentity)); + } catch (CommandException e) { + logger.warn("Ignoring {}: {}", u, e.getMessage()); + } } - }, outputWriter); - for (var m : managers) { - try { - m.close(); - } catch (IOException e) { - logger.warn("Cleanup failed", e); + command.handleCommand(ns, new SignalCreator() { + private List> onManagerAddedHandlers = new ArrayList<>(); + + @Override + public List getAccountNumbers() { + synchronized (managers) { + return managers.stream().map(Manager::getSelfNumber).collect(Collectors.toList()); + } + } + + @Override + public void addManager(final Manager m) { + synchronized (managers) { + if (!managers.contains(m)) { + managers.add(m); + for (final var handler : onManagerAddedHandlers) { + handler.accept(m); + } + } + } + } + + @Override + public void addOnManagerAddedHandler(final Consumer handler) { + onManagerAddedHandlers.add(handler); + } + + @Override + public Manager getManager(final String phoneNumber) { + synchronized (managers) { + return managers.stream() + .filter(m -> m.getSelfNumber().equals(phoneNumber)) + .findFirst() + .orElse(null); + } + } + + @Override + public ProvisioningManager getNewProvisioningManager() { + return ProvisioningManager.init(dataPath, serviceEnvironment, BaseConfig.USER_AGENT); + } + + @Override + public RegistrationManager getNewRegistrationManager(String username) throws IOException { + return RegistrationManager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); + } + }, outputWriter); + } finally { + synchronized (managers) { + for (var m : managers) { + try { + m.close(); + } catch (IOException e) { + logger.warn("Cleanup failed", e); + } + } + managers.clear(); } } } @@ -312,6 +361,11 @@ public class App { try { manager.checkAccountState(); } catch (IOException e) { + try { + manager.close(); + } catch (IOException ie) { + logger.warn("Failed to close broken account", ie); + } throw new IOErrorException("Error while checking account " + username + ": " + e.getMessage(), e); } @@ -344,10 +398,14 @@ public class App { private void handleCommand( Command command, Signal ts, DBusConnection dBusConn, OutputWriter outputWriter ) throws CommandException { - if (command instanceof ExtendedDbusCommand) { - ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn, outputWriter); - } else if (command instanceof DbusCommand) { - ((DbusCommand) command).handleCommand(ns, ts, outputWriter); + if (command instanceof LocalCommand localCommand) { + try (final var m = new DbusManagerImpl(ts, dBusConn)) { + localCommand.handleCommand(ns, m, outputWriter); + } catch (UnsupportedOperationException e) { + throw new UserErrorException("Command is not yet implemented via dbus", e); + } catch (IOException | DBusExecutionException e) { + throw new UnexpectedErrorException(e.getMessage(), e); + } } else { throw new UserErrorException("Command is not yet implemented via dbus"); }