]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java
Extract AccountHelper
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / AccountHelper.java
1 package org.asamk.signal.manager.helper;
2
3 import org.asamk.signal.manager.DeviceLinkInfo;
4 import org.asamk.signal.manager.SignalDependencies;
5 import org.asamk.signal.manager.api.InvalidDeviceLinkException;
6 import org.asamk.signal.manager.config.ServiceConfig;
7 import org.asamk.signal.manager.storage.SignalAccount;
8 import org.asamk.signal.manager.util.KeyUtils;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import org.whispersystems.libsignal.InvalidKeyException;
12 import org.whispersystems.libsignal.util.guava.Optional;
13 import org.whispersystems.signalservice.api.push.ACI;
14 import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
15 import org.whispersystems.signalservice.api.util.DeviceNameUtil;
16
17 import java.io.IOException;
18 import java.util.concurrent.TimeUnit;
19
20 public class AccountHelper {
21
22 private final static Logger logger = LoggerFactory.getLogger(AccountHelper.class);
23
24 private final Context context;
25 private final SignalAccount account;
26 private final SignalDependencies dependencies;
27
28 private Callable unregisteredListener;
29
30 public AccountHelper(final Context context) {
31 this.account = context.getAccount();
32 this.dependencies = context.getDependencies();
33 this.context = context;
34 }
35
36 public void setUnregisteredListener(final Callable unregisteredListener) {
37 this.unregisteredListener = unregisteredListener;
38 }
39
40 public void checkAccountState() throws IOException {
41 if (account.getLastReceiveTimestamp() == 0) {
42 logger.info("The Signal protocol expects that incoming messages are regularly received.");
43 } else {
44 var diffInMilliseconds = System.currentTimeMillis() - account.getLastReceiveTimestamp();
45 long days = TimeUnit.DAYS.convert(diffInMilliseconds, TimeUnit.MILLISECONDS);
46 if (days > 7) {
47 logger.warn(
48 "Messages have been last received {} days ago. The Signal protocol expects that incoming messages are regularly received.",
49 days);
50 }
51 }
52 try {
53 context.getPreKeyHelper().refreshPreKeysIfNecessary();
54 if (account.getAci() == null) {
55 account.setAci(ACI.parseOrNull(dependencies.getAccountManager().getWhoAmI().getAci()));
56 }
57 updateAccountAttributes();
58 } catch (AuthorizationFailedException e) {
59 account.setRegistered(false);
60 throw e;
61 }
62 }
63
64 public void setDeviceName(String deviceName) {
65 final var privateKey = account.getIdentityKeyPair().getPrivateKey();
66 final var encryptedDeviceName = DeviceNameUtil.encryptDeviceName(deviceName, privateKey);
67 account.setEncryptedDeviceName(encryptedDeviceName);
68 }
69
70 public void updateAccountAttributes() throws IOException {
71 dependencies.getAccountManager()
72 .setAccountAttributes(account.getEncryptedDeviceName(),
73 null,
74 account.getLocalRegistrationId(),
75 true,
76 null,
77 account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(),
78 account.getSelfUnidentifiedAccessKey(),
79 account.isUnrestrictedUnidentifiedAccess(),
80 ServiceConfig.capabilities,
81 account.isDiscoverableByPhoneNumber());
82 }
83
84 public void addDevice(DeviceLinkInfo deviceLinkInfo) throws IOException, InvalidDeviceLinkException {
85 var identityKeyPair = account.getIdentityKeyPair();
86 var verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode();
87
88 try {
89 dependencies.getAccountManager()
90 .addDevice(deviceLinkInfo.deviceIdentifier(),
91 deviceLinkInfo.deviceKey(),
92 identityKeyPair,
93 Optional.of(account.getProfileKey().serialize()),
94 verificationCode);
95 } catch (InvalidKeyException e) {
96 throw new InvalidDeviceLinkException("Invalid device link", e);
97 }
98 account.setMultiDevice(true);
99 }
100
101 public void removeLinkedDevices(long deviceId) throws IOException {
102 dependencies.getAccountManager().removeDevice(deviceId);
103 var devices = dependencies.getAccountManager().getDevices();
104 account.setMultiDevice(devices.size() > 1);
105 }
106
107 public void setRegistrationPin(String pin) throws IOException {
108 final var masterKey = account.getPinMasterKey() != null
109 ? account.getPinMasterKey()
110 : KeyUtils.createMasterKey();
111
112 context.getPinHelper().setRegistrationLockPin(pin, masterKey);
113
114 account.setRegistrationLockPin(pin, masterKey);
115 }
116
117 public void removeRegistrationPin() throws IOException {
118 // Remove KBS Pin
119 context.getPinHelper().removeRegistrationLockPin();
120
121 account.setRegistrationLockPin(null, null);
122 }
123
124 public void unregister() throws IOException {
125 // When setting an empty GCM id, the Signal-Server also sets the fetchesMessages property to false.
126 // If this is the master device, other users can't send messages to this number anymore.
127 // If this is a linked device, other users can still send messages, but this device doesn't receive them anymore.
128 dependencies.getAccountManager().setGcmId(Optional.absent());
129
130 account.setRegistered(false);
131 unregisteredListener.call();
132 }
133
134 public void deleteAccount() throws IOException {
135 try {
136 context.getPinHelper().removeRegistrationLockPin();
137 } catch (IOException e) {
138 logger.warn("Failed to remove registration lock pin");
139 }
140 account.setRegistrationLockPin(null, null);
141
142 dependencies.getAccountManager().deleteAccount();
143
144 account.setRegistered(false);
145 unregisteredListener.call();
146 }
147
148 public interface Callable {
149
150 void call();
151 }
152 }