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 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=<latest version, format "x.y.z">
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

View file

@ -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

View file

@ -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"
},

View file

@ -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<String, Pair<String, UUID>> areUsersRegistered(Set<String> numbers) throws IOException {
Map<String, String> 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) {

View file

@ -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();

View file

@ -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,

View file

@ -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
}

View file

@ -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() {

View file

@ -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[/_<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
@ -48,7 +48,7 @@ Phone numbers always have the format +<countrycode><regional number>
== 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

View file

@ -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 @@ Dont 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

View file

@ -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 -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 -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_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

View file

@ -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('+', '_');
}
}

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.");
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 {

View file

@ -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);