import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.accounts.AccountsStore;
-import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.asamk.signal.manager.util.KeyUtils;
import org.signal.libsignal.protocol.IdentityKeyPair;
import org.signal.libsignal.protocol.util.KeyHelper;
registrationId,
pniRegistrationId,
profileKey,
- TrustNewIdentity.ON_FIRST_USE);
+ Settings.DEFAULT);
ManagerImpl m = null;
try {
private boolean canRelinkExistingAccount(final String accountPath) throws IOException {
final SignalAccount signalAccount;
try {
- signalAccount = SignalAccount.load(pathConfig.dataPath(),
- accountPath,
- false,
- TrustNewIdentity.ON_FIRST_USE);
+ signalAccount = SignalAccount.load(pathConfig.dataPath(), accountPath, false, Settings.DEFAULT);
} catch (IOException e) {
logger.debug("Account in use or failed to load.", e);
return false;
--- /dev/null
+package org.asamk.signal.manager;
+
+import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
+
+public record Settings(TrustNewIdentity trustNewIdentity, boolean disableMessageSendLog) {
+
+ public static Settings DEFAULT = new Settings(TrustNewIdentity.ON_FIRST_USE, false);
+}
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.accounts.AccountsStore;
-import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.asamk.signal.manager.util.KeyUtils;
import org.signal.libsignal.protocol.util.KeyHelper;
import org.slf4j.Logger;
private final ServiceEnvironment serviceEnvironment;
private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent;
- private final TrustNewIdentity trustNewIdentity;
+ private final Settings settings;
private final AccountsStore accountsStore;
public SignalAccountFiles(
final File settingsPath,
final ServiceEnvironment serviceEnvironment,
final String userAgent,
- final TrustNewIdentity trustNewIdentity
+ final Settings settings
) throws IOException {
this.pathConfig = PathConfig.createDefault(settingsPath);
this.serviceEnvironment = serviceEnvironment;
this.serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(this.serviceEnvironment, userAgent);
this.userAgent = userAgent;
- this.trustNewIdentity = trustNewIdentity;
+ this.settings = settings;
this.accountsStore = new AccountsStore(pathConfig.dataPath(), serviceEnvironment, accountPath -> {
if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) {
return null;
}
try {
- return SignalAccount.load(pathConfig.dataPath(), accountPath, false, trustNewIdentity);
+ return SignalAccount.load(pathConfig.dataPath(), accountPath, false, settings);
} catch (Exception e) {
return null;
}
throw new NotRegisteredException();
}
- var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity);
+ var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, settings);
if (!number.equals(account.getNumber())) {
account.close();
throw new IOException("Number in account file doesn't match expected number: " + account.getNumber());
registrationId,
pniRegistrationId,
profileKey,
- trustNewIdentity);
+ settings);
return new RegistrationManagerImpl(account,
pathConfig,
new AccountFileUpdaterImpl(accountsStore, newAccountPath));
}
- var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity);
+ var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, settings);
if (!number.equals(account.getNumber())) {
account.close();
throw new IOException("Number in account file doesn't match expected number: " + account.getNumber());
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.asamk.signal.manager.Settings;
import org.asamk.signal.manager.api.Pair;
import org.asamk.signal.manager.api.TrustLevel;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.asamk.signal.manager.storage.identities.IdentityKeyStore;
import org.asamk.signal.manager.storage.identities.LegacyIdentityKeyStore;
import org.asamk.signal.manager.storage.identities.SignalIdentityKeyStore;
-import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.asamk.signal.manager.storage.messageCache.MessageCache;
import org.asamk.signal.manager.storage.prekeys.LegacyPreKeyStore;
import org.asamk.signal.manager.storage.prekeys.LegacySignedPreKeyStore;
private IdentityKeyPair pniIdentityKeyPair;
private int localRegistrationId;
private int localPniRegistrationId;
- private TrustNewIdentity trustNewIdentity;
+ private Settings settings;
private long lastReceiveTimestamp = 0;
private boolean registered = false;
}
public static SignalAccount load(
- File dataPath, String accountPath, boolean waitForLock, final TrustNewIdentity trustNewIdentity
+ File dataPath, String accountPath, boolean waitForLock, final Settings settings
) throws IOException {
logger.trace("Opening account file");
final var fileName = getFileName(dataPath, accountPath);
try {
var signalAccount = new SignalAccount(pair.first(), pair.second());
logger.trace("Loading account file");
- signalAccount.load(dataPath, accountPath, trustNewIdentity);
+ signalAccount.load(dataPath, accountPath, settings);
logger.trace("Migrating legacy parts of account file");
signalAccount.migrateLegacyConfigs();
int registrationId,
int pniRegistrationId,
ProfileKey profileKey,
- final TrustNewIdentity trustNewIdentity
+ final Settings settings
) throws IOException {
IOUtils.createPrivateDirectories(dataPath);
var fileName = getFileName(dataPath, accountPath);
signalAccount.pniIdentityKeyPair = pniIdentityKey;
signalAccount.localRegistrationId = registrationId;
signalAccount.localPniRegistrationId = pniRegistrationId;
- signalAccount.trustNewIdentity = trustNewIdentity;
+ signalAccount.settings = settings;
signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore);
signalAccount.registered = false;
int registrationId,
int pniRegistrationId,
ProfileKey profileKey,
- final TrustNewIdentity trustNewIdentity
+ final Settings settings
) throws IOException {
IOUtils.createPrivateDirectories(dataPath);
var fileName = getFileName(dataPath, accountPath);
registrationId,
pniRegistrationId,
profileKey,
- trustNewIdentity);
+ settings);
}
- final var signalAccount = load(dataPath, accountPath, true, trustNewIdentity);
+ final var signalAccount = load(dataPath, accountPath, true, settings);
signalAccount.setProvisioningData(number,
aci,
pni,
int registrationId,
int pniRegistrationId,
ProfileKey profileKey,
- final TrustNewIdentity trustNewIdentity
+ final Settings settings
) throws IOException {
var fileName = getFileName(dataPath, accountPath);
IOUtils.createPrivateFile(fileName);
signalAccount.serviceEnvironment = serviceEnvironment;
signalAccount.localRegistrationId = registrationId;
signalAccount.localPniRegistrationId = pniRegistrationId;
- signalAccount.trustNewIdentity = trustNewIdentity;
+ signalAccount.settings = settings;
signalAccount.setProvisioningData(number,
aci,
pni,
}
private void load(
- File dataPath, String accountPath, final TrustNewIdentity trustNewIdentity
+ File dataPath, String accountPath, final Settings settings
) throws IOException {
this.dataPath = dataPath;
this.accountPath = accountPath;
this.aciIdentityKeyPair = aciIdentityKeyPair;
this.localRegistrationId = registrationId;
- this.trustNewIdentity = trustNewIdentity;
+ this.settings = settings;
migratedLegacyConfig = loadLegacyStores(rootNode, legacySignalProtocolStore) || migratedLegacyConfig;
public IdentityKeyStore getIdentityKeyStore() {
return getOrCreate(() -> identityKeyStore,
- () -> identityKeyStore = new IdentityKeyStore(getAccountDatabase(), trustNewIdentity));
+ () -> identityKeyStore = new IdentityKeyStore(getAccountDatabase(), settings.trustNewIdentity()));
}
public SignalIdentityKeyStore getAciIdentityKeyStore() {
public MessageSendLogStore getMessageSendLogStore() {
return getOrCreate(() -> messageSendLogStore,
- () -> messageSendLogStore = new MessageSendLogStore(getAccountDatabase()));
+ () -> messageSendLogStore = new MessageSendLogStore(getAccountDatabase(),
+ settings.disableMessageSendLog()));
}
public CredentialsProvider getCredentialsProvider() {
private final Database database;
private final Thread cleanupThread;
+ private final boolean sendLogDisabled;
- public MessageSendLogStore(final Database database) {
+ public MessageSendLogStore(final Database database, final boolean disableMessageSendLog) {
this.database = database;
+ this.sendLogDisabled = disableMessageSendLog;
this.cleanupThread = new Thread(() -> {
try {
final var interval = Duration.ofHours(1).toMillis();
try (final var connection = database.getConnection()) {
deleteOutdatedEntries(connection);
} catch (SQLException e) {
+ logger.debug("MSL", e);
logger.warn("Deleting outdated entries failed");
break;
}
public long insertIfPossible(
long sentTimestamp, SendMessageResult sendMessageResult, ContentHint contentHint, boolean urgent
) {
+ if (sendLogDisabled) {
+ return -1;
+ }
final RecipientDevices recipientDevice = getRecipientDevices(sendMessageResult);
if (recipientDevice == null) {
return -1;
public long insertIfPossible(
long sentTimestamp, List<SendMessageResult> sendMessageResults, ContentHint contentHint, boolean urgent
) {
+ if (sendLogDisabled) {
+ return -1;
+ }
final var recipientDevices = sendMessageResults.stream()
.map(this::getRecipientDevices)
.filter(Objects::nonNull)
}
public void addRecipientToExistingEntryIfPossible(final long contentId, final SendMessageResult sendMessageResult) {
+ if (sendLogDisabled) {
+ return;
+ }
final RecipientDevices recipientDevice = getRecipientDevices(sendMessageResult);
if (recipientDevice == null) {
return;
public void addRecipientToExistingEntryIfPossible(
final long contentId, final List<SendMessageResult> sendMessageResults
) {
+ if (sendLogDisabled) {
+ return;
+ }
final var recipientDevices = sendMessageResults.stream()
.map(this::getRecipientDevices)
.filter(Objects::nonNull)
- `always`: Trust any new identity key without verification
- `never`: Don't trust any unknown identity key, every key must be verified manually
+*--disable-send-log*::
+Disable message send log (for resending messages that recipient couldn't decrypt).
+
== Commands
=== register
import org.asamk.signal.dbus.DbusRegistrationManagerImpl;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.RegistrationManager;
+import org.asamk.signal.manager.Settings;
import org.asamk.signal.manager.SignalAccountFiles;
import org.asamk.signal.manager.api.AccountCheckException;
import org.asamk.signal.manager.api.NotRegisteredException;
.type(Arguments.enumStringType(TrustNewIdentityCli.class))
.setDefault(TrustNewIdentityCli.ON_FIRST_USE);
+ parser.addArgument("--disable-send-log")
+ .help("Disable message send log (for resending messages that recipient couldn't decrypt)")
+ .action(Arguments.storeTrue());
+
var subparsers = parser.addSubparsers().title("subcommands").dest("command");
Commands.getCommandSubparserAttachers().forEach((key, value) -> {
? TrustNewIdentity.ON_FIRST_USE
: trustNewIdentityCli == TrustNewIdentityCli.ALWAYS ? TrustNewIdentity.ALWAYS : TrustNewIdentity.NEVER;
+ final var disableSendLog = Boolean.TRUE.equals(ns.getBoolean("disable-send-log"));
+
final SignalAccountFiles signalAccountFiles;
try {
signalAccountFiles = new SignalAccountFiles(configPath,
serviceEnvironment,
BaseConfig.USER_AGENT,
- trustNewIdentity);
+ new Settings(trustNewIdentity, disableSendLog));
} catch (IOException e) {
throw new IOErrorException("Failed to read local accounts list", e);
}