]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java
Remove libsignal-service from manager lib API
[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.commands.SignalCreator;
7 import org.asamk.signal.manager.Manager;
8 import org.asamk.signal.manager.ProvisioningManager;
9 import org.asamk.signal.manager.RegistrationManager;
10 import org.asamk.signal.manager.UserAlreadyExists;
11 import org.asamk.signal.manager.api.CaptchaRequiredException;
12 import org.asamk.signal.manager.api.IncorrectPinException;
13 import org.asamk.signal.manager.api.Pair;
14 import org.asamk.signal.manager.api.PinLockedException;
15 import org.freedesktop.dbus.DBusPath;
16
17 import java.io.IOException;
18 import java.net.URI;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.concurrent.TimeoutException;
22 import java.util.function.Function;
23 import java.util.stream.Collectors;
24
25 public class DbusSignalControlImpl implements org.asamk.SignalControl {
26
27 private final SignalCreator c;
28 private final Function<Manager, Thread> newManagerRunner;
29
30 private final List<Pair<Manager, Thread>> receiveThreads = new ArrayList<>();
31 private final Object stopTrigger = new Object();
32 private final String objectPath;
33
34 public DbusSignalControlImpl(
35 final SignalCreator c, final Function<Manager, Thread> newManagerRunner, final String objectPath
36 ) {
37 this.c = c;
38 this.newManagerRunner = newManagerRunner;
39 this.objectPath = objectPath;
40 }
41
42 public void addManager(Manager m) {
43 var thread = newManagerRunner.apply(m);
44 if (thread == null) {
45 return;
46 }
47 synchronized (receiveThreads) {
48 receiveThreads.add(new Pair<>(m, thread));
49 }
50 }
51
52 public void run() {
53 synchronized (stopTrigger) {
54 try {
55 stopTrigger.wait();
56 } catch (InterruptedException ignored) {
57 }
58 }
59
60 synchronized (receiveThreads) {
61 for (var t : receiveThreads) {
62 t.second().interrupt();
63 }
64 }
65 while (true) {
66 final Thread thread;
67 synchronized (receiveThreads) {
68 if (receiveThreads.size() == 0) {
69 break;
70 }
71 var pair = receiveThreads.remove(0);
72 thread = pair.second();
73 }
74 try {
75 thread.join();
76 } catch (InterruptedException ignored) {
77 }
78 }
79 }
80
81 @Override
82 public boolean isRemote() {
83 return false;
84 }
85
86 @Override
87 public String getObjectPath() {
88 return objectPath;
89 }
90
91 @Override
92 public void register(
93 final String number, final boolean voiceVerification
94 ) throws Error.Failure, Error.InvalidNumber {
95 registerWithCaptcha(number, voiceVerification, null);
96 }
97
98 @Override
99 public void registerWithCaptcha(
100 final String number, final boolean voiceVerification, final String captcha
101 ) throws Error.Failure, Error.InvalidNumber {
102 if (!Manager.isValidNumber(number, null)) {
103 throw new SignalControl.Error.InvalidNumber(
104 "Invalid username (phone number), make sure you include the country code.");
105 }
106 try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) {
107 registrationManager.register(voiceVerification, captcha);
108 } catch (CaptchaRequiredException e) {
109 String message = captcha == null ? "Captcha required for verification." : "Invalid captcha given.";
110 throw new SignalControl.Error.RequiresCaptcha(message);
111 } catch (IOException e) {
112 throw new SignalControl.Error.Failure(e.getClass().getSimpleName() + " " + e.getMessage());
113 }
114 }
115
116 @Override
117 public void verify(final String number, final String verificationCode) throws Error.Failure, Error.InvalidNumber {
118 verifyWithPin(number, verificationCode, null);
119 }
120
121 @Override
122 public void verifyWithPin(
123 final String number, final String verificationCode, final String pin
124 ) throws Error.Failure, Error.InvalidNumber {
125 try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) {
126 final Manager manager = registrationManager.verifyAccount(verificationCode, pin);
127 addManager(manager);
128 } catch (IOException | PinLockedException | IncorrectPinException e) {
129 throw new SignalControl.Error.Failure(e.getClass().getSimpleName() + " " + e.getMessage());
130 }
131 }
132
133 @Override
134 public String link(final String newDeviceName) throws Error.Failure {
135 try {
136 final ProvisioningManager provisioningManager = c.getNewProvisioningManager();
137 final URI deviceLinkUri = provisioningManager.getDeviceLinkUri();
138 new Thread(() -> {
139 try {
140 final Manager manager = provisioningManager.finishDeviceLink(newDeviceName);
141 addManager(manager);
142 } catch (IOException | TimeoutException | UserAlreadyExists e) {
143 e.printStackTrace();
144 }
145 }).start();
146 return deviceLinkUri.toString();
147 } catch (TimeoutException | IOException e) {
148 throw new SignalControl.Error.Failure(e.getClass().getSimpleName() + " " + e.getMessage());
149 }
150 }
151
152 @Override
153 public String version() {
154 return BaseConfig.PROJECT_VERSION;
155 }
156
157 @Override
158 public List<DBusPath> listAccounts() {
159 synchronized (receiveThreads) {
160 return receiveThreads.stream()
161 .map(Pair::first)
162 .map(Manager::getSelfNumber)
163 .map(u -> new DBusPath(DbusConfig.getObjectPath(u)))
164 .collect(Collectors.toList());
165 }
166 }
167 }