]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java
cf6999a97bd3ada43e06db8dc8f8f4a5eb9ed534
[signal-cli] / src / main / java / org / asamk / signal / dbus / DbusSignalControlImpl.java
1 package org.asamk.signal.dbus;
2
3 import org.asamk.SignalControl;
4 import org.asamk.signal.BaseConfig;
5 import org.asamk.signal.DbusConfig;
6 import org.asamk.signal.manager.Manager;
7 import org.asamk.signal.manager.MultiAccountManager;
8 import org.asamk.signal.manager.ProvisioningManager;
9 import org.asamk.signal.manager.RegistrationManager;
10 import org.asamk.signal.manager.api.CaptchaRequiredException;
11 import org.asamk.signal.manager.api.IncorrectPinException;
12 import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException;
13 import org.asamk.signal.manager.api.PinLockedException;
14 import org.asamk.signal.manager.api.RateLimitException;
15 import org.asamk.signal.manager.api.UserAlreadyExistsException;
16 import org.asamk.signal.manager.api.VerificationMethodNotAvailableException;
17 import org.freedesktop.dbus.DBusPath;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 import java.io.IOException;
22 import java.net.URI;
23 import java.net.URISyntaxException;
24 import java.nio.channels.OverlappingFileLockException;
25 import java.util.List;
26 import java.util.concurrent.TimeoutException;
27
28 public class DbusSignalControlImpl implements org.asamk.SignalControl {
29
30 private static final Logger logger = LoggerFactory.getLogger(DbusSignalControlImpl.class);
31 private final MultiAccountManager c;
32
33 private final String objectPath;
34
35 public DbusSignalControlImpl(final MultiAccountManager c, final String objectPath) {
36 this.c = c;
37 this.objectPath = objectPath;
38 }
39
40 @Override
41 public boolean isRemote() {
42 return false;
43 }
44
45 @Override
46 public String getObjectPath() {
47 return objectPath;
48 }
49
50 @Override
51 public void register(
52 final String number,
53 final boolean voiceVerification
54 ) throws Error.Failure, Error.InvalidNumber {
55 registerWithCaptcha(number, voiceVerification, null);
56 }
57
58 @Override
59 public void registerWithCaptcha(
60 final String number,
61 final boolean voiceVerification,
62 final String captcha
63 ) throws Error.Failure, Error.InvalidNumber {
64 if (!Manager.isValidNumber(number, null)) {
65 throw new SignalControl.Error.InvalidNumber(
66 "Invalid account (phone number), make sure you include the country code.");
67 }
68 try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) {
69 registrationManager.register(voiceVerification, captcha, false);
70 } catch (RateLimitException e) {
71 String message = "Rate limit reached";
72 throw new SignalControl.Error.Failure(message);
73 } catch (CaptchaRequiredException e) {
74 String message = captcha == null ? "Captcha required for verification." : "Invalid captcha given.";
75 throw new SignalControl.Error.RequiresCaptcha(message);
76 } catch (NonNormalizedPhoneNumberException e) {
77 throw new Error.InvalidNumber(e.getMessage());
78 } catch (OverlappingFileLockException e) {
79 throw new SignalControl.Error.Failure("Account is already in use");
80 } catch (IOException | VerificationMethodNotAvailableException e) {
81 throw new SignalControl.Error.Failure(e.getClass().getSimpleName() + " " + e.getMessage());
82 }
83 }
84
85 @Override
86 public void verify(final String number, final String verificationCode) throws Error.Failure, Error.InvalidNumber {
87 verifyWithPin(number, verificationCode, null);
88 }
89
90 @Override
91 public void verifyWithPin(
92 final String number,
93 final String verificationCode,
94 final String pin
95 ) throws Error.Failure, Error.InvalidNumber {
96 try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) {
97 registrationManager.verifyAccount(verificationCode, pin);
98 } catch (OverlappingFileLockException e) {
99 throw new SignalControl.Error.Failure("Account is already in use");
100 } catch (IOException e) {
101 throw new SignalControl.Error.Failure(e.getClass().getSimpleName() + " " + e.getMessage());
102 } catch (PinLockedException e) {
103 throw new Error.Failure(
104 "Verification failed! This number is locked with a pin. Hours remaining until reset: "
105 + (e.getTimeRemaining() / 1000 / 60 / 60));
106 } catch (IncorrectPinException e) {
107 throw new Error.Failure("Verification failed! Invalid pin, tries remaining: " + e.getTriesRemaining());
108 }
109 }
110
111 @Override
112 public String link(final String newDeviceName) throws Error.Failure {
113 final URI deviceLinkUri;
114 try {
115 deviceLinkUri = c.getNewProvisioningDeviceLinkUri();
116 } catch (TimeoutException | IOException e) {
117 throw new SignalControl.Error.Failure(e.getClass().getSimpleName() + " " + e.getMessage());
118 }
119 Thread.ofPlatform().name("dbus-link").start(() -> {
120 final ProvisioningManager provisioningManager = c.getProvisioningManagerFor(deviceLinkUri);
121 try {
122 provisioningManager.finishDeviceLink(newDeviceName);
123 } catch (IOException | TimeoutException | UserAlreadyExistsException e) {
124 logger.warn("Failed to finish linking", e);
125 }
126 });
127 return deviceLinkUri.toString();
128 }
129
130 @Override
131 public String startLink() throws Error.Failure {
132 try {
133 final URI deviceLinkUri = c.getNewProvisioningDeviceLinkUri();
134 return deviceLinkUri.toString();
135 } catch (TimeoutException | IOException e) {
136 throw new SignalControl.Error.Failure(e.getClass().getSimpleName() + " " + e.getMessage());
137 }
138 }
139
140 @Override
141 public String finishLink(String deviceLinkUri, final String newDeviceName) throws Error.Failure {
142 try {
143 final var provisioningManager = c.getProvisioningManagerFor(new URI(deviceLinkUri));
144 return provisioningManager.finishDeviceLink(newDeviceName);
145 } catch (TimeoutException | IOException | UserAlreadyExistsException | URISyntaxException e) {
146 throw new SignalControl.Error.Failure(e.getClass().getSimpleName() + " " + e.getMessage());
147 }
148 }
149
150 @Override
151 public String version() {
152 return BaseConfig.PROJECT_VERSION;
153 }
154
155 @Override
156 public List<DBusPath> listAccounts() {
157 return c.getAccountNumbers().stream().map(u -> new DBusPath(DbusConfig.getObjectPath(u))).toList();
158 }
159
160 @Override
161 public DBusPath getAccount(final String number) {
162 return new DBusPath(DbusConfig.getObjectPath(number));
163 }
164 }