1 package org
.asamk
.signal
.manager
.helper
;
3 import org
.signal
.zkgroup
.profiles
.ProfileKey
;
4 import org
.whispersystems
.libsignal
.util
.guava
.Optional
;
5 import org
.whispersystems
.signalservice
.api
.SignalServiceMessagePipe
;
6 import org
.whispersystems
.signalservice
.api
.SignalServiceMessageReceiver
;
7 import org
.whispersystems
.signalservice
.api
.crypto
.InvalidCiphertextException
;
8 import org
.whispersystems
.signalservice
.api
.crypto
.ProfileCipher
;
9 import org
.whispersystems
.signalservice
.api
.crypto
.UnidentifiedAccess
;
10 import org
.whispersystems
.signalservice
.api
.crypto
.UnidentifiedAccessPair
;
11 import org
.whispersystems
.signalservice
.api
.profiles
.ProfileAndCredential
;
12 import org
.whispersystems
.signalservice
.api
.profiles
.SignalServiceProfile
;
13 import org
.whispersystems
.signalservice
.api
.push
.SignalServiceAddress
;
14 import org
.whispersystems
.signalservice
.api
.push
.exceptions
.NotFoundException
;
15 import org
.whispersystems
.signalservice
.api
.push
.exceptions
.PushNetworkException
;
16 import org
.whispersystems
.signalservice
.internal
.util
.concurrent
.CascadingFuture
;
17 import org
.whispersystems
.signalservice
.internal
.util
.concurrent
.ListenableFuture
;
18 import org
.whispersystems
.util
.Base64
;
20 import java
.io
.IOException
;
21 import java
.util
.Arrays
;
22 import java
.util
.concurrent
.ExecutionException
;
23 import java
.util
.concurrent
.TimeUnit
;
24 import java
.util
.concurrent
.TimeoutException
;
26 public final class ProfileHelper
{
28 private final ProfileKeyProvider profileKeyProvider
;
30 private final UnidentifiedAccessProvider unidentifiedAccessProvider
;
32 private final MessagePipeProvider messagePipeProvider
;
34 private final MessageReceiverProvider messageReceiverProvider
;
37 final ProfileKeyProvider profileKeyProvider
,
38 final UnidentifiedAccessProvider unidentifiedAccessProvider
,
39 final MessagePipeProvider messagePipeProvider
,
40 final MessageReceiverProvider messageReceiverProvider
42 this.profileKeyProvider
= profileKeyProvider
;
43 this.unidentifiedAccessProvider
= unidentifiedAccessProvider
;
44 this.messagePipeProvider
= messagePipeProvider
;
45 this.messageReceiverProvider
= messageReceiverProvider
;
48 public ProfileAndCredential
retrieveProfileSync(
49 SignalServiceAddress recipient
, SignalServiceProfile
.RequestType requestType
50 ) throws IOException
{
52 return retrieveProfile(recipient
, requestType
).get(10, TimeUnit
.SECONDS
);
53 } catch (ExecutionException e
) {
54 if (e
.getCause() instanceof PushNetworkException
) {
55 throw (PushNetworkException
) e
.getCause();
56 } else if (e
.getCause() instanceof NotFoundException
) {
57 throw (NotFoundException
) e
.getCause();
59 throw new IOException(e
);
61 } catch (InterruptedException
| TimeoutException e
) {
62 throw new PushNetworkException(e
);
66 public ListenableFuture
<ProfileAndCredential
> retrieveProfile(
67 SignalServiceAddress address
, SignalServiceProfile
.RequestType requestType
69 Optional
<UnidentifiedAccess
> unidentifiedAccess
= getUnidentifiedAccess(address
);
70 Optional
<ProfileKey
> profileKey
= Optional
.fromNullable(profileKeyProvider
.getProfileKey(address
));
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 public String
decryptName(
91 ProfileKey profileKey
, String encryptedName
92 ) throws InvalidCiphertextException
, IOException
{
93 if (encryptedName
== null) {
97 ProfileCipher profileCipher
= new ProfileCipher(profileKey
);
98 return new String(profileCipher
.decryptName(Base64
.decode(encryptedName
)));
101 private ListenableFuture
<ProfileAndCredential
> getPipeRetrievalFuture(
102 SignalServiceAddress address
,
103 Optional
<ProfileKey
> profileKey
,
104 Optional
<UnidentifiedAccess
> unidentifiedAccess
,
105 SignalServiceProfile
.RequestType requestType
106 ) throws IOException
{
107 SignalServiceMessagePipe unidentifiedPipe
= messagePipeProvider
.getMessagePipe(true);
108 SignalServiceMessagePipe pipe
= unidentifiedPipe
!= null && unidentifiedAccess
.isPresent()
110 : messagePipeProvider
.getMessagePipe(false);
112 return pipe
.getProfile(address
, profileKey
, unidentifiedAccess
, requestType
);
115 throw new IOException("No pipe available!");
118 private ListenableFuture
<ProfileAndCredential
> getSocketRetrievalFuture(
119 SignalServiceAddress address
,
120 Optional
<ProfileKey
> profileKey
,
121 Optional
<UnidentifiedAccess
> unidentifiedAccess
,
122 SignalServiceProfile
.RequestType requestType
124 SignalServiceMessageReceiver receiver
= messageReceiverProvider
.getMessageReceiver();
125 return receiver
.retrieveProfile(address
, profileKey
, unidentifiedAccess
, requestType
);
128 private Optional
<UnidentifiedAccess
> getUnidentifiedAccess(SignalServiceAddress recipient
) {
129 Optional
<UnidentifiedAccessPair
> unidentifiedAccess
= unidentifiedAccessProvider
.getAccessFor(recipient
);
131 if (unidentifiedAccess
.isPresent()) {
132 return unidentifiedAccess
.get().getTargetUnidentifiedAccess();
135 return Optional
.absent();