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
,
50 SignalServiceProfile
.RequestType requestType
51 ) throws IOException
{
53 return retrieveProfile(recipient
, requestType
).get(10, TimeUnit
.SECONDS
);
54 } catch (ExecutionException e
) {
55 if (e
.getCause() instanceof PushNetworkException
) {
56 throw (PushNetworkException
) e
.getCause();
57 } else if (e
.getCause() instanceof NotFoundException
) {
58 throw (NotFoundException
) e
.getCause();
60 throw new IOException(e
);
62 } catch (InterruptedException
| TimeoutException e
) {
63 throw new PushNetworkException(e
);
67 public ListenableFuture
<ProfileAndCredential
> retrieveProfile(
68 SignalServiceAddress address
,
69 SignalServiceProfile
.RequestType requestType
71 Optional
<UnidentifiedAccess
> unidentifiedAccess
= getUnidentifiedAccess(address
);
72 Optional
<ProfileKey
> profileKey
= Optional
.fromNullable(profileKeyProvider
.getProfileKey(address
));
74 if (unidentifiedAccess
.isPresent()) {
75 return new CascadingFuture
<>(Arrays
.asList(() -> getPipeRetrievalFuture(address
, profileKey
, unidentifiedAccess
, requestType
),
76 () -> getSocketRetrievalFuture(address
, profileKey
, unidentifiedAccess
, requestType
),
77 () -> getPipeRetrievalFuture(address
, profileKey
, Optional
.absent(), requestType
),
78 () -> getSocketRetrievalFuture(address
, profileKey
, Optional
.absent(), requestType
)),
79 e
-> !(e
instanceof NotFoundException
));
81 return new CascadingFuture
<>(Arrays
.asList(() -> getPipeRetrievalFuture(address
, profileKey
, Optional
.absent(), requestType
),
82 () -> getSocketRetrievalFuture(address
, profileKey
, Optional
.absent(), requestType
)),
83 e
-> !(e
instanceof NotFoundException
));
87 public String
decryptName(
88 ProfileKey profileKey
,
90 ) throws InvalidCiphertextException
, IOException
{
91 if (encryptedName
== null) {
95 ProfileCipher profileCipher
= new ProfileCipher(profileKey
);
96 return new String(profileCipher
.decryptName(Base64
.decode(encryptedName
)));
99 private ListenableFuture
<ProfileAndCredential
> getPipeRetrievalFuture(
100 SignalServiceAddress address
,
101 Optional
<ProfileKey
> profileKey
,
102 Optional
<UnidentifiedAccess
> unidentifiedAccess
,
103 SignalServiceProfile
.RequestType requestType
104 ) throws IOException
{
105 SignalServiceMessagePipe unidentifiedPipe
= messagePipeProvider
.getMessagePipe(true);
106 SignalServiceMessagePipe pipe
= unidentifiedPipe
!= null && unidentifiedAccess
.isPresent()
108 : messagePipeProvider
.getMessagePipe(false);
110 return pipe
.getProfile(address
, 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
122 SignalServiceMessageReceiver receiver
= messageReceiverProvider
.getMessageReceiver();
123 return receiver
.retrieveProfile(address
, profileKey
, unidentifiedAccess
, requestType
);
126 private Optional
<UnidentifiedAccess
> getUnidentifiedAccess(SignalServiceAddress recipient
) {
127 Optional
<UnidentifiedAccessPair
> unidentifiedAccess
= unidentifiedAccessProvider
.getAccessFor(recipient
);
129 if (unidentifiedAccess
.isPresent()) {
130 return unidentifiedAccess
.get().getTargetUnidentifiedAccess();
133 return Optional
.absent();