diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index b0bfebf8..938dc2a3 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.util.KeyHelper; import org.whispersystems.signalservice.api.SignalServiceAccountManager; +import org.whispersystems.signalservice.api.SignalServiceAccountManager.NewDeviceRegistrationReturn; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -93,11 +94,22 @@ public class ProvisioningManager { } public Manager finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists { - var ret = accountManager.getNewDeviceRegistration(tempIdentityKey); - var number = ret.getNumber(); - - logger.info("Received link information from {}, linking in progress ...", number); - + NewDeviceRegistrationReturn ret = null; + String number = null; + try { + logger.info("Waiting for link request from new device ..."); + ret = accountManager.getNewDeviceRegistration(tempIdentityKey); + number = ret.getNumber(); + logger.info("Received link information from {}, linking in progress ...", number); + } catch (TimeoutException e) { + accountManager.cancelInFlightRequests(); + logger.error("Timeout on linking {}", number); + throw e; + } catch (IOException f) { + accountManager.cancelInFlightRequests(); + logger.error("Error on linking {}", number); + throw f; + } if (SignalAccount.userExists(pathConfig.getDataPath(), number) && !canRelinkExistingAccount(number)) { throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.getDataPath(), number)); } diff --git a/man/signal-cli-dbus.5.adoc b/man/signal-cli-dbus.5.adoc index 635859fb..a8d9ca4e 100755 --- a/man/signal-cli-dbus.5.adoc +++ b/man/signal-cli-dbus.5.adoc @@ -32,7 +32,7 @@ Where is according to DBus specification: * : Array of ... * : String * : Byte -* : Boolean (0|1) +* : Boolean (false|true) * : Signed 64-bit integer (long) * : Signed 32-bit integer (int) * <> : no return value @@ -68,13 +68,13 @@ updateProfile(newName, about , aboutEmoji , avatar, remove) -> <> * about : About message for profile (empty if unchanged) * aboutEmoji : Emoji for profile (empty if unchanged) * avatar : Filename of avatar picture for profile (empty if unchanged) -* remove : Set to 1 if the existing avatar picture should be removed +* remove : Set to true if the existing avatar picture should be removed Exceptions: Failure setContactBlocked(number, block) -> <>:: * number : Phone number affected by method -* block : 0=remove block , 1=blocked +* block : false=remove block, true=block contact Messages from blocked numbers will no longer be forwarded via DBus. @@ -84,7 +84,7 @@ setGroupBlocked(groupId, block) -> <>:: setGroupBlocked(base64GroupId, block) -> <>:: * groupId : Byte array representing the internal group identifier * base64GroupId : String representing the internal group identifier in Base64 format -* block : 0=remove block , 1=blocked +* block : false=remove block , true=block contact Messages from blocked groups will no longer be forwarded via DBus. @@ -110,7 +110,7 @@ isMember(base64GroupId) -> active:: * base64GroupId : String representing the internal group identifier in Base64 format * active : Boolean representing whether you are a member of the group -Note that this method does not raise an Exception for a non-existing/unknown group but will simply return 0 (false) +Note that this method does not raise an Exception for a non-existing/unknown group but will simply return false sendEndSessionMessage(recipients) -> <>:: * recipients : String array of phone numbers @@ -206,7 +206,7 @@ setExpirationTimer(number,expiration) -> <>:: getGroupIds() -> groupList:: getGroupIds(dummy) -> groupList:: -dummy : any string (ignored by method; force output to be identical with getBase64GroupIds) +dummy : any string (ignored by method; forces output to be identical with getBase64GroupIds) groupList : Array of Byte arrays representing the internal group identifiers base64GroupList : Array of strings representing the internal group identifiers in Base64 format @@ -246,17 +246,17 @@ Searches contacts and known profiles for a given name and returns the list of al isContactBlocked(number) -> state:: * number : Phone number -* state : 1=blocked, 0=not blocked +* state : true=blocked, false=not blocked -Exceptions: None, for unknown numbers 0 (false) is returned +Exceptions: None, for unknown numbers false is returned isGroupBlocked(groupId) -> state:: isGroupBlocked(base64GroupId) -> state:: * groupId : Byte array representing the internal group identifier * base64GroupId : String representing the internal group identifier in Base64 format -* state : 1=blocked, 0=not blocked +* state : true=blocked, false=not blocked -Exceptions: None, for unknown groups 0 (false) is returned +Exceptions: None, for unknown groups false is returned version() -> version:: * version : Version string of signal-cli @@ -265,7 +265,7 @@ isRegistered(number) -> result:: isRegistered(numbers) -> results:: * number : Phone number * numbers : String array of phone numbers -* result : 1=number is registered, 0=number is not registered +* result : true=number is registered, false=number is not registered * results : Boolean array of results listDevices() -> devices:: @@ -286,17 +286,15 @@ updateAccount() -> <> This command reverses an `unregister` command. - -== Methods for org.asamk.SignalControl - getObjectPath() -> objectPath:: * objectPath : The DBus object path associated with this connection version() -> version:: * version : Version string of signal-cli +link() -> deviceLinkUri:: link(newDeviceName) -> deviceLinkUri:: -* newDeviceName : Name to give new device +* newDeviceName : Name to give new device (defaults to "cli" if no name is given) * deviceLinkUri : URI of newly linked device register(number, voiceVerification) -> <>:: diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 8188a8c6..0fe167a8 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -1,6 +1,6 @@ package org.asamk; -import org.asamk.Signal.Error; +import org.asamk.SignalControl; import org.asamk.signal.manager.AvatarStore; import org.asamk.signal.manager.groups.GroupId; import org.freedesktop.dbus.exceptions.DBusException; @@ -132,6 +132,22 @@ public interface Signal extends DBusInterface { String getObjectPath(); + String link() throws Error.Failure; + + String link(String newDeviceName) throws Error.Failure; + + void register( + String number, boolean voiceVerification + ) throws Error.Failure, Error.InvalidNumber, SignalControl.Error.RequiresCaptcha; + + void registerWithCaptcha( + final String number, final boolean voiceVerification, final String captcha + ) throws Error.Failure, Error.InvalidNumber, SignalControl.Error.RequiresCaptcha; + + void verify(String number, String verificationCode) throws Error.Failure, Error.InvalidNumber; + + void verifyWithPin(String number, String verificationCode, String pin) throws Error.Failure, Error.InvalidNumber; + List listNumbers(); List listDevices() throws Error.Failure; diff --git a/src/main/java/org/asamk/SignalControl.java b/src/main/java/org/asamk/SignalControl.java index e6c86536..c38e0a10 100644 --- a/src/main/java/org/asamk/SignalControl.java +++ b/src/main/java/org/asamk/SignalControl.java @@ -12,25 +12,7 @@ import java.util.List; */ public interface SignalControl extends DBusInterface { - void register( - String number, boolean voiceVerification - ) throws Error.Failure, Error.InvalidNumber, Error.RequiresCaptcha; - - void registerWithCaptcha( - String number, boolean voiceVerification, String captcha - ) throws Error.Failure, Error.InvalidNumber, Error.RequiresCaptcha; - - void verify(String number, String verificationCode) throws Error.Failure, Error.InvalidNumber; - - void verifyWithPin(String number, String verificationCode, String pin) throws Error.Failure, Error.InvalidNumber; - - String link(String newDeviceName) throws Error.Failure; - - String version(); - - List listAccounts(); - - String getObjectPath(); + List listAccounts(); interface Error { diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 49172f80..6225fa62 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -43,6 +43,8 @@ public class App { private final static Logger logger = LoggerFactory.getLogger(App.class); private final Namespace ns; + public static File dataPath; + public static ServiceEnvironment serviceEnvironment; static ArgumentParser buildArgumentParser() { var parser = ArgumentParsers.newFor("signal-cli", VERSION_0_9_0_DEFAULT_SETTINGS) @@ -85,8 +87,14 @@ public class App { return parser; } - public App(final Namespace ns) { + public App( + final Namespace ns, + File dataPath, + ServiceEnvironment serviceEnvironment + ) { this.ns = ns; + this.dataPath = dataPath; + this.serviceEnvironment = serviceEnvironment; } public void init() throws CommandException { @@ -105,7 +113,7 @@ public class App { throw new UserErrorException("Command doesn't support output type " + outputType.toString()); } - var username = ns.getString("username"); + String username = ns.getString("username"); final var useDbus = ns.getBoolean("dbus"); final var useDbusSystem = ns.getBoolean("dbus-system"); @@ -115,7 +123,7 @@ public class App { return; } - final File dataPath; +// final File dataPath; var config = ns.getString("config"); if (config != null) { dataPath = new File(config); @@ -124,7 +132,7 @@ public class App { } final var serviceEnvironmentCli = ns.get("service-environment"); - final var serviceEnvironment = serviceEnvironmentCli == ServiceEnvironmentCli.LIVE + serviceEnvironment = serviceEnvironmentCli == ServiceEnvironmentCli.LIVE ? ServiceEnvironment.LIVE : ServiceEnvironment.SANDBOX; diff --git a/src/main/java/org/asamk/signal/BaseConfig.java b/src/main/java/org/asamk/signal/BaseConfig.java index afafc7d7..d5933b67 100644 --- a/src/main/java/org/asamk/signal/BaseConfig.java +++ b/src/main/java/org/asamk/signal/BaseConfig.java @@ -5,7 +5,7 @@ public class BaseConfig { public final static String PROJECT_NAME = BaseConfig.class.getPackage().getImplementationTitle(); public final static String PROJECT_VERSION = BaseConfig.class.getPackage().getImplementationVersion(); - final static String USER_AGENT = PROJECT_NAME == null ? "signal-cli" : PROJECT_NAME + " " + PROJECT_VERSION; + public final static String USER_AGENT = PROJECT_NAME == null ? "signal-cli" : PROJECT_NAME + " " + PROJECT_VERSION; private BaseConfig() { } diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index e0747500..2a05d55e 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -27,9 +27,11 @@ 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.manager.config.ServiceEnvironment; import org.asamk.signal.util.SecurityProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import java.io.File; import java.security.Security; public class Main { @@ -45,10 +47,12 @@ public class Main { var parser = App.buildArgumentParser(); var ns = parser.parseArgsOrFail(args); + File dataPath = null; + ServiceEnvironment serviceEnvironment = null; int status = 0; try { - new App(ns).init(); + new App(ns, dataPath, serviceEnvironment).init(); } catch (CommandException e) { System.err.println(e.getMessage()); status = getStatusForError(e); diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java index 35f530b0..a2c7159d 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java @@ -1,13 +1,17 @@ package org.asamk.signal.dbus; import org.asamk.SignalControl; +import org.asamk.signal.App; import org.asamk.signal.BaseConfig; import org.asamk.signal.DbusConfig; import org.asamk.signal.commands.SignalCreator; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.PathConfig; import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.RegistrationManager; import org.asamk.signal.manager.UserAlreadyExists; +import org.asamk.signal.manager.config.ServiceConfig; +import org.asamk.signal.manager.config.ServiceEnvironment; import org.freedesktop.dbus.DBusPath; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.signalservice.api.KeyBackupServicePinException; @@ -15,6 +19,7 @@ import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException; import java.io.IOException; +import java.lang.System.Logger; import java.net.URI; import java.util.ArrayList; import java.util.List; @@ -24,12 +29,12 @@ import java.util.stream.Collectors; public class DbusSignalControlImpl implements org.asamk.SignalControl { - private final SignalCreator c; - private final Function newManagerRunner; + private static SignalCreator c; + private static Function newManagerRunner; - private final List> receiveThreads = new ArrayList<>(); - private final Object stopTrigger = new Object(); - private final String objectPath; + private static List> receiveThreads = new ArrayList<>(); + private static Object stopTrigger = new Object(); + private static String objectPath; public DbusSignalControlImpl( final SignalCreator c, final Function newManagerRunner, final String objectPath @@ -39,7 +44,7 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { this.objectPath = objectPath; } - public void addManager(Manager m) { + public static void addManager(Manager m) { var thread = newManagerRunner.apply(m); if (thread == null) { return; @@ -78,30 +83,39 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { } } +/* @Override public boolean isRemote() { return false; } +*/ @Override public String getObjectPath() { return objectPath; } - @Override - public void register( + public static void register( final String number, final boolean voiceVerification - ) throws Error.Failure, Error.InvalidNumber { + ) { registerWithCaptcha(number, voiceVerification, null); } - @Override - public void registerWithCaptcha( + public static void registerWithCaptcha( final String number, final boolean voiceVerification, final String captcha - ) throws Error.Failure, Error.InvalidNumber { - try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) { + ) { + RegistrationManager registrationManager = null; + try { + registrationManager = + RegistrationManager.init(number, App.dataPath, App.serviceEnvironment, BaseConfig.USER_AGENT); registrationManager.register(voiceVerification, captcha); + System.out.println("registered"); } catch (CaptchaRequiredException e) { + try { + registrationManager.close(); + } catch (IOException f) { + throw new SignalControl.Error.Failure(f.getClass().getSimpleName() + " " + f.getMessage()); + } String message = captcha == null ? "Captcha required for verification." : "Invalid captcha given."; throw new SignalControl.Error.RequiresCaptcha(message); } catch (IOException e) { @@ -109,16 +123,16 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { } } - @Override - public void verify(final String number, final String verificationCode) throws Error.Failure, Error.InvalidNumber { + public static void verify(final String number, final String verificationCode) { verifyWithPin(number, verificationCode, null); } - @Override - public void verifyWithPin( - final String number, final String verificationCode, final String pin - ) throws Error.Failure, Error.InvalidNumber { - try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) { + public static void verifyWithPin(final String number, final String verificationCode, final String pin) + { + RegistrationManager registrationManager = null; + try { + registrationManager = + RegistrationManager.init(number, App.dataPath, App.serviceEnvironment, BaseConfig.USER_AGENT); final Manager manager = registrationManager.verifyAccount(verificationCode, pin); addManager(manager); } catch (IOException | KeyBackupSystemNoDataException | KeyBackupServicePinException e) { @@ -126,17 +140,20 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { } } - @Override - public String link(final String newDeviceName) throws Error.Failure { + public static String link() { + return link("cli"); + } + + public static String link(final String newDeviceName) { try { - final ProvisioningManager provisioningManager = c.getNewProvisioningManager(); + final ProvisioningManager provisioningManager = + ProvisioningManager.init(App.dataPath, App.serviceEnvironment, BaseConfig.USER_AGENT); final URI deviceLinkUri = provisioningManager.getDeviceLinkUri(); new Thread(() -> { try { - final Manager manager = provisioningManager.finishDeviceLink(newDeviceName); + Manager manager = provisioningManager.finishDeviceLink(newDeviceName); addManager(manager); } catch (IOException | TimeoutException | UserAlreadyExists e) { - e.printStackTrace(); } }).start(); return deviceLinkUri.toString(); @@ -145,11 +162,11 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { } } - @Override - public String version() { + public static String version() { return BaseConfig.PROJECT_VERSION; } + @Override public List listAccounts() { synchronized (receiveThreads) { diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 2dc64561..e49553e2 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -1,6 +1,8 @@ package org.asamk.signal.dbus; import org.asamk.Signal; +import org.asamk.SignalControl; +import org.asamk.SignalControl.Error; import org.asamk.signal.BaseConfig; import org.asamk.signal.OutputWriter; import org.asamk.signal.PlainTextWriter; @@ -250,7 +252,7 @@ public class DbusSignalImpl implements Signal { @Override public long sendNoteToSelfMessage( final String message, final List attachments - ) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity { + ) { try { final var results = m.sendSelfMessage(message, attachments); checkSendMessageResult(results.first(), results.second()); @@ -528,61 +530,6 @@ public class DbusSignalImpl implements Signal { return Base64.getEncoder().encodeToString(groupId); } -/* - GroupLinkState getGroupLinkState(String value) throws UserErrorException { - if (value == null) { - return null; - } - switch (value) { - case "enabled": - return GroupLinkState.ENABLED; - case "enabled-with-approval": - case "enabledWithApproval": - return GroupLinkState.ENABLED_WITH_APPROVAL; - case "disabled": - return GroupLinkState.DISABLED; - default: - throw new UserErrorException("Invalid group link state: " + value); - } - } - - GroupPermission getGroupPermission(String value) throws UserErrorException { - if (value == null) { - return null; - } - switch (value) { - case "every-member": - case "everyMember": - return GroupPermission.EVERY_MEMBER; - case "only-admins": - case "onlyAdmins": - return GroupPermission.ONLY_ADMINS; - default: - throw new UserErrorException("Invalid group permission: " + value); - } - } -*/ - - /* - * - * -public Pair> updateGroup( -GroupId groupId, -String name, -String description, -List addMembers, -List removeMembers, -List addAdmins, -List removeAdmins, -boolean resetGroupLink, -GroupLinkState groupLinkState, -GroupPermission addMemberPermission, -GroupPermission editDetailsPermission, -File avatarFile, -Integer expirationTimer - - */ - @Override public String updateGroup( String base64GroupId, @@ -737,9 +684,44 @@ Integer expirationTimer // future interface changes @Override public String version() { - return BaseConfig.PROJECT_VERSION; + return DbusSignalControlImpl.version(); } + @Override + public String link() { + return DbusSignalControlImpl.link(); + } + + @Override + public String link(String newDeviceName) { + return DbusSignalControlImpl.link(newDeviceName); + } + + @Override + public void register( + String number, boolean voiceVerification + ) { + DbusSignalControlImpl.register(number, voiceVerification); + } + + @Override + public void registerWithCaptcha( + String number, boolean voiceVerification, String captcha + ) { + DbusSignalControlImpl.registerWithCaptcha(number, voiceVerification, captcha); + } + + @Override + public void verify(String number, String verificationCode) { + DbusSignalControlImpl.verify(number, verificationCode); + } + + @Override + public void verifyWithPin(String number, String verificationCode, String pin) { + DbusSignalControlImpl.verifyWithPin(number, verificationCode, pin); + } + + // Create a unique list of Numbers from Identities and Contacts to really get // all numbers the system knows @Override