1 package org
.asamk
.signal
.manager
;
3 import org
.asamk
.signal
.manager
.api
.AccountCheckException
;
4 import org
.asamk
.signal
.manager
.api
.NotRegisteredException
;
5 import org
.asamk
.signal
.manager
.config
.ServiceConfig
;
6 import org
.asamk
.signal
.manager
.config
.ServiceEnvironment
;
7 import org
.asamk
.signal
.manager
.config
.ServiceEnvironmentConfig
;
8 import org
.asamk
.signal
.manager
.storage
.SignalAccount
;
9 import org
.asamk
.signal
.manager
.storage
.accounts
.AccountsStore
;
10 import org
.asamk
.signal
.manager
.storage
.identities
.TrustNewIdentity
;
11 import org
.asamk
.signal
.manager
.util
.KeyUtils
;
12 import org
.slf4j
.Logger
;
13 import org
.slf4j
.LoggerFactory
;
14 import org
.whispersystems
.libsignal
.util
.KeyHelper
;
17 import java
.io
.IOException
;
18 import java
.util
.Objects
;
20 import java
.util
.function
.Consumer
;
22 public class SignalAccountFiles
{
24 private static final Logger logger
= LoggerFactory
.getLogger(MultiAccountManager
.class);
26 private final PathConfig pathConfig
;
27 private final ServiceEnvironmentConfig serviceEnvironmentConfig
;
28 private final String userAgent
;
29 private final TrustNewIdentity trustNewIdentity
;
30 private final AccountsStore accountsStore
;
32 public SignalAccountFiles(
33 final File settingsPath
,
34 final ServiceEnvironment serviceEnvironment
,
35 final String userAgent
,
36 final TrustNewIdentity trustNewIdentity
37 ) throws IOException
{
38 this.pathConfig
= PathConfig
.createDefault(settingsPath
);
39 this.serviceEnvironmentConfig
= ServiceConfig
.getServiceEnvironmentConfig(serviceEnvironment
, userAgent
);
40 this.userAgent
= userAgent
;
41 this.trustNewIdentity
= trustNewIdentity
;
42 this.accountsStore
= new AccountsStore(pathConfig
.dataPath());
45 public Set
<String
> getAllLocalAccountNumbers() {
46 return accountsStore
.getAllNumbers();
49 public MultiAccountManager
initMultiAccountManager() {
50 final var managers
= accountsStore
.getAllAccounts().parallelStream().map(a
-> {
52 return initManager(a
.number(), a
.path());
53 } catch (NotRegisteredException
| IOException
| AccountCheckException e
) {
54 logger
.warn("Ignoring {}: {} ({})", a
.number(), e
.getMessage(), e
.getClass().getSimpleName());
57 }).filter(Objects
::nonNull
).toList();
59 return new MultiAccountManagerImpl(managers
, this);
62 public Manager
initManager(String number
) throws IOException
, NotRegisteredException
, AccountCheckException
{
63 final var accountPath
= accountsStore
.getPathByNumber(number
);
64 return this.initManager(number
, accountPath
);
67 private Manager
initManager(
68 String number
, String accountPath
69 ) throws IOException
, NotRegisteredException
, AccountCheckException
{
70 if (accountPath
== null) {
71 throw new NotRegisteredException();
73 if (!SignalAccount
.accountFileExists(pathConfig
.dataPath(), accountPath
)) {
74 throw new NotRegisteredException();
77 var account
= SignalAccount
.load(pathConfig
.dataPath(), accountPath
, true, trustNewIdentity
);
78 if (!number
.equals(account
.getNumber())) {
80 throw new IOException("Number in account file doesn't match expected number: " + account
.getNumber());
83 if (!account
.isRegistered()) {
85 throw new NotRegisteredException();
88 account
.initDatabase();
90 final var manager
= new ManagerImpl(account
,
92 (newNumber
, newAci
) -> accountsStore
.updateAccount(accountPath
, newNumber
, newAci
),
93 serviceEnvironmentConfig
,
97 manager
.checkAccountState();
98 } catch (IOException e
) {
100 throw new AccountCheckException("Error while checking account " + number
+ ": " + e
.getMessage(), e
);
106 public ProvisioningManager
initProvisioningManager() {
107 return initProvisioningManager(null);
110 public ProvisioningManager
initProvisioningManager(Consumer
<Manager
> newManagerListener
) {
111 return new ProvisioningManagerImpl(pathConfig
,
112 serviceEnvironmentConfig
,
118 public RegistrationManager
initRegistrationManager(String number
) throws IOException
{
119 return initRegistrationManager(number
, null);
122 public RegistrationManager
initRegistrationManager(
123 String number
, Consumer
<Manager
> newManagerListener
124 ) throws IOException
{
125 final var accountPath
= accountsStore
.getPathByNumber(number
);
126 if (accountPath
== null || !SignalAccount
.accountFileExists(pathConfig
.dataPath(), accountPath
)) {
127 final var newAccountPath
= accountPath
== null ? accountsStore
.addAccount(number
, null) : accountPath
;
128 var aciIdentityKey
= KeyUtils
.generateIdentityKeyPair();
129 var pniIdentityKey
= KeyUtils
.generateIdentityKeyPair();
130 var registrationId
= KeyHelper
.generateRegistrationId(false);
132 var profileKey
= KeyUtils
.createProfileKey();
133 var account
= SignalAccount
.create(pathConfig
.dataPath(),
142 return new RegistrationManagerImpl(account
,
144 serviceEnvironmentConfig
,
147 (newNumber
, newAci
) -> accountsStore
.updateAccount(newAccountPath
, newNumber
, newAci
));
150 var account
= SignalAccount
.load(pathConfig
.dataPath(), accountPath
, true, trustNewIdentity
);
151 if (!number
.equals(account
.getNumber())) {
153 throw new IOException("Number in account file doesn't match expected number: " + account
.getNumber());
156 return new RegistrationManagerImpl(account
,
158 serviceEnvironmentConfig
,
161 (newNumber
, newAci
) -> accountsStore
.updateAccount(accountPath
, newNumber
, newAci
));