import org.asamk.Signal;
import org.asamk.signal.commands.Command;
import org.asamk.signal.commands.Commands;
-import org.asamk.signal.commands.ExtendedDbusCommand;
import org.asamk.signal.commands.LocalCommand;
import org.asamk.signal.commands.MultiLocalCommand;
import org.asamk.signal.commands.ProvisioningCommand;
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;
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")
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);
var username = ns.getString("username");
- final var useDbus = Boolean.TRUE.equals(ns.getBoolean("dbus"));
- final var useDbusSystem = Boolean.TRUE.equals(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);
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");
}
? 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.getAllLocalNumbers(dataPath);
- if (command instanceof MultiLocalCommand) {
- handleMultiLocalCommand((MultiLocalCommand) command,
+ if (command instanceof MultiLocalCommand multiLocalCommand) {
+ handleMultiLocalCommand(multiLocalCommand,
dataPath,
serviceEnvironment,
usernames,
}
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;
}
final TrustNewIdentity trustNewIdentity
) throws CommandException {
final var managers = new ArrayList<Manager>();
- 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<Consumer<Manager>> onManagerAddedHandlers = new ArrayList<>();
+
+ @Override
+ public List<String> 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<Manager> 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();
}
}
}
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);
}
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 LocalCommand) {
- try {
- ((LocalCommand) command).handleCommand(ns, new DbusManagerImpl(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 (DBusExecutionException e) {
+ } catch (IOException | DBusExecutionException e) {
throw new UnexpectedErrorException(e.getMessage(), e);
}
} else {