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