X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/1db7f8d76eaefc7f3b2ff3d4de1883ee8b302d9e..ff6b733cd0448c05f4be5aad32895cc8c748ee79:/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java new file mode 100644 index 00000000..f1d37e81 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -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 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 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 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)); + } +}