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}