1 package org
.asamk
.signal
.dbus
;
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
.PinLockMissingException
;
14 import org
.asamk
.signal
.manager
.api
.PinLockedException
;
15 import org
.asamk
.signal
.manager
.api
.RateLimitException
;
16 import org
.asamk
.signal
.manager
.api
.UserAlreadyExistsException
;
17 import org
.asamk
.signal
.manager
.api
.VerificationMethodNotAvailableException
;
18 import org
.freedesktop
.dbus
.DBusPath
;
19 import org
.slf4j
.Logger
;
20 import org
.slf4j
.LoggerFactory
;
22 import java
.io
.IOException
;
24 import java
.net
.URISyntaxException
;
25 import java
.nio
.channels
.OverlappingFileLockException
;
26 import java
.util
.List
;
27 import java
.util
.concurrent
.TimeoutException
;
29 public class DbusSignalControlImpl
implements org
.asamk
.SignalControl
{
31 private static final Logger logger
= LoggerFactory
.getLogger(DbusSignalControlImpl
.class);
32 private final MultiAccountManager c
;
34 private final String objectPath
;
36 public DbusSignalControlImpl(final MultiAccountManager c
, final String objectPath
) {
38 this.objectPath
= objectPath
;
42 public boolean isRemote() {
47 public String
getObjectPath() {
54 final boolean voiceVerification
55 ) throws Error
.Failure
, Error
.InvalidNumber
{
56 registerWithCaptcha(number
, voiceVerification
, null);
60 public void registerWithCaptcha(
62 final boolean voiceVerification
,
64 ) throws Error
.Failure
, Error
.InvalidNumber
{
65 if (!Manager
.isValidNumber(number
, null)) {
66 throw new SignalControl
.Error
.InvalidNumber(
67 "Invalid account (phone number), make sure you include the country code.");
69 try (final RegistrationManager registrationManager
= c
.getNewRegistrationManager(number
)) {
70 registrationManager
.register(voiceVerification
, captcha
, false);
71 } catch (RateLimitException e
) {
72 String message
= "Rate limit reached";
73 throw new SignalControl
.Error
.Failure(message
);
74 } catch (CaptchaRequiredException e
) {
75 String message
= captcha
== null ?
"Captcha required for verification." : "Invalid captcha given.";
76 throw new SignalControl
.Error
.RequiresCaptcha(message
);
77 } catch (NonNormalizedPhoneNumberException e
) {
78 throw new Error
.InvalidNumber(e
.getMessage());
79 } catch (OverlappingFileLockException e
) {
80 throw new SignalControl
.Error
.Failure("Account is already in use");
81 } catch (IOException
| VerificationMethodNotAvailableException e
) {
82 throw new SignalControl
.Error
.Failure(e
.getClass().getSimpleName() + " " + e
.getMessage());
87 public void verify(final String number
, final String verificationCode
) throws Error
.Failure
, Error
.InvalidNumber
{
88 verifyWithPin(number
, verificationCode
, null);
92 public void verifyWithPin(
94 final String verificationCode
,
96 ) throws Error
.Failure
, Error
.InvalidNumber
{
97 try (final RegistrationManager registrationManager
= c
.getNewRegistrationManager(number
)) {
98 registrationManager
.verifyAccount(verificationCode
, pin
);
99 } catch (OverlappingFileLockException e
) {
100 throw new SignalControl
.Error
.Failure("Account is already in use");
101 } catch (IOException e
) {
102 throw new SignalControl
.Error
.Failure(e
.getClass().getSimpleName() + " " + e
.getMessage());
103 } catch (PinLockedException e
) {
104 throw new Error
.Failure(
105 "Verification failed! This number is locked with a pin. Hours remaining until reset: "
106 + (e
.getTimeRemaining() / 1000 / 60 / 60));
107 } catch (IncorrectPinException e
) {
108 throw new Error
.Failure("Verification failed! Invalid pin, tries remaining: " + e
.getTriesRemaining());
109 } catch (PinLockMissingException e
) {
110 throw new Error
.Failure("Account is pin locked, but pin data has been deleted on the server.");
115 public String
link(final String newDeviceName
) throws Error
.Failure
{
116 final URI deviceLinkUri
;
118 deviceLinkUri
= c
.getNewProvisioningDeviceLinkUri();
119 } catch (TimeoutException
| IOException e
) {
120 throw new SignalControl
.Error
.Failure(e
.getClass().getSimpleName() + " " + e
.getMessage());
122 Thread
.ofPlatform().name("dbus-link").start(() -> {
123 final ProvisioningManager provisioningManager
= c
.getProvisioningManagerFor(deviceLinkUri
);
125 provisioningManager
.finishDeviceLink(newDeviceName
);
126 } catch (IOException
| TimeoutException
| UserAlreadyExistsException e
) {
127 logger
.warn("Failed to finish linking", e
);
130 return deviceLinkUri
.toString();
134 public String
startLink() throws Error
.Failure
{
136 final URI deviceLinkUri
= c
.getNewProvisioningDeviceLinkUri();
137 return deviceLinkUri
.toString();
138 } catch (TimeoutException
| IOException e
) {
139 throw new SignalControl
.Error
.Failure(e
.getClass().getSimpleName() + " " + e
.getMessage());
144 public String
finishLink(String deviceLinkUri
, final String newDeviceName
) throws Error
.Failure
{
146 final var provisioningManager
= c
.getProvisioningManagerFor(new URI(deviceLinkUri
));
147 return provisioningManager
.finishDeviceLink(newDeviceName
);
148 } catch (TimeoutException
| IOException
| UserAlreadyExistsException
| URISyntaxException e
) {
149 throw new SignalControl
.Error
.Failure(e
.getClass().getSimpleName() + " " + e
.getMessage());
154 public String
version() {
155 return BaseConfig
.PROJECT_VERSION
;
159 public List
<DBusPath
> listAccounts() {
160 return c
.getAccountNumbers().stream().map(u
-> new DBusPath(DbusConfig
.getObjectPath(u
))).toList();
164 public DBusPath
getAccount(final String number
) {
165 return new DBusPath(DbusConfig
.getObjectPath(number
));