]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java
Fix TextStyle doc example typo (#1251)
[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.util.KeyUtils;
11 import org.signal.libsignal.protocol.util.KeyHelper;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14 import org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException;
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 ServiceEnvironment serviceEnvironment;
28 private final ServiceEnvironmentConfig serviceEnvironmentConfig;
29 private final String userAgent;
30 private final Settings settings;
31 private final AccountsStore accountsStore;
32
33 public SignalAccountFiles(
34 final File settingsPath,
35 final ServiceEnvironment serviceEnvironment,
36 final String userAgent,
37 final Settings settings
38 ) throws IOException {
39 this.pathConfig = PathConfig.createDefault(settingsPath);
40 this.serviceEnvironment = serviceEnvironment;
41 this.serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(this.serviceEnvironment, userAgent);
42 this.userAgent = userAgent;
43 this.settings = settings;
44 this.accountsStore = new AccountsStore(pathConfig.dataPath(), serviceEnvironment, accountPath -> {
45 if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) {
46 return null;
47 }
48
49 try {
50 return SignalAccount.load(pathConfig.dataPath(), accountPath, false, settings);
51 } catch (Exception e) {
52 return null;
53 }
54 });
55 }
56
57 public Set<String> getAllLocalAccountNumbers() throws IOException {
58 return accountsStore.getAllNumbers();
59 }
60
61 public MultiAccountManager initMultiAccountManager() throws IOException {
62 final var managers = accountsStore.getAllAccounts().parallelStream().map(a -> {
63 try {
64 return initManager(a.number(), a.path());
65 } catch (NotRegisteredException | IOException | AccountCheckException e) {
66 logger.warn("Ignoring {}: {} ({})", a.number(), e.getMessage(), e.getClass().getSimpleName());
67 return null;
68 } catch (Throwable e) {
69 logger.error("Failed to load {}: {} ({})", a.number(), e.getMessage(), e.getClass().getSimpleName());
70 throw e;
71 }
72 }).filter(Objects::nonNull).toList();
73
74 return new MultiAccountManagerImpl(managers, this);
75 }
76
77 public Manager initManager(String number) throws IOException, NotRegisteredException, AccountCheckException {
78 final var accountPath = accountsStore.getPathByNumber(number);
79 return this.initManager(number, accountPath);
80 }
81
82 private Manager initManager(
83 String number, String accountPath
84 ) throws IOException, NotRegisteredException, AccountCheckException {
85 if (accountPath == null) {
86 throw new NotRegisteredException();
87 }
88 if (!SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) {
89 throw new NotRegisteredException();
90 }
91
92 var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, settings);
93 if (!number.equals(account.getNumber())) {
94 account.close();
95 throw new IOException("Number in account file doesn't match expected number: " + account.getNumber());
96 }
97
98 if (!account.isRegistered()) {
99 account.close();
100 throw new NotRegisteredException();
101 }
102
103 if (account.getServiceEnvironment() != null && account.getServiceEnvironment() != serviceEnvironment) {
104 throw new IOException("Account is registered in another environment: " + account.getServiceEnvironment());
105 }
106
107 account.initDatabase();
108
109 final var manager = new ManagerImpl(account,
110 pathConfig,
111 new AccountFileUpdaterImpl(accountsStore, accountPath),
112 serviceEnvironmentConfig,
113 userAgent);
114
115 try {
116 manager.checkAccountState();
117 } catch (DeprecatedVersionException e) {
118 manager.close();
119 throw new AccountCheckException("signal-cli version is too old for the Signal-Server, please update.");
120 } catch (IOException e) {
121 manager.close();
122 throw new AccountCheckException("Error while checking account " + number + ": " + e.getMessage(), e);
123 }
124
125 if (account.getServiceEnvironment() == null) {
126 account.setServiceEnvironment(serviceEnvironment);
127 accountsStore.updateAccount(accountPath, account.getNumber(), account.getAci());
128 }
129
130 return manager;
131 }
132
133 public ProvisioningManager initProvisioningManager() {
134 return initProvisioningManager(null);
135 }
136
137 public ProvisioningManager initProvisioningManager(Consumer<Manager> newManagerListener) {
138 return new ProvisioningManagerImpl(pathConfig,
139 serviceEnvironmentConfig,
140 userAgent,
141 newManagerListener,
142 accountsStore);
143 }
144
145 public RegistrationManager initRegistrationManager(String number) throws IOException {
146 return initRegistrationManager(number, null);
147 }
148
149 public RegistrationManager initRegistrationManager(
150 String number, Consumer<Manager> newManagerListener
151 ) throws IOException {
152 final var accountPath = accountsStore.getPathByNumber(number);
153 if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) {
154 final var newAccountPath = accountPath == null ? accountsStore.addAccount(number, null) : accountPath;
155 var aciIdentityKey = KeyUtils.generateIdentityKeyPair();
156 var pniIdentityKey = KeyUtils.generateIdentityKeyPair();
157 var registrationId = KeyHelper.generateRegistrationId(false);
158 var pniRegistrationId = KeyHelper.generateRegistrationId(false);
159
160 var profileKey = KeyUtils.createProfileKey();
161 var account = SignalAccount.create(pathConfig.dataPath(),
162 newAccountPath,
163 number,
164 serviceEnvironment,
165 aciIdentityKey,
166 pniIdentityKey,
167 registrationId,
168 pniRegistrationId,
169 profileKey,
170 settings);
171
172 return new RegistrationManagerImpl(account,
173 pathConfig,
174 serviceEnvironmentConfig,
175 userAgent,
176 newManagerListener,
177 new AccountFileUpdaterImpl(accountsStore, newAccountPath));
178 }
179
180 var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, settings);
181 if (!number.equals(account.getNumber())) {
182 account.close();
183 throw new IOException("Number in account file doesn't match expected number: " + account.getNumber());
184 }
185
186 return new RegistrationManagerImpl(account,
187 pathConfig,
188 serviceEnvironmentConfig,
189 userAgent,
190 newManagerListener,
191 new AccountFileUpdaterImpl(accountsStore, accountPath));
192 }
193 }