1 package org
.asamk
.signal
.manager
.helper
;
3 import org
.asamk
.signal
.manager
.storage
.recipients
.RecipientId
;
4 import org
.signal
.zkgroup
.profiles
.ProfileKey
;
5 import org
.whispersystems
.libsignal
.util
.guava
.Optional
;
6 import org
.whispersystems
.signalservice
.api
.crypto
.UnidentifiedAccess
;
7 import org
.whispersystems
.signalservice
.api
.profiles
.ProfileAndCredential
;
8 import org
.whispersystems
.signalservice
.api
.profiles
.SignalServiceProfile
;
9 import org
.whispersystems
.signalservice
.api
.push
.SignalServiceAddress
;
10 import org
.whispersystems
.signalservice
.api
.push
.exceptions
.NotFoundException
;
11 import org
.whispersystems
.signalservice
.api
.push
.exceptions
.PushNetworkException
;
12 import org
.whispersystems
.signalservice
.internal
.util
.concurrent
.CascadingFuture
;
13 import org
.whispersystems
.signalservice
.internal
.util
.concurrent
.ListenableFuture
;
15 import java
.io
.IOException
;
16 import java
.util
.Arrays
;
17 import java
.util
.concurrent
.ExecutionException
;
18 import java
.util
.concurrent
.TimeUnit
;
19 import java
.util
.concurrent
.TimeoutException
;
21 public final class ProfileHelper
{
23 private final ProfileKeyProvider profileKeyProvider
;
25 private final UnidentifiedAccessProvider unidentifiedAccessProvider
;
27 private final MessagePipeProvider messagePipeProvider
;
29 private final MessageReceiverProvider messageReceiverProvider
;
31 private final SignalServiceAddressResolver addressResolver
;
34 final ProfileKeyProvider profileKeyProvider
,
35 final UnidentifiedAccessProvider unidentifiedAccessProvider
,
36 final MessagePipeProvider messagePipeProvider
,
37 final MessageReceiverProvider messageReceiverProvider
,
38 final SignalServiceAddressResolver addressResolver
40 this.profileKeyProvider
= profileKeyProvider
;
41 this.unidentifiedAccessProvider
= unidentifiedAccessProvider
;
42 this.messagePipeProvider
= messagePipeProvider
;
43 this.messageReceiverProvider
= messageReceiverProvider
;
44 this.addressResolver
= addressResolver
;
47 public ProfileAndCredential
retrieveProfileSync(
48 RecipientId recipientId
, SignalServiceProfile
.RequestType requestType
49 ) throws IOException
{
51 return retrieveProfile(recipientId
, requestType
).get(10, TimeUnit
.SECONDS
);
52 } catch (ExecutionException e
) {
53 if (e
.getCause() instanceof PushNetworkException
) {
54 throw (PushNetworkException
) e
.getCause();
55 } else if (e
.getCause() instanceof NotFoundException
) {
56 throw (NotFoundException
) e
.getCause();
58 throw new IOException(e
);
60 } catch (InterruptedException
| TimeoutException e
) {
61 throw new PushNetworkException(e
);
65 public ListenableFuture
<ProfileAndCredential
> retrieveProfile(
66 RecipientId recipientId
, SignalServiceProfile
.RequestType requestType
68 var unidentifiedAccess
= getUnidentifiedAccess(recipientId
);
69 var profileKey
= Optional
.fromNullable(profileKeyProvider
.getProfileKey(recipientId
));
71 final var address
= addressResolver
.resolveSignalServiceAddress(recipientId
);
72 if (unidentifiedAccess
.isPresent()) {
73 return new CascadingFuture
<>(Arrays
.asList(() -> getPipeRetrievalFuture(address
,
77 () -> getSocketRetrievalFuture(address
, profileKey
, unidentifiedAccess
, requestType
),
78 () -> getPipeRetrievalFuture(address
, profileKey
, Optional
.absent(), requestType
),
79 () -> getSocketRetrievalFuture(address
, profileKey
, Optional
.absent(), requestType
)),
80 e
-> !(e
instanceof NotFoundException
));
82 return new CascadingFuture
<>(Arrays
.asList(() -> getPipeRetrievalFuture(address
,
85 requestType
), () -> getSocketRetrievalFuture(address
, profileKey
, Optional
.absent(), requestType
)),
86 e
-> !(e
instanceof NotFoundException
));
90 private ListenableFuture
<ProfileAndCredential
> getPipeRetrievalFuture(
91 SignalServiceAddress address
,
92 Optional
<ProfileKey
> profileKey
,
93 Optional
<UnidentifiedAccess
> unidentifiedAccess
,
94 SignalServiceProfile
.RequestType requestType
95 ) throws IOException
{
96 var unidentifiedPipe
= messagePipeProvider
.getMessagePipe(true);
97 var pipe
= unidentifiedPipe
!= null && unidentifiedAccess
.isPresent()
99 : messagePipeProvider
.getMessagePipe(false);
102 return pipe
.getProfile(address
, profileKey
, unidentifiedAccess
, requestType
);
103 } catch (NoClassDefFoundError e
) {
104 // Native zkgroup lib not available for ProfileKey
105 if (!address
.getNumber().isPresent()) {
106 throw new NotFoundException("Can't request profile without number");
108 var addressWithoutUuid
= new SignalServiceAddress(Optional
.absent(), address
.getNumber());
109 return pipe
.getProfile(addressWithoutUuid
, profileKey
, unidentifiedAccess
, requestType
);
113 throw new IOException("No pipe available!");
116 private ListenableFuture
<ProfileAndCredential
> getSocketRetrievalFuture(
117 SignalServiceAddress address
,
118 Optional
<ProfileKey
> profileKey
,
119 Optional
<UnidentifiedAccess
> unidentifiedAccess
,
120 SignalServiceProfile
.RequestType requestType
121 ) throws NotFoundException
{
122 var receiver
= messageReceiverProvider
.getMessageReceiver();
124 return receiver
.retrieveProfile(address
, profileKey
, unidentifiedAccess
, requestType
);
125 } catch (NoClassDefFoundError e
) {
126 // Native zkgroup lib not available for ProfileKey
127 if (!address
.getNumber().isPresent()) {
128 throw new NotFoundException("Can't request profile without number");
130 var addressWithoutUuid
= new SignalServiceAddress(Optional
.absent(), address
.getNumber());
131 return receiver
.retrieveProfile(addressWithoutUuid
, profileKey
, unidentifiedAccess
, requestType
);
135 private Optional
<UnidentifiedAccess
> getUnidentifiedAccess(RecipientId recipientId
) {
136 var unidentifiedAccess
= unidentifiedAccessProvider
.getAccessFor(recipientId
);
138 if (unidentifiedAccess
.isPresent()) {
139 return unidentifiedAccess
.get().getTargetUnidentifiedAccess();
142 return Optional
.absent();