package org.asamk.signal.manager.helper; import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceMessagePipe; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.internal.util.concurrent.CascadingFuture; import org.whispersystems.signalservice.internal.util.concurrent.ListenableFuture; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public final class ProfileHelper { private final ProfileKeyProvider profileKeyProvider; private final UnidentifiedAccessProvider unidentifiedAccessProvider; private final MessagePipeProvider messagePipeProvider; private final MessageReceiverProvider messageReceiverProvider; public ProfileHelper( final ProfileKeyProvider profileKeyProvider, final UnidentifiedAccessProvider unidentifiedAccessProvider, final MessagePipeProvider messagePipeProvider, final MessageReceiverProvider messageReceiverProvider ) { this.profileKeyProvider = profileKeyProvider; this.unidentifiedAccessProvider = unidentifiedAccessProvider; this.messagePipeProvider = messagePipeProvider; this.messageReceiverProvider = messageReceiverProvider; } public ProfileAndCredential retrieveProfileSync( SignalServiceAddress recipient, SignalServiceProfile.RequestType requestType ) throws IOException { try { return retrieveProfile(recipient, requestType).get(10, TimeUnit.SECONDS); } catch (ExecutionException e) { if (e.getCause() instanceof PushNetworkException) { throw (PushNetworkException) e.getCause(); } else if (e.getCause() instanceof NotFoundException) { throw (NotFoundException) e.getCause(); } else { throw new IOException(e); } } catch (InterruptedException | TimeoutException e) { throw new PushNetworkException(e); } } public ListenableFuture retrieveProfile( SignalServiceAddress address, SignalServiceProfile.RequestType requestType ) { Optional unidentifiedAccess = getUnidentifiedAccess(address); Optional profileKey = Optional.fromNullable(profileKeyProvider.getProfileKey(address)); if (unidentifiedAccess.isPresent()) { return new CascadingFuture<>(Arrays.asList(() -> getPipeRetrievalFuture(address, profileKey, unidentifiedAccess, requestType), () -> getSocketRetrievalFuture(address, profileKey, unidentifiedAccess, requestType), () -> getPipeRetrievalFuture(address, profileKey, Optional.absent(), requestType), () -> getSocketRetrievalFuture(address, profileKey, Optional.absent(), requestType)), e -> !(e instanceof NotFoundException)); } else { return new CascadingFuture<>(Arrays.asList(() -> getPipeRetrievalFuture(address, profileKey, Optional.absent(), requestType), () -> getSocketRetrievalFuture(address, profileKey, Optional.absent(), requestType)), e -> !(e instanceof NotFoundException)); } } private ListenableFuture getPipeRetrievalFuture( SignalServiceAddress address, Optional profileKey, Optional unidentifiedAccess, SignalServiceProfile.RequestType requestType ) throws IOException { SignalServiceMessagePipe unidentifiedPipe = messagePipeProvider.getMessagePipe(true); SignalServiceMessagePipe pipe = unidentifiedPipe != null && unidentifiedAccess.isPresent() ? unidentifiedPipe : messagePipeProvider.getMessagePipe(false); if (pipe != null) { try { return pipe.getProfile(address, profileKey, unidentifiedAccess, requestType); } catch (NoClassDefFoundError e) { // Native zkgroup lib not available for ProfileKey if (!address.getNumber().isPresent()) { throw new NotFoundException("Can't request profile without number"); } SignalServiceAddress addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber()); return pipe.getProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType); } } throw new IOException("No pipe available!"); } private ListenableFuture getSocketRetrievalFuture( SignalServiceAddress address, Optional profileKey, Optional unidentifiedAccess, SignalServiceProfile.RequestType requestType ) throws NotFoundException { SignalServiceMessageReceiver receiver = messageReceiverProvider.getMessageReceiver(); try { return receiver.retrieveProfile(address, profileKey, unidentifiedAccess, requestType); } catch (NoClassDefFoundError e) { // Native zkgroup lib not available for ProfileKey if (!address.getNumber().isPresent()) { throw new NotFoundException("Can't request profile without number"); } SignalServiceAddress addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber()); return receiver.retrieveProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType); } } private Optional getUnidentifiedAccess(SignalServiceAddress recipient) { Optional unidentifiedAccess = unidentifiedAccessProvider.getAccessFor(recipient); if (unidentifiedAccess.isPresent()) { return unidentifiedAccess.get().getTargetUnidentifiedAccess(); } return Optional.absent(); } }