Add SignalAccountFiles as a central entry point

This commit is contained in:
AsamK 2022-02-09 19:07:23 +01:00
parent 1db7f8d76e
commit ff6b733cd0
19 changed files with 398 additions and 304 deletions

View file

@ -9,7 +9,7 @@ public class LibSignalLogger implements SignalProtocolLogger {
private final static Logger logger = LoggerFactory.getLogger("LibSignal");
public static void initLogger() {
static void initLogger() {
SignalProtocolLoggerProvider.setProvider(new LibSignalLogger());
}

View file

@ -1,6 +1,5 @@
package org.asamk.signal.manager;
import org.asamk.signal.manager.api.AccountCheckException;
import org.asamk.signal.manager.api.AttachmentInvalidException;
import org.asamk.signal.manager.api.Configuration;
import org.asamk.signal.manager.api.Device;
@ -12,7 +11,6 @@ import org.asamk.signal.manager.api.InvalidStickerException;
import org.asamk.signal.manager.api.Message;
import org.asamk.signal.manager.api.MessageEnvelope;
import org.asamk.signal.manager.api.NotMasterDeviceException;
import org.asamk.signal.manager.api.NotRegisteredException;
import org.asamk.signal.manager.api.Pair;
import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.SendGroupMessageResults;
@ -23,16 +21,12 @@ import org.asamk.signal.manager.api.StickerPackUrl;
import org.asamk.signal.manager.api.TypingAction;
import org.asamk.signal.manager.api.UnregisteredRecipientException;
import org.asamk.signal.manager.api.UpdateGroup;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.groups.GroupInviteLinkUrl;
import org.asamk.signal.manager.groups.GroupNotFoundException;
import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.LastGroupAdminException;
import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.asamk.signal.manager.storage.recipients.Contact;
import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
@ -51,45 +45,6 @@ import java.util.UUID;
public interface Manager extends Closeable {
static Manager init(
String number,
File settingsPath,
ServiceEnvironment serviceEnvironment,
String userAgent,
TrustNewIdentity trustNewIdentity
) throws IOException, NotRegisteredException, AccountCheckException {
var pathConfig = PathConfig.createDefault(settingsPath);
if (!SignalAccount.userExists(pathConfig.dataPath(), number)) {
throw new NotRegisteredException();
}
var account = SignalAccount.load(pathConfig.dataPath(), number, true, trustNewIdentity);
if (!account.isRegistered()) {
account.close();
throw new NotRegisteredException();
}
account.initDatabase();
final var serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
final var manager = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
try {
manager.checkAccountState();
} catch (IOException e) {
manager.close();
throw new AccountCheckException("Error while checking account " + account + ": " + e.getMessage(), e);
}
return manager;
}
static void initLogger() {
LibSignalLogger.initLogger();
}
static boolean isValidNumber(final String e164Number, final String countryCode) {
return PhoneNumberFormatter.isValidNumber(e164Number, countryCode);
}

View file

@ -45,6 +45,7 @@ import org.asamk.signal.manager.groups.GroupNotFoundException;
import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.LastGroupAdminException;
import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.manager.helper.AccountFileUpdater;
import org.asamk.signal.manager.helper.Context;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.groups.GroupInfo;
@ -99,6 +100,7 @@ class ManagerImpl implements Manager {
private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class);
private SignalAccount account;
private final AccountFileUpdater accountFileUpdater;
private final SignalDependencies dependencies;
private final Context context;
@ -114,13 +116,15 @@ class ManagerImpl implements Manager {
ManagerImpl(
SignalAccount account,
PathConfig pathConfig,
AccountFileUpdater accountFileUpdater,
ServiceEnvironmentConfig serviceEnvironmentConfig,
String userAgent
) {
this.account = account;
this.accountFileUpdater = accountFileUpdater;
final var credentialsProvider = new DynamicCredentialsProvider(account.getAci(),
account.getAccount(),
account.getNumber(),
account.getPassword(),
account.getDeviceId());
final var sessionLock = new SignalSessionLock() {
@ -142,7 +146,12 @@ class ManagerImpl implements Manager {
final var attachmentStore = new AttachmentStore(pathConfig.attachmentsPath());
final var stickerPackStore = new StickerPackStore(pathConfig.stickerPacksPath());
this.context = new Context(account, dependencies, avatarStore, attachmentStore, stickerPackStore);
this.context = new Context(account,
accountFileUpdater,
dependencies,
avatarStore,
attachmentStore,
stickerPackStore);
this.context.getAccountHelper().setUnregisteredListener(this::close);
this.context.getReceiveHelper().setAuthenticationFailureListener(this::close);
this.context.getReceiveHelper().setCaughtUpWithOldMessagesListener(() -> {
@ -168,7 +177,7 @@ class ManagerImpl implements Manager {
@Override
public String getSelfNumber() {
return account.getAccount();
return account.getNumber();
}
void checkAccountState() throws IOException {
@ -179,7 +188,7 @@ class ManagerImpl implements Manager {
public Map<String, Pair<String, UUID>> areUsersRegistered(Set<String> numbers) throws IOException {
final var canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> {
try {
final var canonicalizedNumber = PhoneNumberFormatter.formatNumber(n, account.getAccount());
final var canonicalizedNumber = PhoneNumberFormatter.formatNumber(n, account.getNumber());
if (!canonicalizedNumber.equals(n)) {
logger.debug("Normalized number {} to {}.", n, canonicalizedNumber);
}

View file

@ -0,0 +1,8 @@
package org.asamk.signal.manager;
public class ManagerLogger {
public static void initLogger() {
LibSignalLogger.initLogger();
}
}

View file

@ -1,58 +1,13 @@
package org.asamk.signal.manager;
import org.asamk.signal.manager.api.AccountCheckException;
import org.asamk.signal.manager.api.NotRegisteredException;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
public interface MultiAccountManager extends AutoCloseable {
static MultiAccountManager init(
final File settingsPath,
final ServiceEnvironment serviceEnvironment,
final String userAgent,
final TrustNewIdentity trustNewIdentity
) {
final var logger = LoggerFactory.getLogger(MultiAccountManager.class);
final var managers = getAllLocalAccountNumbers(settingsPath).stream().map(a -> {
try {
return Manager.init(a, settingsPath, serviceEnvironment, userAgent, trustNewIdentity);
} catch (NotRegisteredException | IOException | AccountCheckException e) {
logger.warn("Ignoring {}: {} ({})", a, e.getMessage(), e.getClass().getSimpleName());
return null;
}
}).filter(Objects::nonNull).toList();
return new MultiAccountManagerImpl(managers, settingsPath, serviceEnvironment, userAgent);
}
static List<String> getAllLocalAccountNumbers(File settingsPath) {
var pathConfig = PathConfig.createDefault(settingsPath);
final var dataPath = pathConfig.dataPath();
final var files = dataPath.listFiles();
if (files == null) {
return List.of();
}
return Arrays.stream(files)
.filter(File::isFile)
.map(File::getName)
.filter(file -> PhoneNumberFormatter.isValidNumber(file, null))
.toList();
}
List<String> getAccountNumbers();
List<Manager> getManagers();

View file

@ -1,10 +1,8 @@
package org.asamk.signal.manager;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
@ -25,21 +23,12 @@ class MultiAccountManagerImpl implements MultiAccountManager {
private final Set<Consumer<Manager>> onManagerRemovedHandlers = new HashSet<>();
private final Set<Manager> managers = new HashSet<>();
private final Map<URI, ProvisioningManager> provisioningManagers = new HashMap<>();
private final File settingsPath;
private final ServiceEnvironment serviceEnvironment;
private final String userAgent;
private final SignalAccountFiles signalAccountFiles;
public MultiAccountManagerImpl(
final Collection<Manager> managers,
final File settingsPath,
final ServiceEnvironment serviceEnvironment,
final String userAgent
) {
public MultiAccountManagerImpl(final Collection<Manager> managers, final SignalAccountFiles signalAccountFiles) {
this.signalAccountFiles = signalAccountFiles;
this.managers.addAll(managers);
managers.forEach(m -> m.addClosedListener(() -> this.removeManager(m)));
this.settingsPath = settingsPath;
this.serviceEnvironment = serviceEnvironment;
this.userAgent = userAgent;
}
@Override
@ -99,9 +88,9 @@ class MultiAccountManagerImpl implements MultiAccountManager {
}
@Override
public Manager getManager(final String account) {
public Manager getManager(final String number) {
synchronized (managers) {
return managers.stream().filter(m -> m.getSelfNumber().equals(account)).findFirst().orElse(null);
return managers.stream().filter(m -> m.getSelfNumber().equals(number)).findFirst().orElse(null);
}
}
@ -119,12 +108,12 @@ class MultiAccountManagerImpl implements MultiAccountManager {
}
private ProvisioningManager getNewProvisioningManager() {
return ProvisioningManager.init(settingsPath, serviceEnvironment, userAgent, this::addManager);
return signalAccountFiles.initProvisioningManager(this::addManager);
}
@Override
public RegistrationManager getNewRegistrationManager(String account) throws IOException {
return RegistrationManager.init(account, settingsPath, serviceEnvironment, userAgent, this::addManager);
public RegistrationManager getNewRegistrationManager(String number) throws IOException {
return signalAccountFiles.initRegistrationManager(number, this::addManager);
}
@Override

View file

@ -1,36 +1,13 @@
package org.asamk.signal.manager;
import org.asamk.signal.manager.api.UserAlreadyExistsException;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironment;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
public interface ProvisioningManager {
static ProvisioningManager init(
File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
) {
return init(settingsPath, serviceEnvironment, userAgent, null);
}
static ProvisioningManager init(
File settingsPath,
ServiceEnvironment serviceEnvironment,
String userAgent,
Consumer<Manager> newManagerListener
) {
var pathConfig = PathConfig.createDefault(settingsPath);
final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
return new ProvisioningManagerImpl(pathConfig, serviceConfiguration, userAgent, newManagerListener);
}
URI getDeviceLinkUri() throws TimeoutException, IOException;
String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExistsException;

View file

@ -20,6 +20,7 @@ import org.asamk.signal.manager.api.UserAlreadyExistsException;
import org.asamk.signal.manager.config.ServiceConfig;
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.slf4j.Logger;
@ -47,6 +48,7 @@ class ProvisioningManagerImpl implements ProvisioningManager {
private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent;
private final Consumer<Manager> newManagerListener;
private final AccountsStore accountsStore;
private final SignalServiceAccountManager accountManager;
private final IdentityKeyPair tempIdentityKey;
@ -57,12 +59,14 @@ class ProvisioningManagerImpl implements ProvisioningManager {
PathConfig pathConfig,
ServiceEnvironmentConfig serviceEnvironmentConfig,
String userAgent,
final Consumer<Manager> newManagerListener
final Consumer<Manager> newManagerListener,
final AccountsStore accountsStore
) {
this.pathConfig = pathConfig;
this.serviceEnvironmentConfig = serviceEnvironmentConfig;
this.userAgent = userAgent;
this.newManagerListener = newManagerListener;
this.accountsStore = accountsStore;
tempIdentityKey = KeyUtils.generateIdentityKeyPair();
registrationId = KeyHelper.generateRegistrationId(false);
@ -91,11 +95,23 @@ class ProvisioningManagerImpl implements ProvisioningManager {
public String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExistsException {
var ret = accountManager.getNewDeviceRegistration(tempIdentityKey);
var number = ret.getNumber();
var aci = ret.getAci();
logger.info("Received link information from {}, linking in progress ...", number);
if (SignalAccount.userExists(pathConfig.dataPath(), number) && !canRelinkExistingAccount(number)) {
throw new UserAlreadyExistsException(number, SignalAccount.getFileName(pathConfig.dataPath(), number));
var accountPath = accountsStore.getPathByAci(aci);
if (accountPath == null) {
accountPath = accountsStore.getPathByNumber(number);
}
if (accountPath != null
&& SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)
&& !canRelinkExistingAccount(accountPath)) {
throw new UserAlreadyExistsException(number, SignalAccount.getFileName(pathConfig.dataPath(), accountPath));
}
if (accountPath == null) {
accountPath = accountsStore.addAccount(number, aci);
} else {
accountsStore.updateAccount(accountPath, number, aci);
}
var encryptedDeviceName = deviceName == null
@ -115,8 +131,9 @@ class ProvisioningManagerImpl implements ProvisioningManager {
SignalAccount account = null;
try {
account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.dataPath(),
accountPath,
number,
ret.getAci(),
aci,
password,
encryptedDeviceName,
deviceId,
@ -127,7 +144,12 @@ class ProvisioningManagerImpl implements ProvisioningManager {
ManagerImpl m = null;
try {
m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
final var accountPathFinal = accountPath;
m = new ManagerImpl(account,
pathConfig,
(newNumber, newAci) -> accountsStore.updateAccount(accountPathFinal, newNumber, newAci),
serviceEnvironmentConfig,
userAgent);
account = null;
logger.debug("Refreshing pre keys");
@ -162,10 +184,13 @@ class ProvisioningManagerImpl implements ProvisioningManager {
}
}
private boolean canRelinkExistingAccount(final String number) throws IOException {
private boolean canRelinkExistingAccount(final String accountPath) throws IOException {
final SignalAccount signalAccount;
try {
signalAccount = SignalAccount.load(pathConfig.dataPath(), number, false, TrustNewIdentity.ON_FIRST_USE);
signalAccount = SignalAccount.load(pathConfig.dataPath(),
accountPath,
false,
TrustNewIdentity.ON_FIRST_USE);
} catch (IOException e) {
logger.debug("Account in use or failed to load.", e);
return false;
@ -177,7 +202,11 @@ class ProvisioningManagerImpl implements ProvisioningManager {
return false;
}
final var m = new ManagerImpl(signalAccount, pathConfig, serviceEnvironmentConfig, userAgent);
final var m = new ManagerImpl(signalAccount,
pathConfig,
(newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci),
serviceEnvironmentConfig,
userAgent);
try (m) {
m.checkAccountState();
} catch (AuthorizationFailedException ignored) {

View file

@ -3,60 +3,12 @@ package org.asamk.signal.manager;
import org.asamk.signal.manager.api.CaptchaRequiredException;
import org.asamk.signal.manager.api.IncorrectPinException;
import org.asamk.signal.manager.api.PinLockedException;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.asamk.signal.manager.util.KeyUtils;
import org.whispersystems.libsignal.util.KeyHelper;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.function.Consumer;
public interface RegistrationManager extends Closeable {
static RegistrationManager init(
String number, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
) throws IOException {
return init(number, settingsPath, serviceEnvironment, userAgent, null);
}
static RegistrationManager init(
String number,
File settingsPath,
ServiceEnvironment serviceEnvironment,
String userAgent,
Consumer<Manager> newManagerListener
) throws IOException {
var pathConfig = PathConfig.createDefault(settingsPath);
final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
if (!SignalAccount.userExists(pathConfig.dataPath(), number)) {
var identityKey = KeyUtils.generateIdentityKeyPair();
var registrationId = KeyHelper.generateRegistrationId(false);
var profileKey = KeyUtils.createProfileKey();
var account = SignalAccount.create(pathConfig.dataPath(),
number,
identityKey,
registrationId,
profileKey,
TrustNewIdentity.ON_FIRST_USE);
return new RegistrationManagerImpl(account,
pathConfig,
serviceConfiguration,
userAgent,
newManagerListener);
}
var account = SignalAccount.load(pathConfig.dataPath(), number, true, TrustNewIdentity.ON_FIRST_USE);
return new RegistrationManagerImpl(account, pathConfig, serviceConfiguration, userAgent, newManagerListener);
}
void register(boolean voiceVerification, String captcha) throws IOException, CaptchaRequiredException;
void verifyAccount(

View file

@ -21,6 +21,7 @@ import org.asamk.signal.manager.api.IncorrectPinException;
import org.asamk.signal.manager.api.PinLockedException;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.asamk.signal.manager.helper.AccountFileUpdater;
import org.asamk.signal.manager.helper.PinHelper;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.util.Utils;
@ -59,16 +60,19 @@ class RegistrationManagerImpl implements RegistrationManager {
private final SignalServiceAccountManager accountManager;
private final PinHelper pinHelper;
private final AccountFileUpdater accountFileUpdater;
RegistrationManagerImpl(
SignalAccount account,
PathConfig pathConfig,
ServiceEnvironmentConfig serviceEnvironmentConfig,
String userAgent,
Consumer<Manager> newManagerListener
Consumer<Manager> newManagerListener,
AccountFileUpdater accountFileUpdater
) {
this.account = account;
this.pathConfig = pathConfig;
this.accountFileUpdater = accountFileUpdater;
this.serviceEnvironmentConfig = serviceEnvironmentConfig;
this.userAgent = userAgent;
this.newManagerListener = newManagerListener;
@ -82,7 +86,7 @@ class RegistrationManagerImpl implements RegistrationManager {
this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider(
// Using empty UUID, because registering doesn't work otherwise
null, account.getAccount(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID),
null, account.getNumber(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID),
userAgent,
groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
@ -101,7 +105,7 @@ class RegistrationManagerImpl implements RegistrationManager {
try {
final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider(account.getAci(),
account.getAccount(),
account.getNumber(),
account.getPassword(),
account.getDeviceId()),
userAgent,
@ -120,7 +124,11 @@ class RegistrationManagerImpl implements RegistrationManager {
account.setRegistered(true);
logger.info("Reactivated existing account, verify is not necessary.");
if (newManagerListener != null) {
final var m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
final var m = new ManagerImpl(account,
pathConfig,
accountFileUpdater,
serviceEnvironmentConfig,
userAgent);
account = null;
newManagerListener.accept(m);
}
@ -187,11 +195,13 @@ class RegistrationManagerImpl implements RegistrationManager {
}
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
account.finishRegistration(ACI.parseOrNull(response.getUuid()), masterKey, pin);
final var aci = ACI.parseOrNull(response.getUuid());
account.finishRegistration(aci, masterKey, pin);
accountFileUpdater.updateAccountIdentifiers(account.getNumber(), aci);
ManagerImpl m = null;
try {
m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
m = new ManagerImpl(account, pathConfig, accountFileUpdater, serviceEnvironmentConfig, userAgent);
account = null;
m.refreshPreKeys();

View file

@ -0,0 +1,152 @@
package org.asamk.signal.manager;
import org.asamk.signal.manager.api.AccountCheckException;
import org.asamk.signal.manager.api.NotRegisteredException;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironment;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.util.KeyHelper;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
public class SignalAccountFiles {
private static final Logger logger = LoggerFactory.getLogger(MultiAccountManager.class);
private final PathConfig pathConfig;
private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent;
private final TrustNewIdentity trustNewIdentity;
private final AccountsStore accountsStore;
public SignalAccountFiles(
final File settingsPath,
final ServiceEnvironment serviceEnvironment,
final String userAgent,
final TrustNewIdentity trustNewIdentity
) {
this.pathConfig = PathConfig.createDefault(settingsPath);
this.serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
this.userAgent = userAgent;
this.trustNewIdentity = trustNewIdentity;
this.accountsStore = new AccountsStore(pathConfig.dataPath());
}
public Set<String> getAllLocalAccountNumbers() {
return accountsStore.getAllNumbers();
}
public MultiAccountManager initMultiAccountManager() {
final var managers = getAllLocalAccountNumbers().stream().map(a -> {
try {
return initManager(a);
} catch (NotRegisteredException | IOException | AccountCheckException e) {
logger.warn("Ignoring {}: {} ({})", a, e.getMessage(), e.getClass().getSimpleName());
return null;
}
}).filter(Objects::nonNull).toList();
return new MultiAccountManagerImpl(managers, this);
}
public Manager initManager(String number) throws IOException, NotRegisteredException, AccountCheckException {
final var accountPath = accountsStore.getPathByNumber(number);
if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) {
throw new NotRegisteredException();
}
var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity);
if (!number.equals(account.getNumber())) {
account.close();
throw new IOException("Number in account file doesn't match expected number: " + account.getNumber());
}
if (!account.isRegistered()) {
account.close();
throw new NotRegisteredException();
}
account.initDatabase();
final var manager = new ManagerImpl(account,
pathConfig,
(newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci),
serviceEnvironmentConfig,
userAgent);
try {
manager.checkAccountState();
} catch (IOException e) {
manager.close();
throw new AccountCheckException("Error while checking account " + number + ": " + e.getMessage(), e);
}
return manager;
}
public ProvisioningManager initProvisioningManager() {
return initProvisioningManager(null);
}
public ProvisioningManager initProvisioningManager(Consumer<Manager> newManagerListener) {
return new ProvisioningManagerImpl(pathConfig,
serviceEnvironmentConfig,
userAgent,
newManagerListener,
accountsStore);
}
public RegistrationManager initRegistrationManager(String number) throws IOException {
return initRegistrationManager(number, null);
}
public RegistrationManager initRegistrationManager(
String number, Consumer<Manager> newManagerListener
) throws IOException {
final var accountPath = accountsStore.getPathByNumber(number);
if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) {
final var newAccountPath = accountPath == null ? accountsStore.addAccount(number, null) : accountPath;
var identityKey = KeyUtils.generateIdentityKeyPair();
var registrationId = KeyHelper.generateRegistrationId(false);
var profileKey = KeyUtils.createProfileKey();
var account = SignalAccount.create(pathConfig.dataPath(),
newAccountPath,
number,
identityKey,
registrationId,
profileKey,
trustNewIdentity);
return new RegistrationManagerImpl(account,
pathConfig,
serviceEnvironmentConfig,
userAgent,
newManagerListener,
(newNumber, newAci) -> accountsStore.updateAccount(newAccountPath, newNumber, newAci));
}
var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity);
if (!number.equals(account.getNumber())) {
account.close();
throw new IOException("Number in account file doesn't match expected number: " + account.getNumber());
}
return new RegistrationManagerImpl(account,
pathConfig,
serviceEnvironmentConfig,
userAgent,
newManagerListener,
(newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci));
}
}

View file

@ -0,0 +1,8 @@
package org.asamk.signal.manager.helper;
import org.whispersystems.signalservice.api.push.ACI;
public interface AccountFileUpdater {
void updateAccountIdentifiers(String number, ACI aci);
}

View file

@ -52,7 +52,9 @@ public class AccountHelper {
try {
context.getPreKeyHelper().refreshPreKeysIfNecessary();
if (account.getAci() == null) {
account.setAci(ACI.parseOrNull(dependencies.getAccountManager().getWhoAmI().getAci()));
final var aci = ACI.parseOrNull(dependencies.getAccountManager().getWhoAmI().getAci());
account.setAci(aci);
context.getAccountFileUpdater().updateAccountIdentifiers(account.getNumber(), aci);
}
updateAccountAttributes();
} catch (AuthorizationFailedException e) {

View file

@ -4,8 +4,8 @@ import org.asamk.signal.manager.AttachmentStore;
import org.asamk.signal.manager.AvatarStore;
import org.asamk.signal.manager.JobExecutor;
import org.asamk.signal.manager.SignalDependencies;
import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore;
import java.util.function.Supplier;
@ -14,6 +14,7 @@ public class Context {
private final Object LOCK = new Object();
private final SignalAccount account;
private final AccountFileUpdater accountFileUpdater;
private final SignalDependencies dependencies;
private final AvatarStore avatarStore;
private final StickerPackStore stickerPackStore;
@ -40,12 +41,14 @@ public class Context {
public Context(
final SignalAccount account,
final AccountFileUpdater accountFileUpdater,
final SignalDependencies dependencies,
final AvatarStore avatarStore,
final AttachmentStore attachmentStore,
final StickerPackStore stickerPackStore
) {
this.account = account;
this.accountFileUpdater = accountFileUpdater;
this.dependencies = dependencies;
this.avatarStore = avatarStore;
this.stickerPackStore = stickerPackStore;
@ -57,6 +60,10 @@ public class Context {
return account;
}
public AccountFileUpdater getAccountFileUpdater() {
return accountFileUpdater;
}
public SignalDependencies getDependencies() {
return dependencies;
}

View file

@ -1,8 +1,8 @@
package org.asamk.signal.manager.helper;
import org.asamk.signal.manager.SignalDependencies;
import org.asamk.signal.manager.api.TrustLevel;
import org.asamk.signal.manager.api.PhoneNumberSharingMode;
import org.asamk.signal.manager.api.TrustLevel;
import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.recipients.Contact;
@ -188,7 +188,7 @@ public class StorageHelper {
return;
}
if (!accountRecord.getE164().equals(account.getAccount())) {
if (!accountRecord.getE164().equals(account.getNumber())) {
// TODO implement changed number handling
}

View file

@ -3,8 +3,8 @@ package org.asamk.signal.manager.storage;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.asamk.signal.manager.api.TrustLevel;
import org.asamk.signal.manager.api.Pair;
import org.asamk.signal.manager.api.TrustLevel;
import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.storage.configuration.ConfigurationStore;
import org.asamk.signal.manager.storage.contacts.ContactsStore;
@ -87,7 +87,8 @@ public class SignalAccount implements Closeable {
private int previousStorageVersion;
private File dataPath;
private String account;
private String accountPath;
private String number;
private ACI aci;
private String encryptedDeviceName;
private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
@ -132,23 +133,18 @@ public class SignalAccount implements Closeable {
}
public static SignalAccount load(
File dataPath, String account, boolean waitForLock, final TrustNewIdentity trustNewIdentity
File dataPath, String accountPath, boolean waitForLock, final TrustNewIdentity trustNewIdentity
) throws IOException {
logger.trace("Opening account file");
final var fileName = getFileName(dataPath, account);
final var fileName = getFileName(dataPath, accountPath);
final var pair = openFileChannel(fileName, waitForLock);
try {
var signalAccount = new SignalAccount(pair.first(), pair.second());
logger.trace("Loading account file");
signalAccount.load(dataPath, trustNewIdentity);
signalAccount.load(dataPath, accountPath, trustNewIdentity);
logger.trace("Migrating legacy parts of account file");
signalAccount.migrateLegacyConfigs();
if (!account.equals(signalAccount.getAccount())) {
throw new IOException("Number in account file doesn't match expected number: "
+ signalAccount.getAccount());
}
return signalAccount;
} catch (Throwable e) {
pair.second().close();
@ -159,14 +155,15 @@ public class SignalAccount implements Closeable {
public static SignalAccount create(
File dataPath,
String account,
String accountPath,
String number,
IdentityKeyPair identityKey,
int registrationId,
ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity
) throws IOException {
IOUtils.createPrivateDirectories(dataPath);
var fileName = getFileName(dataPath, account);
var fileName = getFileName(dataPath, accountPath);
if (!fileName.exists()) {
IOUtils.createPrivateFile(fileName);
}
@ -174,14 +171,15 @@ public class SignalAccount implements Closeable {
final var pair = openFileChannel(fileName, true);
var signalAccount = new SignalAccount(pair.first(), pair.second());
signalAccount.account = account;
signalAccount.accountPath = accountPath;
signalAccount.number = number;
signalAccount.profileKey = profileKey;
signalAccount.dataPath = dataPath;
signalAccount.identityKeyPair = identityKey;
signalAccount.localRegistrationId = registrationId;
signalAccount.trustNewIdentity = trustNewIdentity;
signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account),
signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath),
signalAccount.getRecipientStore(),
signalAccount::saveGroupStore);
signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore);
@ -198,7 +196,8 @@ public class SignalAccount implements Closeable {
public static SignalAccount createOrUpdateLinkedAccount(
File dataPath,
String account,
String accountPath,
String number,
ACI aci,
String password,
String encryptedDeviceName,
@ -209,10 +208,11 @@ public class SignalAccount implements Closeable {
final TrustNewIdentity trustNewIdentity
) throws IOException {
IOUtils.createPrivateDirectories(dataPath);
var fileName = getFileName(dataPath, account);
var fileName = getFileName(dataPath, accountPath);
if (!fileName.exists()) {
return createLinkedAccount(dataPath,
account,
accountPath,
number,
aci,
password,
encryptedDeviceName,
@ -223,8 +223,8 @@ public class SignalAccount implements Closeable {
trustNewIdentity);
}
final var signalAccount = load(dataPath, account, true, trustNewIdentity);
signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey);
final var signalAccount = load(dataPath, accountPath, true, trustNewIdentity);
signalAccount.setProvisioningData(number, aci, password, encryptedDeviceName, deviceId, profileKey);
signalAccount.getRecipientStore().resolveRecipientTrusted(signalAccount.getSelfAddress());
signalAccount.getSessionStore().archiveAllSessions();
signalAccount.getSenderKeyStore().deleteAll();
@ -246,7 +246,8 @@ public class SignalAccount implements Closeable {
private static SignalAccount createLinkedAccount(
File dataPath,
String account,
String accountPath,
String number,
ACI aci,
String password,
String encryptedDeviceName,
@ -256,19 +257,20 @@ public class SignalAccount implements Closeable {
ProfileKey profileKey,
final TrustNewIdentity trustNewIdentity
) throws IOException {
var fileName = getFileName(dataPath, account);
var fileName = getFileName(dataPath, accountPath);
IOUtils.createPrivateFile(fileName);
final var pair = openFileChannel(fileName, true);
var signalAccount = new SignalAccount(pair.first(), pair.second());
signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey);
signalAccount.setProvisioningData(number, aci, password, encryptedDeviceName, deviceId, profileKey);
signalAccount.dataPath = dataPath;
signalAccount.accountPath = accountPath;
signalAccount.identityKeyPair = identityKey;
signalAccount.localRegistrationId = registrationId;
signalAccount.trustNewIdentity = trustNewIdentity;
signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account),
signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath),
signalAccount.getRecipientStore(),
signalAccount::saveGroupStore);
signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore);
@ -283,14 +285,14 @@ public class SignalAccount implements Closeable {
}
private void setProvisioningData(
final String account,
final String number,
final ACI aci,
final String password,
final String encryptedDeviceName,
final int deviceId,
final ProfileKey profileKey
) {
this.account = account;
this.number = number;
this.aci = aci;
this.password = password;
this.profileKey = profileKey;
@ -396,7 +398,7 @@ public class SignalAccount implements Closeable {
return new File(getUserPath(dataPath, account), "account.db");
}
public static boolean userExists(File dataPath, String account) {
public static boolean accountFileExists(File dataPath, String account) {
if (account == null) {
return false;
}
@ -405,9 +407,11 @@ public class SignalAccount implements Closeable {
}
private void load(
File dataPath, final TrustNewIdentity trustNewIdentity
File dataPath, String accountPath, final TrustNewIdentity trustNewIdentity
) throws IOException {
JsonNode rootNode;
this.dataPath = dataPath;
this.accountPath = accountPath;
final JsonNode rootNode;
synchronized (fileChannel) {
fileChannel.position(0);
rootNode = jsonProcessor.readTree(Channels.newInputStream(fileChannel));
@ -423,7 +427,7 @@ public class SignalAccount implements Closeable {
previousStorageVersion = accountVersion;
}
account = Utils.getNotNullNode(rootNode, "username").asText();
number = Utils.getNotNullNode(rootNode, "username").asText();
if (rootNode.hasNonNull("password")) {
password = rootNode.get("password").asText();
}
@ -501,7 +505,6 @@ public class SignalAccount implements Closeable {
migratedLegacyConfig = true;
}
this.dataPath = dataPath;
this.identityKeyPair = identityKeyPair;
this.localRegistrationId = registrationId;
this.trustNewIdentity = trustNewIdentity;
@ -511,11 +514,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, account),
getGroupCachePath(dataPath, accountPath),
getRecipientStore(),
this::saveGroupStore);
} else {
groupStore = new GroupStore(getGroupCachePath(dataPath, account),
groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath),
getRecipientStore(),
this::saveGroupStore);
}
@ -732,7 +735,7 @@ public class SignalAccount implements Closeable {
synchronized (fileChannel) {
var rootNode = jsonProcessor.createObjectNode();
rootNode.put("version", CURRENT_STORAGE_VERSION)
.put("username", account)
.put("username", number)
.put("uuid", aci == null ? null : aci.toString())
.put("deviceName", encryptedDeviceName)
.put("deviceId", deviceId)
@ -821,22 +824,23 @@ public class SignalAccount implements Closeable {
}
private PreKeyStore getPreKeyStore() {
return getOrCreate(() -> preKeyStore, () -> preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, account)));
return getOrCreate(() -> preKeyStore,
() -> preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, accountPath)));
}
private SignedPreKeyStore getSignedPreKeyStore() {
return getOrCreate(() -> signedPreKeyStore,
() -> signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, account)));
() -> signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, accountPath)));
}
public SessionStore getSessionStore() {
return getOrCreate(() -> sessionStore,
() -> sessionStore = new SessionStore(getSessionsPath(dataPath, account), getRecipientStore()));
() -> sessionStore = new SessionStore(getSessionsPath(dataPath, accountPath), getRecipientStore()));
}
public IdentityKeyStore getIdentityKeyStore() {
return getOrCreate(() -> identityKeyStore,
() -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, account),
() -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath),
getRecipientStore(),
identityKeyPair,
localRegistrationId,
@ -853,7 +857,7 @@ public class SignalAccount implements Closeable {
public RecipientStore getRecipientStore() {
return getOrCreate(() -> recipientStore,
() -> recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, account),
() -> recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, accountPath),
this::mergeRecipients));
}
@ -867,8 +871,8 @@ public class SignalAccount implements Closeable {
public SenderKeyStore getSenderKeyStore() {
return getOrCreate(() -> senderKeyStore,
() -> senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, account),
getSenderKeysPath(dataPath, account),
() -> senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, accountPath),
getSenderKeysPath(dataPath, accountPath),
getRecipientStore()::resolveRecipientAddress,
getRecipientStore()));
}
@ -879,13 +883,13 @@ public class SignalAccount implements Closeable {
public MessageCache getMessageCache() {
return getOrCreate(() -> messageCache,
() -> messageCache = new MessageCache(getMessageCachePath(dataPath, account)));
() -> messageCache = new MessageCache(getMessageCachePath(dataPath, accountPath)));
}
public AccountDatabase getAccountDatabase() {
return getOrCreate(() -> accountDatabase, () -> {
try {
accountDatabase = AccountDatabase.init(getDatabaseFile(dataPath, account));
accountDatabase = AccountDatabase.init(getDatabaseFile(dataPath, accountPath));
} catch (SQLException e) {
throw new RuntimeException(e);
}
@ -897,8 +901,8 @@ public class SignalAccount implements Closeable {
() -> messageSendLogStore = new MessageSendLogStore(getRecipientStore(), getAccountDatabase()));
}
public String getAccount() {
return account;
public String getNumber() {
return number;
}
public ACI getAci() {
@ -911,11 +915,11 @@ public class SignalAccount implements Closeable {
}
public SignalServiceAddress getSelfAddress() {
return new SignalServiceAddress(aci, account);
return new SignalServiceAddress(aci, number);
}
public RecipientAddress getSelfRecipientAddress() {
return new RecipientAddress(aci == null ? null : aci.uuid(), account);
return new RecipientAddress(aci == null ? null : aci.uuid(), number);
}
public RecipientId getSelfRecipientId() {

View file

@ -0,0 +1,52 @@
package org.asamk.signal.manager.storage.accounts;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import java.io.File;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
public class AccountsStore {
private final File dataPath;
public AccountsStore(final File dataPath) {
this.dataPath = dataPath;
}
public Set<String> getAllNumbers() {
final var files = dataPath.listFiles();
if (files == null) {
return Set.of();
}
return Arrays.stream(files)
.filter(File::isFile)
.map(File::getName)
.filter(file -> PhoneNumberFormatter.isValidNumber(file, null))
.collect(Collectors.toSet());
}
public String getPathByNumber(String number) {
return number;
}
public String getPathByAci(ACI aci) {
return null;
}
public void updateAccount(String path, String number, ACI aci) {
// TODO remove number and uuid from all other accounts
if (!path.equals(number)) {
throw new UnsupportedOperationException("Updating number not supported yet");
}
}
public String addAccount(String number, ACI aci) {
// TODO remove number and uuid from all other accounts
return number;
}
}

View file

@ -22,9 +22,8 @@ import org.asamk.signal.dbus.DbusMultiAccountManagerImpl;
import org.asamk.signal.dbus.DbusProvisioningManagerImpl;
import org.asamk.signal.dbus.DbusRegistrationManagerImpl;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.MultiAccountManager;
import org.asamk.signal.manager.ProvisioningManager;
import org.asamk.signal.manager.RegistrationManager;
import org.asamk.signal.manager.SignalAccountFiles;
import org.asamk.signal.manager.api.AccountCheckException;
import org.asamk.signal.manager.api.NotRegisteredException;
import org.asamk.signal.manager.config.ServiceConfig;
@ -164,26 +163,27 @@ public class App {
? TrustNewIdentity.ON_FIRST_USE
: trustNewIdentityCli == TrustNewIdentityCli.ALWAYS ? TrustNewIdentity.ALWAYS : TrustNewIdentity.NEVER;
final SignalAccountFiles signalAccountFiles = new SignalAccountFiles(configPath,
serviceEnvironment,
BaseConfig.USER_AGENT,
trustNewIdentity);
if (command instanceof ProvisioningCommand provisioningCommand) {
if (account != null) {
throw new UserErrorException("You cannot specify a account (phone number) when linking");
}
handleProvisioningCommand(provisioningCommand, configPath, serviceEnvironment, outputWriter);
handleProvisioningCommand(provisioningCommand, signalAccountFiles, outputWriter);
return;
}
if (account == null) {
if (command instanceof MultiLocalCommand multiLocalCommand) {
handleMultiLocalCommand(multiLocalCommand,
configPath,
serviceEnvironment,
outputWriter,
trustNewIdentity);
handleMultiLocalCommand(multiLocalCommand, signalAccountFiles, outputWriter);
return;
}
var accounts = MultiAccountManager.getAllLocalAccountNumbers(configPath);
var accounts = signalAccountFiles.getAllLocalAccountNumbers();
if (accounts.size() == 0) {
throw new UserErrorException("No local users found, you first need to register or link an account");
} else if (accounts.size() > 1) {
@ -191,13 +191,13 @@ public class App {
"Multiple users found, you need to specify an account (phone number) with -a");
}
account = accounts.get(0);
account = accounts.stream().findFirst().get();
} else if (!Manager.isValidNumber(account, null)) {
throw new UserErrorException("Invalid account (phone number), make sure you include the country code.");
}
if (command instanceof RegistrationCommand registrationCommand) {
handleRegistrationCommand(registrationCommand, account, configPath, serviceEnvironment);
handleRegistrationCommand(registrationCommand, account, signalAccountFiles);
return;
}
@ -205,21 +205,15 @@ public class App {
throw new UserErrorException("Command only works in multi-account mode");
}
handleLocalCommand((LocalCommand) command,
account,
configPath,
serviceEnvironment,
outputWriter,
trustNewIdentity);
handleLocalCommand((LocalCommand) command, account, signalAccountFiles, outputWriter);
}
private void handleProvisioningCommand(
final ProvisioningCommand command,
final File configPath,
final ServiceEnvironment serviceEnvironment,
final SignalAccountFiles signalAccountFiles,
final OutputWriter outputWriter
) throws CommandException {
var pm = ProvisioningManager.init(configPath, serviceEnvironment, BaseConfig.USER_AGENT);
var pm = signalAccountFiles.initProvisioningManager();
command.handleCommand(ns, pm, outputWriter);
}
@ -240,22 +234,9 @@ public class App {
}
private void handleRegistrationCommand(
final RegistrationCommand command,
final String account,
final File configPath,
final ServiceEnvironment serviceEnvironment
final RegistrationCommand command, final String account, final SignalAccountFiles signalAccountFiles
) throws CommandException {
final RegistrationManager manager;
try {
manager = RegistrationManager.init(account, configPath, serviceEnvironment, BaseConfig.USER_AGENT);
} catch (Throwable e) {
throw new UnexpectedErrorException("Error loading or creating state file: "
+ e.getMessage()
+ " ("
+ e.getClass().getSimpleName()
+ ")", e);
}
try (manager) {
try (final var manager = loadRegistrationManager(account, signalAccountFiles)) {
command.handleCommand(ns, manager);
} catch (IOException e) {
logger.warn("Cleanup failed", e);
@ -280,12 +261,10 @@ public class App {
private void handleLocalCommand(
final LocalCommand command,
final String account,
final File configPath,
final ServiceEnvironment serviceEnvironment,
final OutputWriter outputWriter,
final TrustNewIdentity trustNewIdentity
final SignalAccountFiles signalAccountFiles,
final OutputWriter outputWriter
) throws CommandException {
try (var m = loadManager(account, configPath, serviceEnvironment, trustNewIdentity)) {
try (var m = loadManager(account, signalAccountFiles)) {
command.handleCommand(ns, m, outputWriter);
} catch (IOException e) {
logger.warn("Cleanup failed", e);
@ -310,15 +289,10 @@ public class App {
private void handleMultiLocalCommand(
final MultiLocalCommand command,
final File configPath,
final ServiceEnvironment serviceEnvironment,
final OutputWriter outputWriter,
final TrustNewIdentity trustNewIdentity
final SignalAccountFiles signalAccountFiles,
final OutputWriter outputWriter
) throws CommandException {
try (var multiAccountManager = MultiAccountManager.init(configPath,
serviceEnvironment,
BaseConfig.USER_AGENT,
trustNewIdentity)) {
try (var multiAccountManager = signalAccountFiles.initMultiAccountManager()) {
command.handleCommand(ns, multiAccountManager, outputWriter);
}
}
@ -336,15 +310,26 @@ public class App {
}
}
private RegistrationManager loadRegistrationManager(
final String account, final SignalAccountFiles signalAccountFiles
) throws UnexpectedErrorException {
try {
return signalAccountFiles.initRegistrationManager(account);
} catch (Throwable e) {
throw new UnexpectedErrorException("Error loading or creating state file: "
+ e.getMessage()
+ " ("
+ e.getClass().getSimpleName()
+ ")", e);
}
}
private Manager loadManager(
final String account,
final File configPath,
final ServiceEnvironment serviceEnvironment,
final TrustNewIdentity trustNewIdentity
final String account, final SignalAccountFiles signalAccountFiles
) throws CommandException {
logger.trace("Loading account file for {}", account);
try {
return Manager.init(account, configPath, serviceEnvironment, BaseConfig.USER_AGENT, trustNewIdentity);
return signalAccountFiles.initManager(account);
} catch (NotRegisteredException e) {
throw new UserErrorException("User " + account + " is not registered.");
} catch (AccountCheckException ace) {

View file

@ -27,7 +27,7 @@ import org.asamk.signal.commands.exceptions.IOErrorException;
import org.asamk.signal.commands.exceptions.UnexpectedErrorException;
import org.asamk.signal.commands.exceptions.UntrustedKeyErrorException;
import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.ManagerLogger;
import org.asamk.signal.util.SecurityProvider;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.bridge.SLF4JBridgeHandler;
@ -97,7 +97,7 @@ public class Main {
if (verboseLevel > 0) {
java.util.logging.Logger.getLogger("")
.setLevel(verboseLevel > 2 ? java.util.logging.Level.FINEST : java.util.logging.Level.INFO);
Manager.initLogger();
ManagerLogger.initLogger();
}
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();