]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java
Get UUIDs for unknown numbers from server
[signal-cli] / src / main / java / org / asamk / signal / manager / helper / ProfileHelper.java
1 package org.asamk.signal.manager.helper;
2
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;
19
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;
25
26 public final class ProfileHelper {
27
28 private final ProfileKeyProvider profileKeyProvider;
29
30 private final UnidentifiedAccessProvider unidentifiedAccessProvider;
31
32 private final MessagePipeProvider messagePipeProvider;
33
34 private final MessageReceiverProvider messageReceiverProvider;
35
36 public ProfileHelper(
37 final ProfileKeyProvider profileKeyProvider,
38 final UnidentifiedAccessProvider unidentifiedAccessProvider,
39 final MessagePipeProvider messagePipeProvider,
40 final MessageReceiverProvider messageReceiverProvider
41 ) {
42 this.profileKeyProvider = profileKeyProvider;
43 this.unidentifiedAccessProvider = unidentifiedAccessProvider;
44 this.messagePipeProvider = messagePipeProvider;
45 this.messageReceiverProvider = messageReceiverProvider;
46 }
47
48 public ProfileAndCredential retrieveProfileSync(
49 SignalServiceAddress recipient, SignalServiceProfile.RequestType requestType
50 ) throws IOException {
51 try {
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();
58 } else {
59 throw new IOException(e);
60 }
61 } catch (InterruptedException | TimeoutException e) {
62 throw new PushNetworkException(e);
63 }
64 }
65
66 public ListenableFuture<ProfileAndCredential> retrieveProfile(
67 SignalServiceAddress address, SignalServiceProfile.RequestType requestType
68 ) {
69 Optional<UnidentifiedAccess> unidentifiedAccess = getUnidentifiedAccess(address);
70 Optional<ProfileKey> profileKey = Optional.fromNullable(profileKeyProvider.getProfileKey(address));
71
72 if (unidentifiedAccess.isPresent()) {
73 return new CascadingFuture<>(Arrays.asList(() -> getPipeRetrievalFuture(address,
74 profileKey,
75 unidentifiedAccess,
76 requestType),
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));
81 } else {
82 return new CascadingFuture<>(Arrays.asList(() -> getPipeRetrievalFuture(address,
83 profileKey,
84 Optional.absent(),
85 requestType), () -> getSocketRetrievalFuture(address, profileKey, Optional.absent(), requestType)),
86 e -> !(e instanceof NotFoundException));
87 }
88 }
89
90 public String decryptName(
91 ProfileKey profileKey, String encryptedName
92 ) throws InvalidCiphertextException, IOException {
93 if (encryptedName == null) {
94 return null;
95 }
96
97 ProfileCipher profileCipher = new ProfileCipher(profileKey);
98 return new String(profileCipher.decryptName(Base64.decode(encryptedName)));
99 }
100
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()
109 ? unidentifiedPipe
110 : messagePipeProvider.getMessagePipe(false);
111 if (pipe != null) {
112 return pipe.getProfile(address, profileKey, unidentifiedAccess, requestType);
113 }
114
115 throw new IOException("No pipe available!");
116 }
117
118 private ListenableFuture<ProfileAndCredential> getSocketRetrievalFuture(
119 SignalServiceAddress address,
120 Optional<ProfileKey> profileKey,
121 Optional<UnidentifiedAccess> unidentifiedAccess,
122 SignalServiceProfile.RequestType requestType
123 ) {
124 SignalServiceMessageReceiver receiver = messageReceiverProvider.getMessageReceiver();
125 return receiver.retrieveProfile(address, profileKey, unidentifiedAccess, requestType);
126 }
127
128 private Optional<UnidentifiedAccess> getUnidentifiedAccess(SignalServiceAddress recipient) {
129 Optional<UnidentifiedAccessPair> unidentifiedAccess = unidentifiedAccessProvider.getAccessFor(recipient);
130
131 if (unidentifiedAccess.isPresent()) {
132 return unidentifiedAccess.get().getTargetUnidentifiedAccess();
133 }
134
135 return Optional.absent();
136 }
137 }