]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java
f775cc1cba8e2dda1945108917b0e3aae41289fa
[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,
50 SignalServiceProfile.RequestType requestType
51 ) throws IOException {
52 try {
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();
59 } else {
60 throw new IOException(e);
61 }
62 } catch (InterruptedException | TimeoutException e) {
63 throw new PushNetworkException(e);
64 }
65 }
66
67 public ListenableFuture<ProfileAndCredential> retrieveProfile(
68 SignalServiceAddress address,
69 SignalServiceProfile.RequestType requestType
70 ) {
71 Optional<UnidentifiedAccess> unidentifiedAccess = getUnidentifiedAccess(address);
72 Optional<ProfileKey> profileKey = Optional.fromNullable(profileKeyProvider.getProfileKey(address));
73
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));
80 } else {
81 return new CascadingFuture<>(Arrays.asList(() -> getPipeRetrievalFuture(address, profileKey, Optional.absent(), requestType),
82 () -> getSocketRetrievalFuture(address, profileKey, Optional.absent(), requestType)),
83 e -> !(e instanceof NotFoundException));
84 }
85 }
86
87 public String decryptName(
88 ProfileKey profileKey,
89 String encryptedName
90 ) throws InvalidCiphertextException, IOException {
91 if (encryptedName == null) {
92 return null;
93 }
94
95 ProfileCipher profileCipher = new ProfileCipher(profileKey);
96 return new String(profileCipher.decryptName(Base64.decode(encryptedName)));
97 }
98
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()
107 ? unidentifiedPipe
108 : messagePipeProvider.getMessagePipe(false);
109 if (pipe != null) {
110 return pipe.getProfile(address, profileKey, unidentifiedAccess, requestType);
111 }
112
113 throw new IOException("No pipe available!");
114 }
115
116 private ListenableFuture<ProfileAndCredential> getSocketRetrievalFuture(
117 SignalServiceAddress address,
118 Optional<ProfileKey> profileKey,
119 Optional<UnidentifiedAccess> unidentifiedAccess,
120 SignalServiceProfile.RequestType requestType
121 ) {
122 SignalServiceMessageReceiver receiver = messageReceiverProvider.getMessageReceiver();
123 return receiver.retrieveProfile(address, profileKey, unidentifiedAccess, requestType);
124 }
125
126 private Optional<UnidentifiedAccess> getUnidentifiedAccess(SignalServiceAddress recipient) {
127 Optional<UnidentifiedAccessPair> unidentifiedAccess = unidentifiedAccessProvider.getAccessFor(recipient);
128
129 if (unidentifiedAccess.isPresent()) {
130 return unidentifiedAccess.get().getTargetUnidentifiedAccess();
131 }
132
133 return Optional.absent();
134 }
135 }