]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java
Improve stop receive handling
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / SignalAccountFiles.java
1 package org.asamk.signal.manager;
2
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;
15
16 import java.io.File;
17 import java.io.IOException;
18 import java.util.Objects;
19 import java.util.Set;
20 import java.util.function.Consumer;
21
22 public class SignalAccountFiles {
23
24 private static final Logger logger = LoggerFactory.getLogger(MultiAccountManager.class);
25
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;
31
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());
43 }
44
45 public Set<String> getAllLocalAccountNumbers() {
46 return accountsStore.getAllNumbers();
47 }
48
49 public MultiAccountManager initMultiAccountManager() {
50 final var managers = getAllLocalAccountNumbers().stream().map(a -> {
51 try {
52 return initManager(a);
53 } catch (NotRegisteredException | IOException | AccountCheckException e) {
54 logger.warn("Ignoring {}: {} ({})", a, e.getMessage(), e.getClass().getSimpleName());
55 return null;
56 }
57 }).filter(Objects::nonNull).toList();
58
59 return new MultiAccountManagerImpl(managers, this);
60 }
61
62 public Manager initManager(String number) throws IOException, NotRegisteredException, AccountCheckException {
63 final var accountPath = accountsStore.getPathByNumber(number);
64 if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) {
65 throw new NotRegisteredException();
66 }
67
68 var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity);
69 if (!number.equals(account.getNumber())) {
70 account.close();
71 throw new IOException("Number in account file doesn't match expected number: " + account.getNumber());
72 }
73
74 if (!account.isRegistered()) {
75 account.close();
76 throw new NotRegisteredException();
77 }
78
79 account.initDatabase();
80
81 final var manager = new ManagerImpl(account,
82 pathConfig,
83 (newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci),
84 serviceEnvironmentConfig,
85 userAgent);
86
87 try {
88 manager.checkAccountState();
89 } catch (IOException e) {
90 manager.close();
91 throw new AccountCheckException("Error while checking account " + number + ": " + e.getMessage(), e);
92 }
93
94 return manager;
95 }
96
97 public ProvisioningManager initProvisioningManager() {
98 return initProvisioningManager(null);
99 }
100
101 public ProvisioningManager initProvisioningManager(Consumer<Manager> newManagerListener) {
102 return new ProvisioningManagerImpl(pathConfig,
103 serviceEnvironmentConfig,
104 userAgent,
105 newManagerListener,
106 accountsStore);
107 }
108
109 public RegistrationManager initRegistrationManager(String number) throws IOException {
110 return initRegistrationManager(number, null);
111 }
112
113 public RegistrationManager initRegistrationManager(
114 String number, Consumer<Manager> newManagerListener
115 ) throws IOException {
116 final var accountPath = accountsStore.getPathByNumber(number);
117 if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) {
118 final var newAccountPath = accountPath == null ? accountsStore.addAccount(number, null) : accountPath;
119 var identityKey = KeyUtils.generateIdentityKeyPair();
120 var registrationId = KeyHelper.generateRegistrationId(false);
121
122 var profileKey = KeyUtils.createProfileKey();
123 var account = SignalAccount.create(pathConfig.dataPath(),
124 newAccountPath,
125 number,
126 identityKey,
127 registrationId,
128 profileKey,
129 trustNewIdentity);
130
131 return new RegistrationManagerImpl(account,
132 pathConfig,
133 serviceEnvironmentConfig,
134 userAgent,
135 newManagerListener,
136 (newNumber, newAci) -> accountsStore.updateAccount(newAccountPath, newNumber, newAci));
137 }
138
139 var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity);
140 if (!number.equals(account.getNumber())) {
141 account.close();
142 throw new IOException("Number in account file doesn't match expected number: " + account.getNumber());
143 }
144
145 return new RegistrationManagerImpl(account,
146 pathConfig,
147 serviceEnvironmentConfig,
148 userAgent,
149 newManagerListener,
150 (newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci));
151 }
152 }