Rename username to account

This commit is contained in:
AsamK 2021-11-12 16:38:55 +01:00
parent 398cddaf29
commit 8aab644db9
14 changed files with 245 additions and 218 deletions

View file

@ -1,65 +1,88 @@
# signal-cli # 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. signal-cli is a commandline interface
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 [libsignal-service-java](https://github.com/WhisperSystems/libsignal-service-java). It supports registering,
For registering you need a phone number where you can receive SMS or incoming calls. verifying, sending and receiving messages. To be able to link to an existing Signal-Android/signal-cli instance,
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. signal-cli uses a [patched libsignal-service-java](https://github.com/AsamK/libsignal-service-java), because
It also has a JSON-RPC based interface, see the [documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service) for more information. 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 ## 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: System requirements:
- at least Java Runtime Environment (JRE) 17 - at least Java Runtime Environment (JRE) 17
- native libraries: libzkgroup, libsignal-client - 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 ### Install system-wide on Linux
See [latest version](https://github.com/AsamK/signal-cli/releases). See [latest version](https://github.com/AsamK/signal-cli/releases).
```sh ```sh
export VERSION=<latest version, format "x.y.z"> export VERSION=<latest version, format "x.y.z">
wget https://github.com/AsamK/signal-cli/releases/download/v"${VERSION}"/signal-cli-"${VERSION}".tar.gz 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 tar xf signal-cli-"${VERSION}".tar.gz -C /opt
sudo ln -sf /opt/signal-cli-"${VERSION}"/bin/signal-cli /usr/local/bin/ sudo ln -sf /opt/signal-cli-"${VERSION}"/bin/signal-cli /usr/local/bin/
``` ```
You can find further instructions on the Wiki: You can find further instructions on the Wiki:
- [Quickstart](https://github.com/AsamK/signal-cli/wiki/Quickstart) - [Quickstart](https://github.com/AsamK/signal-cli/wiki/Quickstart)
- [DBus Service](https://github.com/AsamK/signal-cli/wiki/DBus-service) - [DBus Service](https://github.com/AsamK/signal-cli/wiki/DBus-service)
## Usage ## 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) * 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 * 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. * 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 * 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). **Hint**: The Signal protocol expects that incoming messages are regularly received (using `daemon` or `receive`
This is required for the encryption to work efficiently and for getting updates to groups, expiration timer and other features. command). This is required for the encryption to work efficiently and for getting updates to groups, expiration timer
and other features.
## Storage ## Storage
@ -70,8 +93,8 @@ The password and cryptographic keys are created when registering and stored in t
## Building ## Building
This project uses [Gradle](http://gradle.org) for building and maintaining This project uses [Gradle](http://gradle.org) for building and maintaining dependencies. If you have a recent gradle
dependencies. If you have a recent gradle version installed, you can replace `./gradlew` with `gradle` in the following steps. version installed, you can replace `./gradlew` with `gradle` in the following steps.
1. Checkout the source somewhere on your filesystem with 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 ./gradlew build
2a. Create shell wrapper in *build/install/signal-cli/bin*: 2a. Create shell wrapper in *build/install/signal-cli/bin*:
./gradlew installDist ./gradlew installDist
2b. Create tar file in *build/distributions*: 2b. Create tar file in *build/distributions*:
./gradlew distTar ./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 ./gradlew fatJar
2d. Compile and run signal-cli: 2d. Compile and run signal-cli:
./gradlew run --args="--help" ./gradlew run --args="--help"
### Building a native binary with GraalVM (EXPERIMENTAL) ### Building a native binary with GraalVM (EXPERIMENTAL)
It is possible to build a native binary with [GraalVM](https://www.graalvm.org). It is possible to build a native binary with [GraalVM](https://www.graalvm.org). This is still experimental and will not
This is still experimental and will not work in all situations. work in all situations.
1. [Install GraalVM and setup the enviroment](https://www.graalvm.org/docs/getting-started/#install-graalvm) 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) 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* The binary is available at *build/native/nativeCompile/signal-cli*
## FAQ and Troubleshooting ## FAQ and Troubleshooting
For frequently asked questions and issues have a look at the [wiki](https://github.com/AsamK/signal-cli/wiki/FAQ) For frequently asked questions and issues have a look at the [wiki](https://github.com/AsamK/signal-cli/wiki/FAQ)
## License ## License

View file

@ -8,7 +8,7 @@ After=network-online.target
[Service] [Service]
Type=dbus Type=dbus
Environment="SIGNAL_CLI_OPTS=-Xms2m" 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 User=signal-cli
BusName=org.asamk.Signal BusName=org.asamk.Signal
# JVM always exits with 143 in reaction to SIGTERM signal # JVM always exits with 143 in reaction to SIGTERM signal

View file

@ -64,6 +64,9 @@
{ {
"pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_IT\\E" "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" "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_PA\\E"
}, },

View file

@ -156,7 +156,7 @@ public class ManagerImpl implements Manager {
this.serviceEnvironmentConfig = serviceEnvironmentConfig; this.serviceEnvironmentConfig = serviceEnvironmentConfig;
final var credentialsProvider = new DynamicCredentialsProvider(account.getAci(), final var credentialsProvider = new DynamicCredentialsProvider(account.getAci(),
account.getUsername(), account.getAccount(),
account.getPassword(), account.getPassword(),
account.getDeviceId()); account.getDeviceId());
final var sessionLock = new SignalSessionLock() { final var sessionLock = new SignalSessionLock() {
@ -251,7 +251,7 @@ public class ManagerImpl implements Manager {
@Override @Override
public String getSelfNumber() { public String getSelfNumber() {
return account.getUsername(); return account.getAccount();
} }
@Override @Override
@ -285,7 +285,7 @@ public class ManagerImpl implements Manager {
public Map<String, Pair<String, UUID>> areUsersRegistered(Set<String> numbers) throws IOException { public Map<String, Pair<String, UUID>> areUsersRegistered(Set<String> numbers) throws IOException {
Map<String, String> canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> { Map<String, String> canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> {
try { try {
return PhoneNumberFormatter.formatNumber(n, account.getUsername()); return PhoneNumberFormatter.formatNumber(n, account.getAccount());
} catch (InvalidNumberException e) { } catch (InvalidNumberException e) {
return ""; return "";
} }
@ -1265,7 +1265,7 @@ public class ManagerImpl implements Manager {
/** /**
* Trust this the identity with this fingerprint * Trust this the identity with this fingerprint
* *
* @param recipient username of the identity * @param recipient account of the identity
* @param fingerprint Fingerprint * @param fingerprint Fingerprint
*/ */
@Override @Override
@ -1282,7 +1282,7 @@ public class ManagerImpl implements Manager {
/** /**
* Trust this the identity with this safety number * Trust this the identity with this safety number
* *
* @param recipient username of the identity * @param recipient account of the identity
* @param safetyNumber Safety number * @param safetyNumber Safety number
*/ */
@Override @Override
@ -1299,7 +1299,7 @@ public class ManagerImpl implements Manager {
/** /**
* Trust this the identity with this scannable safety number * 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 * @param safetyNumber Scannable safety number
*/ */
@Override @Override
@ -1316,7 +1316,7 @@ public class ManagerImpl implements Manager {
/** /**
* Trust all keys of this identity without verification * Trust all keys of this identity without verification
* *
* @param recipient username of the identity * @param recipient account of the identity
*/ */
@Override @Override
public boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient) { public boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient) {

View file

@ -22,7 +22,7 @@ public interface MultiAccountManager extends AutoCloseable {
ProvisioningManager getNewProvisioningManager(); ProvisioningManager getNewProvisioningManager();
RegistrationManager getNewRegistrationManager(String username) throws IOException; RegistrationManager getNewRegistrationManager(String account) throws IOException;
@Override @Override
void close(); void close();

View file

@ -88,7 +88,7 @@ public class RegistrationManager implements Closeable {
this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider( new DynamicCredentialsProvider(
// Using empty UUID, because registering doesn't work otherwise // 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, userAgent,
groupsV2Operations, groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY); ServiceConfig.AUTOMATIC_NETWORK_RETRY);
@ -142,7 +142,7 @@ public class RegistrationManager implements Closeable {
try { try {
final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider(account.getAci(), new DynamicCredentialsProvider(account.getAci(),
account.getUsername(), account.getAccount(),
account.getPassword(), account.getPassword(),
account.getDeviceId()), account.getDeviceId()),
userAgent, userAgent,

View file

@ -195,7 +195,7 @@ public class StorageHelper {
return; return;
} }
if (!accountRecord.getE164().equals(account.getUsername())) { if (!accountRecord.getE164().equals(account.getAccount())) {
// TODO implement changed number handling // TODO implement changed number handling
} }

View file

@ -77,7 +77,7 @@ public class SignalAccount implements Closeable {
private final FileChannel fileChannel; private final FileChannel fileChannel;
private final FileLock lock; private final FileLock lock;
private String username; private String account;
private ACI aci; private ACI aci;
private String encryptedDeviceName; private String encryptedDeviceName;
private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
@ -116,21 +116,21 @@ public class SignalAccount implements Closeable {
} }
public static SignalAccount load( public static SignalAccount load(
File dataPath, String username, boolean waitForLock, final TrustNewIdentity trustNewIdentity File dataPath, String account, boolean waitForLock, final TrustNewIdentity trustNewIdentity
) throws IOException { ) throws IOException {
final var fileName = getFileName(dataPath, username); final var fileName = getFileName(dataPath, account);
final var pair = openFileChannel(fileName, waitForLock); final var pair = openFileChannel(fileName, waitForLock);
try { try {
var account = new SignalAccount(pair.first(), pair.second()); var signalAccount = new SignalAccount(pair.first(), pair.second());
account.load(dataPath, trustNewIdentity); signalAccount.load(dataPath, trustNewIdentity);
account.migrateLegacyConfigs(); signalAccount.migrateLegacyConfigs();
if (!username.equals(account.getUsername())) { if (!account.equals(signalAccount.getAccount())) {
throw new IOException("Username in account file doesn't match expected number: " throw new IOException("Number in account file doesn't match expected number: "
+ account.getUsername()); + signalAccount.getAccount());
} }
return account; return signalAccount;
} catch (Throwable e) { } catch (Throwable e) {
pair.second().close(); pair.second().close();
pair.first().close(); pair.first().close();
@ -140,37 +140,37 @@ public class SignalAccount implements Closeable {
public static SignalAccount create( public static SignalAccount create(
File dataPath, File dataPath,
String username, String account,
IdentityKeyPair identityKey, IdentityKeyPair identityKey,
int registrationId, int registrationId,
ProfileKey profileKey, ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity final TrustNewIdentity trustNewIdentity
) throws IOException { ) throws IOException {
IOUtils.createPrivateDirectories(dataPath); IOUtils.createPrivateDirectories(dataPath);
var fileName = getFileName(dataPath, username); var fileName = getFileName(dataPath, account);
if (!fileName.exists()) { if (!fileName.exists()) {
IOUtils.createPrivateFile(fileName); IOUtils.createPrivateFile(fileName);
} }
final var pair = openFileChannel(fileName, true); 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; signalAccount.account = account;
account.profileKey = profileKey; signalAccount.profileKey = profileKey;
account.initStores(dataPath, identityKey, registrationId, trustNewIdentity); signalAccount.initStores(dataPath, identityKey, registrationId, trustNewIdentity);
account.groupStore = new GroupStore(getGroupCachePath(dataPath, username), signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account),
account.recipientStore, signalAccount.recipientStore,
account::saveGroupStore); signalAccount::saveGroupStore);
account.stickerStore = new StickerStore(account::saveStickerStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore);
account.configurationStore = new ConfigurationStore(account::saveConfigurationStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore);
account.registered = false; signalAccount.registered = false;
account.migrateLegacyConfigs(); signalAccount.migrateLegacyConfigs();
account.save(); signalAccount.save();
return account; return signalAccount;
} }
private void initStores( private void initStores(
@ -179,18 +179,18 @@ public class SignalAccount implements Closeable {
final int registrationId, final int registrationId,
final TrustNewIdentity trustNewIdentity final TrustNewIdentity trustNewIdentity
) throws IOException { ) throws IOException {
recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, username), this::mergeRecipients); recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, account), this::mergeRecipients);
preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, username)); preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, account));
signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, username)); signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, account));
sessionStore = new SessionStore(getSessionsPath(dataPath, username), recipientStore); sessionStore = new SessionStore(getSessionsPath(dataPath, account), recipientStore);
identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, username), identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, account),
recipientStore, recipientStore,
identityKey, identityKey,
registrationId, registrationId,
trustNewIdentity); trustNewIdentity);
senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, username), senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, account),
getSenderKeysPath(dataPath, username), getSenderKeysPath(dataPath, account),
recipientStore::resolveRecipientAddress, recipientStore::resolveRecipientAddress,
recipientStore); recipientStore);
signalProtocolStore = new SignalProtocolStore(preKeyStore, signalProtocolStore = new SignalProtocolStore(preKeyStore,
@ -200,12 +200,12 @@ public class SignalAccount implements Closeable {
senderKeyStore, senderKeyStore,
this::isMultiDevice); this::isMultiDevice);
messageCache = new MessageCache(getMessageCachePath(dataPath, username)); messageCache = new MessageCache(getMessageCachePath(dataPath, account));
} }
public static SignalAccount createOrUpdateLinkedAccount( public static SignalAccount createOrUpdateLinkedAccount(
File dataPath, File dataPath,
String username, String account,
ACI aci, ACI aci,
String password, String password,
String encryptedDeviceName, String encryptedDeviceName,
@ -216,10 +216,10 @@ public class SignalAccount implements Closeable {
final TrustNewIdentity trustNewIdentity final TrustNewIdentity trustNewIdentity
) throws IOException { ) throws IOException {
IOUtils.createPrivateDirectories(dataPath); IOUtils.createPrivateDirectories(dataPath);
var fileName = getFileName(dataPath, username); var fileName = getFileName(dataPath, account);
if (!fileName.exists()) { if (!fileName.exists()) {
return createLinkedAccount(dataPath, return createLinkedAccount(dataPath,
username, account,
aci, aci,
password, password,
encryptedDeviceName, encryptedDeviceName,
@ -230,13 +230,13 @@ public class SignalAccount implements Closeable {
trustNewIdentity); trustNewIdentity);
} }
final var account = load(dataPath, username, true, trustNewIdentity); final var signalAccount = load(dataPath, account, true, trustNewIdentity);
account.setProvisioningData(username, aci, password, encryptedDeviceName, deviceId, profileKey); signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey);
account.recipientStore.resolveRecipientTrusted(account.getSelfAddress()); signalAccount.recipientStore.resolveRecipientTrusted(signalAccount.getSelfAddress());
account.sessionStore.archiveAllSessions(); signalAccount.sessionStore.archiveAllSessions();
account.senderKeyStore.deleteAll(); signalAccount.senderKeyStore.deleteAll();
account.clearAllPreKeys(); signalAccount.clearAllPreKeys();
return account; return signalAccount;
} }
private void clearAllPreKeys() { private void clearAllPreKeys() {
@ -249,7 +249,7 @@ public class SignalAccount implements Closeable {
private static SignalAccount createLinkedAccount( private static SignalAccount createLinkedAccount(
File dataPath, File dataPath,
String username, String account,
ACI aci, ACI aci,
String password, String password,
String encryptedDeviceName, String encryptedDeviceName,
@ -259,37 +259,37 @@ public class SignalAccount implements Closeable {
ProfileKey profileKey, ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity final TrustNewIdentity trustNewIdentity
) throws IOException { ) throws IOException {
var fileName = getFileName(dataPath, username); var fileName = getFileName(dataPath, account);
IOUtils.createPrivateFile(fileName); IOUtils.createPrivateFile(fileName);
final var pair = openFileChannel(fileName, true); 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); signalAccount.initStores(dataPath, identityKey, registrationId, trustNewIdentity);
account.groupStore = new GroupStore(getGroupCachePath(dataPath, username), signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account),
account.recipientStore, signalAccount.recipientStore,
account::saveGroupStore); signalAccount::saveGroupStore);
account.stickerStore = new StickerStore(account::saveStickerStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore);
account.configurationStore = new ConfigurationStore(account::saveConfigurationStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore);
account.recipientStore.resolveRecipientTrusted(account.getSelfAddress()); signalAccount.recipientStore.resolveRecipientTrusted(signalAccount.getSelfAddress());
account.migrateLegacyConfigs(); signalAccount.migrateLegacyConfigs();
account.save(); signalAccount.save();
return account; return signalAccount;
} }
private void setProvisioningData( private void setProvisioningData(
final String username, final String account,
final ACI aci, final ACI aci,
final String password, final String password,
final String encryptedDeviceName, final String encryptedDeviceName,
final int deviceId, final int deviceId,
final ProfileKey profileKey final ProfileKey profileKey
) { ) {
this.username = username; this.account = account;
this.aci = aci; this.aci = aci;
this.password = password; this.password = password;
this.profileKey = profileKey; this.profileKey = profileKey;
@ -324,12 +324,12 @@ public class SignalAccount implements Closeable {
senderKeyStore.mergeRecipients(recipientId, toBeMergedRecipientId); senderKeyStore.mergeRecipients(recipientId, toBeMergedRecipientId);
} }
public static File getFileName(File dataPath, String username) { public static File getFileName(File dataPath, String account) {
return new File(dataPath, username); return new File(dataPath, account);
} }
private static File getUserPath(final File dataPath, final String username) { private static File getUserPath(final File dataPath, final String account) {
final var path = new File(dataPath, username + ".d"); final var path = new File(dataPath, account + ".d");
try { try {
IOUtils.createPrivateDirectories(path); IOUtils.createPrivateDirectories(path);
} catch (IOException e) { } catch (IOException e) {
@ -338,47 +338,47 @@ public class SignalAccount implements Closeable {
return path; return path;
} }
private static File getMessageCachePath(File dataPath, String username) { private static File getMessageCachePath(File dataPath, String account) {
return new File(getUserPath(dataPath, username), "msg-cache"); return new File(getUserPath(dataPath, account), "msg-cache");
} }
private static File getGroupCachePath(File dataPath, String username) { private static File getGroupCachePath(File dataPath, String account) {
return new File(getUserPath(dataPath, username), "group-cache"); return new File(getUserPath(dataPath, account), "group-cache");
} }
private static File getPreKeysPath(File dataPath, String username) { private static File getPreKeysPath(File dataPath, String account) {
return new File(getUserPath(dataPath, username), "pre-keys"); return new File(getUserPath(dataPath, account), "pre-keys");
} }
private static File getSignedPreKeysPath(File dataPath, String username) { private static File getSignedPreKeysPath(File dataPath, String account) {
return new File(getUserPath(dataPath, username), "signed-pre-keys"); return new File(getUserPath(dataPath, account), "signed-pre-keys");
} }
private static File getIdentitiesPath(File dataPath, String username) { private static File getIdentitiesPath(File dataPath, String account) {
return new File(getUserPath(dataPath, username), "identities"); return new File(getUserPath(dataPath, account), "identities");
} }
private static File getSessionsPath(File dataPath, String username) { private static File getSessionsPath(File dataPath, String account) {
return new File(getUserPath(dataPath, username), "sessions"); return new File(getUserPath(dataPath, account), "sessions");
} }
private static File getSenderKeysPath(File dataPath, String username) { private static File getSenderKeysPath(File dataPath, String account) {
return new File(getUserPath(dataPath, username), "sender-keys"); return new File(getUserPath(dataPath, account), "sender-keys");
} }
private static File getSharedSenderKeysFile(File dataPath, String username) { private static File getSharedSenderKeysFile(File dataPath, String account) {
return new File(getUserPath(dataPath, username), "shared-sender-keys-store"); return new File(getUserPath(dataPath, account), "shared-sender-keys-store");
} }
private static File getRecipientsStoreFile(File dataPath, String username) { private static File getRecipientsStoreFile(File dataPath, String account) {
return new File(getUserPath(dataPath, username), "recipients-store"); return new File(getUserPath(dataPath, account), "recipients-store");
} }
public static boolean userExists(File dataPath, String username) { public static boolean userExists(File dataPath, String account) {
if (username == null) { if (account == null) {
return false; return false;
} }
var f = getFileName(dataPath, username); var f = getFileName(dataPath, account);
return !(!f.exists() || f.isDirectory()); 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(); password = Utils.getNotNullNode(rootNode, "password").asText();
registered = Utils.getNotNullNode(rootNode, "registered").asBoolean(); registered = Utils.getNotNullNode(rootNode, "registered").asBoolean();
if (rootNode.hasNonNull("uuid")) { if (rootNode.hasNonNull("uuid")) {
@ -483,11 +483,11 @@ public class SignalAccount implements Closeable {
if (rootNode.hasNonNull("groupStore")) { if (rootNode.hasNonNull("groupStore")) {
groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class); groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class);
groupStore = GroupStore.fromStorage(groupStoreStorage, groupStore = GroupStore.fromStorage(groupStoreStorage,
getGroupCachePath(dataPath, username), getGroupCachePath(dataPath, account),
recipientStore, recipientStore,
this::saveGroupStore); this::saveGroupStore);
} else { } else {
groupStore = new GroupStore(getGroupCachePath(dataPath, username), recipientStore, this::saveGroupStore); groupStore = new GroupStore(getGroupCachePath(dataPath, account), recipientStore, this::saveGroupStore);
} }
if (rootNode.hasNonNull("stickerStore")) { if (rootNode.hasNonNull("stickerStore")) {
@ -702,7 +702,7 @@ public class SignalAccount implements Closeable {
synchronized (fileChannel) { synchronized (fileChannel) {
var rootNode = jsonProcessor.createObjectNode(); var rootNode = jsonProcessor.createObjectNode();
rootNode.put("version", CURRENT_STORAGE_VERSION) rootNode.put("version", CURRENT_STORAGE_VERSION)
.put("username", username) .put("username", account)
.put("uuid", aci == null ? null : aci.toString()) .put("uuid", aci == null ? null : aci.toString())
.put("deviceName", encryptedDeviceName) .put("deviceName", encryptedDeviceName)
.put("deviceId", deviceId) .put("deviceId", deviceId)
@ -827,8 +827,8 @@ public class SignalAccount implements Closeable {
return messageCache; return messageCache;
} }
public String getUsername() { public String getAccount() {
return username; return account;
} }
public ACI getAci() { public ACI getAci() {
@ -841,11 +841,11 @@ public class SignalAccount implements Closeable {
} }
public SignalServiceAddress getSelfAddress() { public SignalServiceAddress getSelfAddress() {
return new SignalServiceAddress(aci, username); return new SignalServiceAddress(aci, account);
} }
public RecipientId getSelfRecipientId() { 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() { public String getEncryptedDeviceName() {

View file

@ -11,11 +11,11 @@ DBus API for signal-cli - A commandline and dbus interface for the Signal messen
== Synopsis == 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[/_<phonenum>] org.asamk.Signal.<method> [string:<string argument>] [array:<type>:<array argument>] *dbus-send* [--system | --session] [--print-reply] --type=method_call --dest="org.asamk.Signal" /org/asamk/Signal[/_<phonenum>] org.asamk.Signal.<method> [string:<string argument>] [array:<type>:<array argument>]
Note: when daemon was started without explicit `-u USERNAME`, the `dbus-send` command requires adding the phone number in `/org/asamk/Signal/_<phonenum>`. Note: when daemon was started without explicit `-a ACCOUNT`, the `dbus-send` command requires adding the phone number in `/org/asamk/Signal/_<phonenum>`.
== Description == Description
@ -48,7 +48,7 @@ Phone numbers always have the format +<countrycode><regional number>
== Methods == Methods
=== Control 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 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 (_). `/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. 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:: 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 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 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 == Authors

View file

@ -11,7 +11,7 @@ signal-cli - A commandline and dbus interface for the Signal messenger
== Synopsis == 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 == 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. Make sure you have full read/write access to the given directory.
(Default: `$XDG_DATA_HOME/signal-cli` (`$HOME/.local/share/signal-cli`)) (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. 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. 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 === daemon
signal-cli can run in daemon mode and provides an experimental dbus interface. 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. objects under the same bus name.
*--system*:: *--system*::
@ -506,37 +506,37 @@ Dont download attachments of received messages.
== Examples == Examples
Register a number (with SMS verification):: 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:: 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:: 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:: 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:: 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:: 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:: Accept a group invitation::
signal-cli -u USERNAME updateGroup -g GROUP_ID signal-cli -a ACCOUNT updateGroup -g GROUP_ID
Leave a group:: 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:: 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:: 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:: 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 == Exit codes
* *1*: Error is probably caused and fixable by the user * *1*: Error is probably caused and fixable by the user

View file

@ -47,13 +47,13 @@ register() {
PIN=$2 PIN=$2
echo -n "Enter a captcha token (https://signalcaptchas.org/staging/challenge/generate.html): " echo -n "Enter a captcha token (https://signalcaptchas.org/staging/challenge/generate.html): "
read CAPTCHA read CAPTCHA
run_main -u "$NUMBER" register --captcha "$CAPTCHA" run_main -a "$NUMBER" register --captcha "$CAPTCHA"
echo -n "Enter validation code for ${NUMBER}: " echo -n "Enter validation code for ${NUMBER}: "
read CODE read CODE
if [ -z "$PIN" ]; then if [ -z "$PIN" ]; then
run_main -u "$NUMBER" verify "$CODE" run_main -a "$NUMBER" verify "$CODE"
else else
run_main -u "$NUMBER" verify "$CODE" --pin "$PIN" run_main -a "$NUMBER" verify "$CODE" --pin "$PIN"
fi fi
} }
@ -64,12 +64,12 @@ link() {
mkfifo "$LINK_CODE_FILE" mkfifo "$LINK_CODE_FILE"
run_linked link -n "test-device" >"$LINK_CODE_FILE" & run_linked link -n "test-device" >"$LINK_CODE_FILE" &
read LINK_CODE <"$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 wait
run_linked -u "$NUMBER" send --note-to-self -m hi run_linked -a "$NUMBER" send --note-to-self -m hi
run_main -u "$NUMBER" receive run_main -a "$NUMBER" receive
run_linked -u "$NUMBER" receive run_linked -a "$NUMBER" receive
run_main -u "$NUMBER" receive run_main -a "$NUMBER" receive
} }
run_main --version run_main --version
@ -83,12 +83,12 @@ sleep 5
## DBus ## 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 & #run_main daemon &
#DAEMON_PID=$! #DAEMON_PID=$!
#sleep 10 #sleep 10
#run_main -u "$NUMBER_1" --dbus send "$NUMBER_2" -m hii #run_main -a "$NUMBER_1" --dbus send "$NUMBER_2" -m hii
#run_main -u "$NUMBER_2" --dbus receive #run_main -a "$NUMBER_2" --dbus receive
#kill "$DAEMON_PID" #kill "$DAEMON_PID"
@ -98,8 +98,8 @@ FIFO_FILE="${PATH_MAIN}/dbus-fifo"
rm -f "$FIFO_FILE" rm -f "$FIFO_FILE"
mkfifo "$FIFO_FILE" mkfifo "$FIFO_FILE"
run_main -u "$NUMBER_1" send "$NUMBER_2" -m hi run_main -a "$NUMBER_1" send "$NUMBER_2" -m hi
run_main -u "$NUMBER_2" jsonRpc < "$FIFO_FILE" & run_main -a "$NUMBER_2" jsonRpc < "$FIFO_FILE" &
exec 3<> "$FIFO_FILE" exec 3<> "$FIFO_FILE"
echo '{"jsonrpc":"2.0","id":"id","method":"updateContact","params":{"recipient":"'"$NUMBER_1"'","name":"NUMBER_1","expiration":10}}' >&3 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 wait
run_main -u "$NUMBER_1" setPin "$TEST_PIN_1" run_main -a "$NUMBER_1" setPin "$TEST_PIN_1"
run_main -u "$NUMBER_2" removePin run_main -a "$NUMBER_2" removePin
## Contacts ## Contacts
run_main -u "$NUMBER_2" updateContact "$NUMBER_1" -n NUMBER_1 -e 10 run_main -a "$NUMBER_2" updateContact "$NUMBER_1" -n NUMBER_1 -e 10
run_main -u "$NUMBER_2" block "$NUMBER_1" run_main -a "$NUMBER_2" block "$NUMBER_1"
run_main -u "$NUMBER_2" unblock "$NUMBER_1" run_main -a "$NUMBER_2" unblock "$NUMBER_1"
run_main -u "$NUMBER_2" listContacts run_main -a "$NUMBER_2" listContacts
run_main -u "$NUMBER_1" send "$NUMBER_2" -m hi run_main -a "$NUMBER_1" send "$NUMBER_2" -m hi
run_main -u "$NUMBER_2" receive run_main -a "$NUMBER_2" receive
run_main -u "$NUMBER_2" send "$NUMBER_1" -m hi run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi
run_main -u "$NUMBER_1" receive run_main -a "$NUMBER_1" receive
run_main -u "$NUMBER_2" receive run_main -a "$NUMBER_2" receive
## Groups ## Groups
GROUP_ID=$(run_main -u "$NUMBER_1" updateGroup -n GRUPPE -a LICENSE -m "$NUMBER_1" | grep -oP '(?<=").+(?=")') GROUP_ID=$(run_main -a "$NUMBER_1" updateGroup -n GRUPPE -a LICENSE -m "$NUMBER_1" | grep -oP '(?<=").+(?=")')
run_main -u "$NUMBER_1" send "$NUMBER_2" -m first run_main -a "$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 -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 -u "$NUMBER_1" listGroups -d run_main -a "$NUMBER_1" listGroups -d
run_main -u "$NUMBER_1" --output=json listGroups -d run_main -a "$NUMBER_1" --output=json listGroups -d
run_main -u "$NUMBER_2" --verbose receive run_main -a "$NUMBER_2" --verbose receive
run_main -u "$NUMBER_2" quitGroup -g "$GROUP_ID" run_main -a "$NUMBER_2" quitGroup -g "$GROUP_ID"
run_main -u "$NUMBER_2" listGroups -d run_main -a "$NUMBER_2" listGroups -d
run_main -u "$NUMBER_2" --output=json listGroups -d run_main -a "$NUMBER_2" --output=json listGroups -d
run_main -u "$NUMBER_1" receive run_main -a "$NUMBER_1" receive
run_main -u "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2" run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2"
run_main -u "$NUMBER_1" --verbose block -g "$GROUP_ID" run_main -a "$NUMBER_1" --verbose block -g "$GROUP_ID"
run_main -u "$NUMBER_1" --verbose unblock -g "$GROUP_ID" run_main -a "$NUMBER_1" --verbose unblock -g "$GROUP_ID"
## Identities ## Identities
run_main -u "$NUMBER_1" listIdentities run_main -a "$NUMBER_1" listIdentities
run_main -u "$NUMBER_2" listIdentities run_main -a "$NUMBER_2" listIdentities
run_main -u "$NUMBER_2" trust "$NUMBER_1" -a run_main -a "$NUMBER_2" trust "$NUMBER_1" -a
## Basic send/receive ## Basic send/receive
for OUTPUT in "plain-text" "json"; do for OUTPUT in "plain-text" "json"; do
run_main -u "$NUMBER_1" --output="$OUTPUT" getUserStatus "$NUMBER_1" "$NUMBER_2" "+111111111" run_main -a "$NUMBER_1" --output="$OUTPUT" getUserStatus "$NUMBER_1" "$NUMBER_2" "+111111111"
run_main -u "$NUMBER_1" send "$NUMBER_2" -m hi run_main -a "$NUMBER_1" send "$NUMBER_2" -m hi
run_main -u "$NUMBER_2" send "$NUMBER_1" -m hi run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi
run_main -u "$NUMBER_1" send -g "$GROUP_ID" -m hi -a LICENSE run_main -a "$NUMBER_1" send -g "$GROUP_ID" -m hi -a LICENSE
TIMESTAMP=$(uname -a | run_main -u "$NUMBER_1" send "$NUMBER_2") TIMESTAMP=$(uname -a | run_main -a "$NUMBER_1" send "$NUMBER_2")
run_main -u "$NUMBER_2" sendReaction "$NUMBER_1" -e 🍀 -a "$NUMBER_1" -t "$TIMESTAMP" run_main -a "$NUMBER_2" sendReaction "$NUMBER_1" -e 🍀 -a "$NUMBER_1" -t "$TIMESTAMP"
run_main -u "$NUMBER_1" remoteDelete "$NUMBER_2" -t "$TIMESTAMP" run_main -a "$NUMBER_1" remoteDelete "$NUMBER_2" -t "$TIMESTAMP"
run_main -u "$NUMBER_2" --output="$OUTPUT" receive run_main -a "$NUMBER_2" --output="$OUTPUT" receive
run_main -u "$NUMBER_1" --output="$OUTPUT" receive run_main -a "$NUMBER_1" --output="$OUTPUT" receive
run_main -u "$NUMBER_1" send -e "$NUMBER_2" run_main -a "$NUMBER_1" send -e "$NUMBER_2"
run_main -u "$NUMBER_2" --output="$OUTPUT" receive run_main -a "$NUMBER_2" --output="$OUTPUT" receive
done done
## Profile ## 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 ## Provisioning
link "$NUMBER_1" link "$NUMBER_1"
link "$NUMBER_2" link "$NUMBER_2"
run_main -u "$NUMBER_1" listDevices run_main -a "$NUMBER_1" listDevices
run_linked -u "$NUMBER_1" sendSyncRequest run_linked -a "$NUMBER_1" sendSyncRequest
run_main -u "$NUMBER_1" sendContacts run_main -a "$NUMBER_1" sendContacts
for OUTPUT in "plain-text" "json"; do for OUTPUT in "plain-text" "json"; do
run_main -u "$NUMBER_1" send "$NUMBER_2" -m hi run_main -a "$NUMBER_1" send "$NUMBER_2" -m hi
run_main -u "$NUMBER_2" send "$NUMBER_1" -m hi run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi
run_main -u "$NUMBER_2" --output="$OUTPUT" receive run_main -a "$NUMBER_2" --output="$OUTPUT" receive
run_main -u "$NUMBER_1" --output="$OUTPUT" receive run_main -a "$NUMBER_1" --output="$OUTPUT" receive
run_linked -u "$NUMBER_1" --output="$OUTPUT" receive run_linked -a "$NUMBER_1" --output="$OUTPUT" receive
done done
run_main -u "$NUMBER_1" removeDevice -d 2 run_main -a "$NUMBER_1" removeDevice -d 2
## Unregister ## Unregister
run_main -u "$NUMBER_1" unregister run_main -a "$NUMBER_1" unregister
run_main -u "$NUMBER_2" unregister --delete-account run_main -a "$NUMBER_2" unregister --delete-account

View file

@ -13,11 +13,11 @@ public class DbusConfig {
return getObjectPath(null); return getObjectPath(null);
} }
public static String getObjectPath(String username) { public static String getObjectPath(String account) {
if (username == null) { if (account == null) {
return SIGNAL_OBJECT_BASE_PATH; return SIGNAL_OBJECT_BASE_PATH;
} }
return SIGNAL_OBJECT_BASE_PATH + "/" + username.replace('+', '_'); return SIGNAL_OBJECT_BASE_PATH + "/" + account.replace('+', '_');
} }
} }

View file

@ -49,13 +49,13 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
"The users key is untrusted, either the user has reinstalled Signal or a third party sent this message."); "The users key is untrusted, either the user has reinstalled Signal or a third party sent this message.");
final var recipientName = e.getSender().getLegacyIdentifier(); final var recipientName = e.getSender().getLegacyIdentifier();
writer.println( 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(), m.getSelfNumber(),
recipientName, recipientName,
m.getSelfNumber(), m.getSelfNumber(),
recipientName); recipientName);
writer.println( 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(), m.getSelfNumber(),
recipientName); recipientName);
} else { } else {

View file

@ -53,7 +53,7 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl {
) throws Error.Failure, Error.InvalidNumber { ) throws Error.Failure, Error.InvalidNumber {
if (!Manager.isValidNumber(number, null)) { if (!Manager.isValidNumber(number, null)) {
throw new SignalControl.Error.InvalidNumber( 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)) { try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) {
registrationManager.register(voiceVerification, captcha); registrationManager.register(voiceVerification, captcha);