1package eu.siacs.conversations.xmpp.manager;
2
3import android.content.Context;
4import com.google.common.util.concurrent.Futures;
5import com.google.common.util.concurrent.ListenableFuture;
6import com.google.common.util.concurrent.MoreExecutors;
7import eu.siacs.conversations.Config;
8import eu.siacs.conversations.services.XmppConnectionService;
9import eu.siacs.conversations.xml.Namespace;
10import eu.siacs.conversations.xmpp.Jid;
11import eu.siacs.conversations.xmpp.XmppConnection;
12import eu.siacs.conversations.xmpp.jingle.transports.SocksByteStreamsTransport;
13import im.conversations.android.xmpp.model.socks5.Query;
14import im.conversations.android.xmpp.model.stanza.Iq;
15import java.util.UUID;
16
17public class StreamHostManager extends AbstractManager {
18
19 public StreamHostManager(final XmppConnectionService context, final XmppConnection connection) {
20 super(context, connection);
21 }
22
23 public ListenableFuture<SocksByteStreamsTransport.Candidate> getProxyCandidate(
24 final boolean asInitiator) {
25 if (Config.DISABLE_PROXY_LOOKUP) {
26 return Futures.immediateFailedFuture(
27 new IllegalStateException("Proxy look up is disabled"));
28 }
29 final var streamer =
30 getManager(DiscoManager.class).findDiscoItemByFeature(Namespace.BYTE_STREAMS);
31 if (streamer == null) {
32 return Futures.immediateFailedFuture(
33 new IllegalStateException("No proxy/streamer found"));
34 }
35 return getProxyCandidate(asInitiator, streamer.getKey());
36 }
37
38 private ListenableFuture<SocksByteStreamsTransport.Candidate> getProxyCandidate(
39 final boolean asInitiator, final Jid streamer) {
40 final var iq = new Iq(Iq.Type.GET, new Query());
41 iq.setTo(streamer);
42 return Futures.transform(
43 connection.sendIqPacket(iq),
44 response -> {
45 final var query = response.getExtension(Query.class);
46 if (query == null) {
47 throw new IllegalStateException("No stream host query found in response");
48 }
49 final var streamHost = query.getStreamHost();
50 if (streamHost == null) {
51 throw new IllegalStateException("no stream host found in query");
52 }
53 final var jid = streamHost.getJid();
54 final var host = streamHost.getHost();
55 final var port = streamHost.getPort();
56 if (jid == null || host == null || port == null) {
57 throw new IllegalStateException("StreamHost had incomplete information");
58 }
59 return new SocksByteStreamsTransport.Candidate(
60 UUID.randomUUID().toString(),
61 host,
62 streamer,
63 port,
64 655360 + (asInitiator ? 0 : 15),
65 SocksByteStreamsTransport.CandidateType.PROXY);
66 },
67 MoreExecutors.directExecutor());
68 }
69}