]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java
Extract lib module
[signal-cli] / lib / 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.UnidentifiedAccess;
8 import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
9 import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
10 import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
11 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
12 import org.whispersystems.signalservice.api.push.exceptions.NotFoundException;
13 import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
14 import org.whispersystems.signalservice.internal.util.concurrent.CascadingFuture;
15 import org.whispersystems.signalservice.internal.util.concurrent.ListenableFuture;
16
17 import java.io.IOException;
18 import java.util.Arrays;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.TimeUnit;
21 import java.util.concurrent.TimeoutException;
22
23 public final class ProfileHelper {
24
25 private final ProfileKeyProvider profileKeyProvider;
26
27 private final UnidentifiedAccessProvider unidentifiedAccessProvider;
28
29 private final MessagePipeProvider messagePipeProvider;
30
31 private final MessageReceiverProvider messageReceiverProvider;
32
33 public ProfileHelper(
34 final ProfileKeyProvider profileKeyProvider,
35 final UnidentifiedAccessProvider unidentifiedAccessProvider,
36 final MessagePipeProvider messagePipeProvider,
37 final MessageReceiverProvider messageReceiverProvider
38 ) {
39 this.profileKeyProvider = profileKeyProvider;
40 this.unidentifiedAccessProvider = unidentifiedAccessProvider;
41 this.messagePipeProvider = messagePipeProvider;
42 this.messageReceiverProvider = messageReceiverProvider;
43 }
44
45 public ProfileAndCredential retrieveProfileSync(
46 SignalServiceAddress recipient, SignalServiceProfile.RequestType requestType
47 ) throws IOException {
48 try {
49 return retrieveProfile(recipient, requestType).get(10, TimeUnit.SECONDS);
50 } catch (ExecutionException e) {
51 if (e.getCause() instanceof PushNetworkException) {
52 throw (PushNetworkException) e.getCause();
53 } else if (e.getCause() instanceof NotFoundException) {
54 throw (NotFoundException) e.getCause();
55 } else {
56 throw new IOException(e);
57 }
58 } catch (InterruptedException | TimeoutException e) {
59 throw new PushNetworkException(e);
60 }
61 }
62
63 public ListenableFuture<ProfileAndCredential> retrieveProfile(
64 SignalServiceAddress address, SignalServiceProfile.RequestType requestType
65 ) {
66 Optional<UnidentifiedAccess> unidentifiedAccess = getUnidentifiedAccess(address);
67 Optional<ProfileKey> profileKey = Optional.fromNullable(profileKeyProvider.getProfileKey(address));
68
69 if (unidentifiedAccess.isPresent()) {
70 return new CascadingFuture<>(Arrays.asList(() -> getPipeRetrievalFuture(address,
71 profileKey,
72 unidentifiedAccess,
73 requestType),
74 () -> getSocketRetrievalFuture(address, profileKey, unidentifiedAccess, requestType),
75 () -> getPipeRetrievalFuture(address, profileKey, Optional.absent(), requestType),
76 () -> getSocketRetrievalFuture(address, profileKey, Optional.absent(), requestType)),
77 e -> !(e instanceof NotFoundException));
78 } else {
79 return new CascadingFuture<>(Arrays.asList(() -> getPipeRetrievalFuture(address,
80 profileKey,
81 Optional.absent(),
82 requestType), () -> getSocketRetrievalFuture(address, profileKey, Optional.absent(), requestType)),
83 e -> !(e instanceof NotFoundException));
84 }
85 }
86
87 private ListenableFuture<ProfileAndCredential> getPipeRetrievalFuture(
88 SignalServiceAddress address,
89 Optional<ProfileKey> profileKey,
90 Optional<UnidentifiedAccess> unidentifiedAccess,
91 SignalServiceProfile.RequestType requestType
92 ) throws IOException {
93 SignalServiceMessagePipe unidentifiedPipe = messagePipeProvider.getMessagePipe(true);
94 SignalServiceMessagePipe pipe = unidentifiedPipe != null && unidentifiedAccess.isPresent()
95 ? unidentifiedPipe
96 : messagePipeProvider.getMessagePipe(false);
97 if (pipe != null) {
98 try {
99 return pipe.getProfile(address, profileKey, unidentifiedAccess, requestType);
100 } catch (NoClassDefFoundError e) {
101 // Native zkgroup lib not available for ProfileKey
102 if (!address.getNumber().isPresent()) {
103 throw new NotFoundException("Can't request profile without number");
104 }
105 SignalServiceAddress addressWithoutUuid = new SignalServiceAddress(Optional.absent(),
106 address.getNumber());
107 return pipe.getProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType);
108 }
109 }
110
111 throw new IOException("No pipe available!");
112 }
113
114 private ListenableFuture<ProfileAndCredential> getSocketRetrievalFuture(
115 SignalServiceAddress address,
116 Optional<ProfileKey> profileKey,
117 Optional<UnidentifiedAccess> unidentifiedAccess,
118 SignalServiceProfile.RequestType requestType
119 ) throws NotFoundException {
120 SignalServiceMessageReceiver receiver = messageReceiverProvider.getMessageReceiver();
121 try {
122 return receiver.retrieveProfile(address, profileKey, unidentifiedAccess, requestType);
123 } catch (NoClassDefFoundError e) {
124 // Native zkgroup lib not available for ProfileKey
125 if (!address.getNumber().isPresent()) {
126 throw new NotFoundException("Can't request profile without number");
127 }
128 SignalServiceAddress addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber());
129 return receiver.retrieveProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType);
130 }
131 }
132
133 private Optional<UnidentifiedAccess> getUnidentifiedAccess(SignalServiceAddress recipient) {
134 Optional<UnidentifiedAccessPair> unidentifiedAccess = unidentifiedAccessProvider.getAccessFor(recipient);
135
136 if (unidentifiedAccess.isPresent()) {
137 return unidentifiedAccess.get().getTargetUnidentifiedAccess();
138 }
139
140 return Optional.absent();
141 }
142 }