From: AsamK Date: Fri, 12 Nov 2021 15:38:55 +0000 (+0100) Subject: Rename username to account X-Git-Tag: v0.10.0~48 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/8aab644db9baa4feb5ccef4f43144313278f4691 Rename username to account --- diff --git a/README.md b/README.md index 67bbb75a..6ed024d0 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,88 @@ # signal-cli -signal-cli is a commandline interface for [libsignal-service-java](https://github.com/WhisperSystems/libsignal-service-java). It supports registering, verifying, sending and receiving messages. -To be able to link to an existing Signal-Android/signal-cli instance, signal-cli uses a [patched libsignal-service-java](https://github.com/AsamK/libsignal-service-java), because libsignal-service-java does not yet support [provisioning as a linked device](https://github.com/WhisperSystems/libsignal-service-java/pull/21). -For registering you need a phone number where you can receive SMS or incoming calls. -signal-cli is primarily intended to be used on servers to notify admins of important events. For this use-case, it has a dbus interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)), that can be used to send messages from any programming language that has dbus bindings. -It also has a JSON-RPC based interface, see the [documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service) for more information. +signal-cli is a commandline interface +for [libsignal-service-java](https://github.com/WhisperSystems/libsignal-service-java). It supports registering, +verifying, sending and receiving messages. To be able to link to an existing Signal-Android/signal-cli instance, +signal-cli uses a [patched libsignal-service-java](https://github.com/AsamK/libsignal-service-java), because +libsignal-service-java does not yet +support [provisioning as a linked device](https://github.com/WhisperSystems/libsignal-service-java/pull/21). For +registering you need a phone number where you can receive SMS or incoming calls. signal-cli is primarily intended to be +used on servers to notify admins of important events. For this use-case, it has a dbus +interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)), that can be used to +send messages from any programming language that has dbus bindings. It also has a JSON-RPC based interface, see +the [documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service) for more information. ## Installation -You can [build signal-cli](#building) yourself, or use the [provided binary files](https://github.com/AsamK/signal-cli/releases/latest), which should work on Linux, macOS and Windows. For Arch Linux there is also a [package in AUR](https://aur.archlinux.org/packages/signal-cli/) and there is a [FreeBSD port](https://www.freshports.org/net-im/signal-cli) available as well. +You can [build signal-cli](#building) yourself, or use +the [provided binary files](https://github.com/AsamK/signal-cli/releases/latest), which should work on Linux, macOS and +Windows. For Arch Linux there is also a [package in AUR](https://aur.archlinux.org/packages/signal-cli/) and there is +a [FreeBSD port](https://www.freshports.org/net-im/signal-cli) available as well. System requirements: + - at least Java Runtime Environment (JRE) 17 - native libraries: libzkgroup, libsignal-client - Those are bundled for x86_64 Linux (with recent enough glibc, see #643), for other systems/architectures see: [Provide native lib for libsignal](https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal) + Those are bundled for x86_64 Linux (with recent enough glibc, see #643), for other systems/architectures + see: [Provide native lib for libsignal](https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal) ### Install system-wide on Linux + See [latest version](https://github.com/AsamK/signal-cli/releases). + ```sh export VERSION= wget https://github.com/AsamK/signal-cli/releases/download/v"${VERSION}"/signal-cli-"${VERSION}".tar.gz sudo tar xf signal-cli-"${VERSION}".tar.gz -C /opt sudo ln -sf /opt/signal-cli-"${VERSION}"/bin/signal-cli /usr/local/bin/ ``` + You can find further instructions on the Wiki: + - [Quickstart](https://github.com/AsamK/signal-cli/wiki/Quickstart) - [DBus Service](https://github.com/AsamK/signal-cli/wiki/DBus-service) ## Usage -For a complete usage overview please read the [man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli.1.adoc) and the [wiki](https://github.com/AsamK/signal-cli/wiki). +For a complete usage overview please read +the [man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli.1.adoc) and +the [wiki](https://github.com/AsamK/signal-cli/wiki). -Important: The USERNAME is your phone number in international format and must include the country calling code. Hence it should start with a "+" sign. (See [Wikipedia](https://en.wikipedia.org/wiki/List_of_country_calling_codes) for a list of all country codes.) +Important: The ACCOUNT is your phone number in international format and must include the country calling code. Hence it +should start with a "+" sign. (See [Wikipedia](https://en.wikipedia.org/wiki/List_of_country_calling_codes) for a list +of all country codes.) * Register a number (with SMS verification) - signal-cli -u USERNAME register + signal-cli -a ACCOUNT register - You can register Signal using a land line number. In this case you can skip SMS verification process and jump directly to the voice call verification by adding the `--voice` switch at the end of above register command. + You can register Signal using a land line number. In this case you can skip SMS verification process and jump directly + to the voice call verification by adding the `--voice` switch at the end of above register command. - Registering may require solving a CAPTCHA challenge: [Registration with captcha](https://github.com/AsamK/signal-cli/wiki/Registration-with-captcha) + Registering may require solving a CAPTCHA + challenge: [Registration with captcha](https://github.com/AsamK/signal-cli/wiki/Registration-with-captcha) -* Verify the number using the code received via SMS or voice, optionally add `--pin PIN_CODE` if you've added a pin code to your account +* Verify the number using the code received via SMS or voice, optionally add `--pin PIN_CODE` if you've added a pin code + to your account - signal-cli -u USERNAME verify CODE + signal-cli -a ACCOUNT verify CODE * Send a message - signal-cli -u USERNAME send -m "This is a message" RECIPIENT + signal-cli -a ACCOUNT send -m "This is a message" RECIPIENT * Pipe the message content from another process. - uname -a | signal-cli -u USERNAME send RECIPIENT + uname -a | signal-cli -a ACCOUNT send RECIPIENT * Receive messages - signal-cli -u USERNAME receive + signal-cli -a ACCOUNT receive -**Hint**: The Signal protocol expects that incoming messages are regularly received (using `daemon` or `receive` command). -This is required for the encryption to work efficiently and for getting updates to groups, expiration timer and other features. +**Hint**: The Signal protocol expects that incoming messages are regularly received (using `daemon` or `receive` +command). This is required for the encryption to work efficiently and for getting updates to groups, expiration timer +and other features. ## Storage @@ -70,8 +93,8 @@ The password and cryptographic keys are created when registering and stored in t ## Building -This project uses [Gradle](http://gradle.org) for building and maintaining -dependencies. If you have a recent gradle version installed, you can replace `./gradlew` with `gradle` in the following steps. +This project uses [Gradle](http://gradle.org) for building and maintaining dependencies. If you have a recent gradle +version installed, you can replace `./gradlew` with `gradle` in the following steps. 1. Checkout the source somewhere on your filesystem with @@ -81,26 +104,26 @@ dependencies. If you have a recent gradle version installed, you can replace `./ ./gradlew build - 2a. Create shell wrapper in *build/install/signal-cli/bin*: + 2a. Create shell wrapper in *build/install/signal-cli/bin*: ./gradlew installDist - 2b. Create tar file in *build/distributions*: + 2b. Create tar file in *build/distributions*: ./gradlew distTar - 2c. Create a fat tar file in *build/libs/signal-cli-fat*: + 2c. Create a fat tar file in *build/libs/signal-cli-fat*: ./gradlew fatJar - 2d. Compile and run signal-cli: + 2d. Compile and run signal-cli: ./gradlew run --args="--help" ### Building a native binary with GraalVM (EXPERIMENTAL) -It is possible to build a native binary with [GraalVM](https://www.graalvm.org). -This is still experimental and will not work in all situations. +It is possible to build a native binary with [GraalVM](https://www.graalvm.org). This is still experimental and will not +work in all situations. 1. [Install GraalVM and setup the enviroment](https://www.graalvm.org/docs/getting-started/#install-graalvm) 2. [Install prerequisites](https://www.graalvm.org/reference-manual/native-image/#prerequisites) @@ -111,6 +134,7 @@ This is still experimental and will not work in all situations. The binary is available at *build/native/nativeCompile/signal-cli* ## FAQ and Troubleshooting + For frequently asked questions and issues have a look at the [wiki](https://github.com/AsamK/signal-cli/wiki/FAQ) ## License diff --git a/data/signal-cli@.service b/data/signal-cli@.service index cb9821dd..4368a5e2 100644 --- a/data/signal-cli@.service +++ b/data/signal-cli@.service @@ -8,7 +8,7 @@ After=network-online.target [Service] Type=dbus Environment="SIGNAL_CLI_OPTS=-Xms2m" -ExecStart=%dir%/bin/signal-cli -u %I --config /var/lib/signal-cli daemon --system +ExecStart=%dir%/bin/signal-cli -a %I --config /var/lib/signal-cli daemon --system User=signal-cli BusName=org.asamk.Signal # JVM always exits with 143 in reaction to SIGTERM signal diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index a15c69c3..33ba1cd1 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -64,6 +64,9 @@ { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_IT\\E" }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MO\\E" + }, { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_PA\\E" }, diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 28a46161..a648b188 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -156,7 +156,7 @@ public class ManagerImpl implements Manager { this.serviceEnvironmentConfig = serviceEnvironmentConfig; final var credentialsProvider = new DynamicCredentialsProvider(account.getAci(), - account.getUsername(), + account.getAccount(), account.getPassword(), account.getDeviceId()); final var sessionLock = new SignalSessionLock() { @@ -251,7 +251,7 @@ public class ManagerImpl implements Manager { @Override public String getSelfNumber() { - return account.getUsername(); + return account.getAccount(); } @Override @@ -285,7 +285,7 @@ public class ManagerImpl implements Manager { public Map> areUsersRegistered(Set numbers) throws IOException { Map canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> { try { - return PhoneNumberFormatter.formatNumber(n, account.getUsername()); + return PhoneNumberFormatter.formatNumber(n, account.getAccount()); } catch (InvalidNumberException e) { return ""; } @@ -1265,7 +1265,7 @@ public class ManagerImpl implements Manager { /** * Trust this the identity with this fingerprint * - * @param recipient username of the identity + * @param recipient account of the identity * @param fingerprint Fingerprint */ @Override @@ -1282,7 +1282,7 @@ public class ManagerImpl implements Manager { /** * Trust this the identity with this safety number * - * @param recipient username of the identity + * @param recipient account of the identity * @param safetyNumber Safety number */ @Override @@ -1299,7 +1299,7 @@ public class ManagerImpl implements Manager { /** * Trust this the identity with this scannable safety number * - * @param recipient username of the identity + * @param recipient account of the identity * @param safetyNumber Scannable safety number */ @Override @@ -1316,7 +1316,7 @@ public class ManagerImpl implements Manager { /** * Trust all keys of this identity without verification * - * @param recipient username of the identity + * @param recipient account of the identity */ @Override public boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient) { diff --git a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java index 7f041845..903e145c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java @@ -22,7 +22,7 @@ public interface MultiAccountManager extends AutoCloseable { ProvisioningManager getNewProvisioningManager(); - RegistrationManager getNewRegistrationManager(String username) throws IOException; + RegistrationManager getNewRegistrationManager(String account) throws IOException; @Override void close(); diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java index 1a124021..24f0d5ba 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -88,7 +88,7 @@ public class RegistrationManager implements Closeable { this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), new DynamicCredentialsProvider( // Using empty UUID, because registering doesn't work otherwise - null, account.getUsername(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID), + null, account.getAccount(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, ServiceConfig.AUTOMATIC_NETWORK_RETRY); @@ -142,7 +142,7 @@ public class RegistrationManager implements Closeable { try { final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), new DynamicCredentialsProvider(account.getAci(), - account.getUsername(), + account.getAccount(), account.getPassword(), account.getDeviceId()), userAgent, diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index fcda9ec5..25ffe1fd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -195,7 +195,7 @@ public class StorageHelper { return; } - if (!accountRecord.getE164().equals(account.getUsername())) { + if (!accountRecord.getE164().equals(account.getAccount())) { // TODO implement changed number handling } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index cb078841..d7f48dca 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -77,7 +77,7 @@ public class SignalAccount implements Closeable { private final FileChannel fileChannel; private final FileLock lock; - private String username; + private String account; private ACI aci; private String encryptedDeviceName; private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; @@ -116,21 +116,21 @@ public class SignalAccount implements Closeable { } public static SignalAccount load( - File dataPath, String username, boolean waitForLock, final TrustNewIdentity trustNewIdentity + File dataPath, String account, boolean waitForLock, final TrustNewIdentity trustNewIdentity ) throws IOException { - final var fileName = getFileName(dataPath, username); + final var fileName = getFileName(dataPath, account); final var pair = openFileChannel(fileName, waitForLock); try { - var account = new SignalAccount(pair.first(), pair.second()); - account.load(dataPath, trustNewIdentity); - account.migrateLegacyConfigs(); + var signalAccount = new SignalAccount(pair.first(), pair.second()); + signalAccount.load(dataPath, trustNewIdentity); + signalAccount.migrateLegacyConfigs(); - if (!username.equals(account.getUsername())) { - throw new IOException("Username in account file doesn't match expected number: " - + account.getUsername()); + if (!account.equals(signalAccount.getAccount())) { + throw new IOException("Number in account file doesn't match expected number: " + + signalAccount.getAccount()); } - return account; + return signalAccount; } catch (Throwable e) { pair.second().close(); pair.first().close(); @@ -140,37 +140,37 @@ public class SignalAccount implements Closeable { public static SignalAccount create( File dataPath, - String username, + String account, IdentityKeyPair identityKey, int registrationId, ProfileKey profileKey, final TrustNewIdentity trustNewIdentity ) throws IOException { IOUtils.createPrivateDirectories(dataPath); - var fileName = getFileName(dataPath, username); + var fileName = getFileName(dataPath, account); if (!fileName.exists()) { IOUtils.createPrivateFile(fileName); } final var pair = openFileChannel(fileName, true); - var account = new SignalAccount(pair.first(), pair.second()); + var signalAccount = new SignalAccount(pair.first(), pair.second()); - account.username = username; - account.profileKey = profileKey; + signalAccount.account = account; + signalAccount.profileKey = profileKey; - account.initStores(dataPath, identityKey, registrationId, trustNewIdentity); - account.groupStore = new GroupStore(getGroupCachePath(dataPath, username), - account.recipientStore, - account::saveGroupStore); - account.stickerStore = new StickerStore(account::saveStickerStore); - account.configurationStore = new ConfigurationStore(account::saveConfigurationStore); + signalAccount.initStores(dataPath, identityKey, registrationId, trustNewIdentity); + signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account), + signalAccount.recipientStore, + signalAccount::saveGroupStore); + signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); + signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); - account.registered = false; + signalAccount.registered = false; - account.migrateLegacyConfigs(); - account.save(); + signalAccount.migrateLegacyConfigs(); + signalAccount.save(); - return account; + return signalAccount; } private void initStores( @@ -179,18 +179,18 @@ public class SignalAccount implements Closeable { final int registrationId, final TrustNewIdentity trustNewIdentity ) throws IOException { - recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, username), this::mergeRecipients); + recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, account), this::mergeRecipients); - preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, username)); - signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, username)); - sessionStore = new SessionStore(getSessionsPath(dataPath, username), recipientStore); - identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, username), + preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, account)); + signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, account)); + sessionStore = new SessionStore(getSessionsPath(dataPath, account), recipientStore); + identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, account), recipientStore, identityKey, registrationId, trustNewIdentity); - senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, username), - getSenderKeysPath(dataPath, username), + senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, account), + getSenderKeysPath(dataPath, account), recipientStore::resolveRecipientAddress, recipientStore); signalProtocolStore = new SignalProtocolStore(preKeyStore, @@ -200,12 +200,12 @@ public class SignalAccount implements Closeable { senderKeyStore, this::isMultiDevice); - messageCache = new MessageCache(getMessageCachePath(dataPath, username)); + messageCache = new MessageCache(getMessageCachePath(dataPath, account)); } public static SignalAccount createOrUpdateLinkedAccount( File dataPath, - String username, + String account, ACI aci, String password, String encryptedDeviceName, @@ -216,10 +216,10 @@ public class SignalAccount implements Closeable { final TrustNewIdentity trustNewIdentity ) throws IOException { IOUtils.createPrivateDirectories(dataPath); - var fileName = getFileName(dataPath, username); + var fileName = getFileName(dataPath, account); if (!fileName.exists()) { return createLinkedAccount(dataPath, - username, + account, aci, password, encryptedDeviceName, @@ -230,13 +230,13 @@ public class SignalAccount implements Closeable { trustNewIdentity); } - final var account = load(dataPath, username, true, trustNewIdentity); - account.setProvisioningData(username, aci, password, encryptedDeviceName, deviceId, profileKey); - account.recipientStore.resolveRecipientTrusted(account.getSelfAddress()); - account.sessionStore.archiveAllSessions(); - account.senderKeyStore.deleteAll(); - account.clearAllPreKeys(); - return account; + final var signalAccount = load(dataPath, account, true, trustNewIdentity); + signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey); + signalAccount.recipientStore.resolveRecipientTrusted(signalAccount.getSelfAddress()); + signalAccount.sessionStore.archiveAllSessions(); + signalAccount.senderKeyStore.deleteAll(); + signalAccount.clearAllPreKeys(); + return signalAccount; } private void clearAllPreKeys() { @@ -249,7 +249,7 @@ public class SignalAccount implements Closeable { private static SignalAccount createLinkedAccount( File dataPath, - String username, + String account, ACI aci, String password, String encryptedDeviceName, @@ -259,37 +259,37 @@ public class SignalAccount implements Closeable { ProfileKey profileKey, final TrustNewIdentity trustNewIdentity ) throws IOException { - var fileName = getFileName(dataPath, username); + var fileName = getFileName(dataPath, account); IOUtils.createPrivateFile(fileName); final var pair = openFileChannel(fileName, true); - var account = new SignalAccount(pair.first(), pair.second()); + var signalAccount = new SignalAccount(pair.first(), pair.second()); - account.setProvisioningData(username, aci, password, encryptedDeviceName, deviceId, profileKey); + signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey); - account.initStores(dataPath, identityKey, registrationId, trustNewIdentity); - account.groupStore = new GroupStore(getGroupCachePath(dataPath, username), - account.recipientStore, - account::saveGroupStore); - account.stickerStore = new StickerStore(account::saveStickerStore); - account.configurationStore = new ConfigurationStore(account::saveConfigurationStore); + signalAccount.initStores(dataPath, identityKey, registrationId, trustNewIdentity); + signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account), + signalAccount.recipientStore, + signalAccount::saveGroupStore); + signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); + signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); - account.recipientStore.resolveRecipientTrusted(account.getSelfAddress()); - account.migrateLegacyConfigs(); - account.save(); + signalAccount.recipientStore.resolveRecipientTrusted(signalAccount.getSelfAddress()); + signalAccount.migrateLegacyConfigs(); + signalAccount.save(); - return account; + return signalAccount; } private void setProvisioningData( - final String username, + final String account, final ACI aci, final String password, final String encryptedDeviceName, final int deviceId, final ProfileKey profileKey ) { - this.username = username; + this.account = account; this.aci = aci; this.password = password; this.profileKey = profileKey; @@ -324,12 +324,12 @@ public class SignalAccount implements Closeable { senderKeyStore.mergeRecipients(recipientId, toBeMergedRecipientId); } - public static File getFileName(File dataPath, String username) { - return new File(dataPath, username); + public static File getFileName(File dataPath, String account) { + return new File(dataPath, account); } - private static File getUserPath(final File dataPath, final String username) { - final var path = new File(dataPath, username + ".d"); + private static File getUserPath(final File dataPath, final String account) { + final var path = new File(dataPath, account + ".d"); try { IOUtils.createPrivateDirectories(path); } catch (IOException e) { @@ -338,47 +338,47 @@ public class SignalAccount implements Closeable { return path; } - private static File getMessageCachePath(File dataPath, String username) { - return new File(getUserPath(dataPath, username), "msg-cache"); + private static File getMessageCachePath(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "msg-cache"); } - private static File getGroupCachePath(File dataPath, String username) { - return new File(getUserPath(dataPath, username), "group-cache"); + private static File getGroupCachePath(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "group-cache"); } - private static File getPreKeysPath(File dataPath, String username) { - return new File(getUserPath(dataPath, username), "pre-keys"); + private static File getPreKeysPath(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "pre-keys"); } - private static File getSignedPreKeysPath(File dataPath, String username) { - return new File(getUserPath(dataPath, username), "signed-pre-keys"); + private static File getSignedPreKeysPath(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "signed-pre-keys"); } - private static File getIdentitiesPath(File dataPath, String username) { - return new File(getUserPath(dataPath, username), "identities"); + private static File getIdentitiesPath(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "identities"); } - private static File getSessionsPath(File dataPath, String username) { - return new File(getUserPath(dataPath, username), "sessions"); + private static File getSessionsPath(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "sessions"); } - private static File getSenderKeysPath(File dataPath, String username) { - return new File(getUserPath(dataPath, username), "sender-keys"); + private static File getSenderKeysPath(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "sender-keys"); } - private static File getSharedSenderKeysFile(File dataPath, String username) { - return new File(getUserPath(dataPath, username), "shared-sender-keys-store"); + private static File getSharedSenderKeysFile(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "shared-sender-keys-store"); } - private static File getRecipientsStoreFile(File dataPath, String username) { - return new File(getUserPath(dataPath, username), "recipients-store"); + private static File getRecipientsStoreFile(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "recipients-store"); } - public static boolean userExists(File dataPath, String username) { - if (username == null) { + public static boolean userExists(File dataPath, String account) { + if (account == null) { return false; } - var f = getFileName(dataPath, username); + var f = getFileName(dataPath, account); return !(!f.exists() || f.isDirectory()); } @@ -400,7 +400,7 @@ public class SignalAccount implements Closeable { } } - username = Utils.getNotNullNode(rootNode, "username").asText(); + account = Utils.getNotNullNode(rootNode, "username").asText(); password = Utils.getNotNullNode(rootNode, "password").asText(); registered = Utils.getNotNullNode(rootNode, "registered").asBoolean(); if (rootNode.hasNonNull("uuid")) { @@ -483,11 +483,11 @@ public class SignalAccount implements Closeable { if (rootNode.hasNonNull("groupStore")) { groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class); groupStore = GroupStore.fromStorage(groupStoreStorage, - getGroupCachePath(dataPath, username), + getGroupCachePath(dataPath, account), recipientStore, this::saveGroupStore); } else { - groupStore = new GroupStore(getGroupCachePath(dataPath, username), recipientStore, this::saveGroupStore); + groupStore = new GroupStore(getGroupCachePath(dataPath, account), recipientStore, this::saveGroupStore); } if (rootNode.hasNonNull("stickerStore")) { @@ -702,7 +702,7 @@ public class SignalAccount implements Closeable { synchronized (fileChannel) { var rootNode = jsonProcessor.createObjectNode(); rootNode.put("version", CURRENT_STORAGE_VERSION) - .put("username", username) + .put("username", account) .put("uuid", aci == null ? null : aci.toString()) .put("deviceName", encryptedDeviceName) .put("deviceId", deviceId) @@ -827,8 +827,8 @@ public class SignalAccount implements Closeable { return messageCache; } - public String getUsername() { - return username; + public String getAccount() { + return account; } public ACI getAci() { @@ -841,11 +841,11 @@ public class SignalAccount implements Closeable { } public SignalServiceAddress getSelfAddress() { - return new SignalServiceAddress(aci, username); + return new SignalServiceAddress(aci, account); } public RecipientId getSelfRecipientId() { - return recipientStore.resolveRecipientTrusted(new RecipientAddress(aci == null ? null : aci.uuid(), username)); + return recipientStore.resolveRecipientTrusted(new RecipientAddress(aci == null ? null : aci.uuid(), account)); } public String getEncryptedDeviceName() { diff --git a/man/signal-cli-dbus.5.adoc b/man/signal-cli-dbus.5.adoc index 75b4e8a1..5950b582 100755 --- a/man/signal-cli-dbus.5.adoc +++ b/man/signal-cli-dbus.5.adoc @@ -11,11 +11,11 @@ DBus API for signal-cli - A commandline and dbus interface for the Signal messen == Synopsis -*signal-cli* [--verbose] [--config CONFIG] [-u USERNAME] [-o {plain-text,json}] daemon [--system] +*signal-cli* [--verbose] [--config CONFIG] [-a ACCOUNT] [-o {plain-text,json}] daemon [--system] *dbus-send* [--system | --session] [--print-reply] --type=method_call --dest="org.asamk.Signal" /org/asamk/Signal[/_] org.asamk.Signal. [string:] [array::] -Note: when daemon was started without explicit `-u USERNAME`, the `dbus-send` command requires adding the phone number in `/org/asamk/Signal/_`. +Note: when daemon was started without explicit `-a ACCOUNT`, the `dbus-send` command requires adding the phone number in `/org/asamk/Signal/_`. == Description @@ -48,7 +48,7 @@ Phone numbers always have the format + == Methods === Control methods -These methods are available if the daemon is started anonymously (without an explicit `-u USERNAME`). +These methods are available if the daemon is started anonymously (without an explicit `-a ACCOUNT`). Requests are sent to `/org/asamk/Signal`; requests related to individual accounts are sent to `/org/asamk/Signal/_441234567890` where the + dialing code is replaced by an underscore (_). Only `version()` is activated in single-account mode; the rest are disabled. @@ -596,7 +596,7 @@ dbus-send --print-reply --type=method_call --dest="org.asamk.Signal" /org/asamk/ Send a group message:: dbus-send --session --print-reply --type=method_call --dest=org.asamk.Signal /org/asamk/Signal org.asamk.Signal.sendGroupMessage string:'The message goes here' array:string:'/path/to/attachmnt1','/path/to/attachmnt2' array:byte:139,22,72,247,116,32,170,104,205,164,207,21,248,77,185 -Print the group name corresponding to a groupId; the daemon runs on system bus, and was started without an explicit `-u USERNAME`:: +Print the group name corresponding to a groupId; the daemon runs on system bus, and was started without an explicit `-a ACCOUNT`:: dbus-send --system --print-reply --type=method_call --dest='org.asamk.Signal' /org/asamk/Signal/_1234567890 org.asamk.Signal.getGroupName array:byte:139,22,72,247,116,32,170,104,205,164,207,21,248,77,185 == Authors diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 334f7407..8eb0f0c2 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -11,7 +11,7 @@ signal-cli - A commandline and dbus interface for the Signal messenger == Synopsis -*signal-cli* [--config CONFIG] [-h | -v | -u USERNAME | --dbus | --dbus-system] command [command-options] +*signal-cli* [--config CONFIG] [-h | -v | -a ACCOUNT | --dbus | --dbus-system] command [command-options] == Description @@ -40,7 +40,7 @@ Set the path, where to store the config. Make sure you have full read/write access to the given directory. (Default: `$XDG_DATA_HOME/signal-cli` (`$HOME/.local/share/signal-cli`)) -*-u* USERNAME, *--username* USERNAME:: +*-a* ACCOUNT, *--account* ACCOUNT:: Specify your phone number, that will be your identifier. The phone number must include the country calling code, i.e. the number must start with a "+" sign. @@ -495,7 +495,7 @@ The path of the manifest.json or a zip file containing the sticker pack you wish === daemon signal-cli can run in daemon mode and provides an experimental dbus interface. -If no `-u` username is given, all local users will be exported as separate dbus +If no `-a` account is given, all local accounts will be exported as separate dbus objects under the same bus name. *--system*:: @@ -506,37 +506,37 @@ Don’t download attachments of received messages. == Examples Register a number (with SMS verification):: -signal-cli -u USERNAME register +signal-cli -a ACCOUNT register Verify the number using the code received via SMS or voice:: -signal-cli -u USERNAME verify CODE +signal-cli -a ACCOUNT verify CODE Send a message to one or more recipients:: -signal-cli -u USERNAME send -m "This is a message" [RECIPIENT [RECIPIENT ...]] [-a [ATTACHMENT [ATTACHMENT ...]]] +signal-cli -a ACCOUNT send -m "This is a message" [RECIPIENT [RECIPIENT ...]] [-a [ATTACHMENT [ATTACHMENT ...]]] Pipe the message content from another process:: -uname -a | signal-cli -u USERNAME send [RECIPIENT [RECIPIENT ...]] +uname -a | signal-cli -a ACCOUNT send [RECIPIENT [RECIPIENT ...]] Create a group:: -signal-cli -u USERNAME updateGroup -n "Group name" -m [MEMBER [MEMBER ...]] +signal-cli -a ACCOUNT updateGroup -n "Group name" -m [MEMBER [MEMBER ...]] Add member to a group:: -signal-cli -u USERNAME updateGroup -g GROUP_ID -m "NEW_MEMBER" +signal-cli -a ACCOUNT updateGroup -g GROUP_ID -m "NEW_MEMBER" Accept a group invitation:: -signal-cli -u USERNAME updateGroup -g GROUP_ID +signal-cli -a ACCOUNT updateGroup -g GROUP_ID Leave a group:: -signal-cli -u USERNAME quitGroup -g GROUP_ID +signal-cli -a ACCOUNT quitGroup -g GROUP_ID Send a message to a group:: -signal-cli -u USERNAME send -m "This is a message" -g GROUP_ID +signal-cli -a ACCOUNT send -m "This is a message" -g GROUP_ID Trust new key, after having verified it:: -signal-cli -u USERNAME trust -v SAFETY_NUMBER NUMBER +signal-cli -a ACCOUNT trust -v SAFETY_NUMBER NUMBER Trust new key, without having verified it. Only use this if you don't care about security:: -signal-cli -u USERNAME trust -a NUMBER +signal-cli -a ACCOUNT trust -a NUMBER == Exit codes * *1*: Error is probably caused and fixable by the user diff --git a/run_tests.sh b/run_tests.sh index b41a460d..5d8b8c1d 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -47,13 +47,13 @@ register() { PIN=$2 echo -n "Enter a captcha token (https://signalcaptchas.org/staging/challenge/generate.html): " read CAPTCHA - run_main -u "$NUMBER" register --captcha "$CAPTCHA" + run_main -a "$NUMBER" register --captcha "$CAPTCHA" echo -n "Enter validation code for ${NUMBER}: " read CODE if [ -z "$PIN" ]; then - run_main -u "$NUMBER" verify "$CODE" + run_main -a "$NUMBER" verify "$CODE" else - run_main -u "$NUMBER" verify "$CODE" --pin "$PIN" + run_main -a "$NUMBER" verify "$CODE" --pin "$PIN" fi } @@ -64,12 +64,12 @@ link() { mkfifo "$LINK_CODE_FILE" run_linked link -n "test-device" >"$LINK_CODE_FILE" & read LINK_CODE <"$LINK_CODE_FILE" - run_main -u "$NUMBER" addDevice --uri "$LINK_CODE" + run_main -a "$NUMBER" addDevice --uri "$LINK_CODE" wait - run_linked -u "$NUMBER" send --note-to-self -m hi - run_main -u "$NUMBER" receive - run_linked -u "$NUMBER" receive - run_main -u "$NUMBER" receive + run_linked -a "$NUMBER" send --note-to-self -m hi + run_main -a "$NUMBER" receive + run_linked -a "$NUMBER" receive + run_main -a "$NUMBER" receive } run_main --version @@ -83,12 +83,12 @@ sleep 5 ## DBus -#run_main -u "$NUMBER_1" --dbus send "$NUMBER_2" -m daemon_not_running || true +#run_main -a "$NUMBER_1" --dbus send "$NUMBER_2" -m daemon_not_running || true #run_main daemon & #DAEMON_PID=$! #sleep 10 -#run_main -u "$NUMBER_1" --dbus send "$NUMBER_2" -m hii -#run_main -u "$NUMBER_2" --dbus receive +#run_main -a "$NUMBER_1" --dbus send "$NUMBER_2" -m hii +#run_main -a "$NUMBER_2" --dbus receive #kill "$DAEMON_PID" @@ -98,8 +98,8 @@ FIFO_FILE="${PATH_MAIN}/dbus-fifo" rm -f "$FIFO_FILE" mkfifo "$FIFO_FILE" -run_main -u "$NUMBER_1" send "$NUMBER_2" -m hi -run_main -u "$NUMBER_2" jsonRpc < "$FIFO_FILE" & +run_main -a "$NUMBER_1" send "$NUMBER_2" -m hi +run_main -a "$NUMBER_2" jsonRpc < "$FIFO_FILE" & exec 3<> "$FIFO_FILE" echo '{"jsonrpc":"2.0","id":"id","method":"updateContact","params":{"recipient":"'"$NUMBER_1"'","name":"NUMBER_1","expiration":10}}' >&3 @@ -126,75 +126,75 @@ exec 3>&- wait -run_main -u "$NUMBER_1" setPin "$TEST_PIN_1" -run_main -u "$NUMBER_2" removePin +run_main -a "$NUMBER_1" setPin "$TEST_PIN_1" +run_main -a "$NUMBER_2" removePin ## Contacts -run_main -u "$NUMBER_2" updateContact "$NUMBER_1" -n NUMBER_1 -e 10 -run_main -u "$NUMBER_2" block "$NUMBER_1" -run_main -u "$NUMBER_2" unblock "$NUMBER_1" -run_main -u "$NUMBER_2" listContacts - -run_main -u "$NUMBER_1" send "$NUMBER_2" -m hi -run_main -u "$NUMBER_2" receive -run_main -u "$NUMBER_2" send "$NUMBER_1" -m hi -run_main -u "$NUMBER_1" receive -run_main -u "$NUMBER_2" receive +run_main -a "$NUMBER_2" updateContact "$NUMBER_1" -n NUMBER_1 -e 10 +run_main -a "$NUMBER_2" block "$NUMBER_1" +run_main -a "$NUMBER_2" unblock "$NUMBER_1" +run_main -a "$NUMBER_2" listContacts + +run_main -a "$NUMBER_1" send "$NUMBER_2" -m hi +run_main -a "$NUMBER_2" receive +run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi +run_main -a "$NUMBER_1" receive +run_main -a "$NUMBER_2" receive ## Groups -GROUP_ID=$(run_main -u "$NUMBER_1" updateGroup -n GRUPPE -a LICENSE -m "$NUMBER_1" | grep -oP '(?<=").+(?=")') -run_main -u "$NUMBER_1" send "$NUMBER_2" -m first -run_main -u "$NUMBER_1" updateGroup -g "$GROUP_ID" -n GRUPPE_UMB -m "$NUMBER_2" --admin "$NUMBER_2" --remove-admin "$NUMBER_2" --description DESCRIPTION --link=enabled-with-approval --set-permission-add-member=only-admins --set-permission-edit-details=only-admins -e 42 -run_main -u "$NUMBER_1" listGroups -d -run_main -u "$NUMBER_1" --output=json listGroups -d -run_main -u "$NUMBER_2" --verbose receive -run_main -u "$NUMBER_2" quitGroup -g "$GROUP_ID" -run_main -u "$NUMBER_2" listGroups -d -run_main -u "$NUMBER_2" --output=json listGroups -d -run_main -u "$NUMBER_1" receive -run_main -u "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2" -run_main -u "$NUMBER_1" --verbose block -g "$GROUP_ID" -run_main -u "$NUMBER_1" --verbose unblock -g "$GROUP_ID" +GROUP_ID=$(run_main -a "$NUMBER_1" updateGroup -n GRUPPE -a LICENSE -m "$NUMBER_1" | grep -oP '(?<=").+(?=")') +run_main -a "$NUMBER_1" send "$NUMBER_2" -m first +run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" -n GRUPPE_UMB -m "$NUMBER_2" --admin "$NUMBER_2" --remove-admin "$NUMBER_2" --description DESCRIPTION --link=enabled-with-approval --set-permission-add-member=only-admins --set-permission-edit-details=only-admins -e 42 +run_main -a "$NUMBER_1" listGroups -d +run_main -a "$NUMBER_1" --output=json listGroups -d +run_main -a "$NUMBER_2" --verbose receive +run_main -a "$NUMBER_2" quitGroup -g "$GROUP_ID" +run_main -a "$NUMBER_2" listGroups -d +run_main -a "$NUMBER_2" --output=json listGroups -d +run_main -a "$NUMBER_1" receive +run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2" +run_main -a "$NUMBER_1" --verbose block -g "$GROUP_ID" +run_main -a "$NUMBER_1" --verbose unblock -g "$GROUP_ID" ## Identities -run_main -u "$NUMBER_1" listIdentities -run_main -u "$NUMBER_2" listIdentities -run_main -u "$NUMBER_2" trust "$NUMBER_1" -a +run_main -a "$NUMBER_1" listIdentities +run_main -a "$NUMBER_2" listIdentities +run_main -a "$NUMBER_2" trust "$NUMBER_1" -a ## Basic send/receive for OUTPUT in "plain-text" "json"; do - run_main -u "$NUMBER_1" --output="$OUTPUT" getUserStatus "$NUMBER_1" "$NUMBER_2" "+111111111" - run_main -u "$NUMBER_1" send "$NUMBER_2" -m hi - run_main -u "$NUMBER_2" send "$NUMBER_1" -m hi - run_main -u "$NUMBER_1" send -g "$GROUP_ID" -m hi -a LICENSE - TIMESTAMP=$(uname -a | run_main -u "$NUMBER_1" send "$NUMBER_2") - run_main -u "$NUMBER_2" sendReaction "$NUMBER_1" -e 🍀 -a "$NUMBER_1" -t "$TIMESTAMP" - run_main -u "$NUMBER_1" remoteDelete "$NUMBER_2" -t "$TIMESTAMP" - run_main -u "$NUMBER_2" --output="$OUTPUT" receive - run_main -u "$NUMBER_1" --output="$OUTPUT" receive - run_main -u "$NUMBER_1" send -e "$NUMBER_2" - run_main -u "$NUMBER_2" --output="$OUTPUT" receive + run_main -a "$NUMBER_1" --output="$OUTPUT" getUserStatus "$NUMBER_1" "$NUMBER_2" "+111111111" + run_main -a "$NUMBER_1" send "$NUMBER_2" -m hi + run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi + run_main -a "$NUMBER_1" send -g "$GROUP_ID" -m hi -a LICENSE + TIMESTAMP=$(uname -a | run_main -a "$NUMBER_1" send "$NUMBER_2") + run_main -a "$NUMBER_2" sendReaction "$NUMBER_1" -e 🍀 -a "$NUMBER_1" -t "$TIMESTAMP" + run_main -a "$NUMBER_1" remoteDelete "$NUMBER_2" -t "$TIMESTAMP" + run_main -a "$NUMBER_2" --output="$OUTPUT" receive + run_main -a "$NUMBER_1" --output="$OUTPUT" receive + run_main -a "$NUMBER_1" send -e "$NUMBER_2" + run_main -a "$NUMBER_2" --output="$OUTPUT" receive done ## Profile -run_main -u "$NUMBER_1" updateProfile --given-name=GIVEN --family-name=FAMILY --about=ABOUT --about-emoji=EMOJI --avatar=LICENSE +run_main -a "$NUMBER_1" updateProfile --given-name=GIVEN --family-name=FAMILY --about=ABOUT --about-emoji=EMOJI --avatar=LICENSE ## Provisioning link "$NUMBER_1" link "$NUMBER_2" -run_main -u "$NUMBER_1" listDevices -run_linked -u "$NUMBER_1" sendSyncRequest -run_main -u "$NUMBER_1" sendContacts +run_main -a "$NUMBER_1" listDevices +run_linked -a "$NUMBER_1" sendSyncRequest +run_main -a "$NUMBER_1" sendContacts for OUTPUT in "plain-text" "json"; do - run_main -u "$NUMBER_1" send "$NUMBER_2" -m hi - run_main -u "$NUMBER_2" send "$NUMBER_1" -m hi - run_main -u "$NUMBER_2" --output="$OUTPUT" receive - run_main -u "$NUMBER_1" --output="$OUTPUT" receive - run_linked -u "$NUMBER_1" --output="$OUTPUT" receive + run_main -a "$NUMBER_1" send "$NUMBER_2" -m hi + run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi + run_main -a "$NUMBER_2" --output="$OUTPUT" receive + run_main -a "$NUMBER_1" --output="$OUTPUT" receive + run_linked -a "$NUMBER_1" --output="$OUTPUT" receive done -run_main -u "$NUMBER_1" removeDevice -d 2 +run_main -a "$NUMBER_1" removeDevice -d 2 ## Unregister -run_main -u "$NUMBER_1" unregister -run_main -u "$NUMBER_2" unregister --delete-account +run_main -a "$NUMBER_1" unregister +run_main -a "$NUMBER_2" unregister --delete-account diff --git a/src/main/java/org/asamk/signal/DbusConfig.java b/src/main/java/org/asamk/signal/DbusConfig.java index eb457c39..ddd534c4 100644 --- a/src/main/java/org/asamk/signal/DbusConfig.java +++ b/src/main/java/org/asamk/signal/DbusConfig.java @@ -13,11 +13,11 @@ public class DbusConfig { return getObjectPath(null); } - public static String getObjectPath(String username) { - if (username == null) { + public static String getObjectPath(String account) { + if (account == null) { return SIGNAL_OBJECT_BASE_PATH; } - return SIGNAL_OBJECT_BASE_PATH + "/" + username.replace('+', '_'); + return SIGNAL_OBJECT_BASE_PATH + "/" + account.replace('+', '_'); } } diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 4e44497f..c53e7583 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -49,13 +49,13 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { "The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message."); final var recipientName = e.getSender().getLegacyIdentifier(); writer.println( - "Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted", + "Use 'signal-cli -a {} listIdentities -n {}', verify the key and run 'signal-cli -a {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted", m.getSelfNumber(), recipientName, m.getSelfNumber(), recipientName); writer.println( - "If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification", + "If you don't care about security, use 'signal-cli -a {} trust -a {}' to trust it without verification", m.getSelfNumber(), recipientName); } else { diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java index 18d966ae..2e9ae08b 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java @@ -53,7 +53,7 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { ) throws Error.Failure, Error.InvalidNumber { if (!Manager.isValidNumber(number, null)) { throw new SignalControl.Error.InvalidNumber( - "Invalid username (phone number), make sure you include the country code."); + "Invalid account (phone number), make sure you include the country code."); } try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) { registrationManager.register(voiceVerification, captcha);