JingleConnectionManager.java

  1package eu.siacs.conversations.xmpp.jingle;
  2
  3import android.annotation.SuppressLint;
  4import android.util.Log;
  5
  6import java.math.BigInteger;
  7import java.security.SecureRandom;
  8import java.util.HashMap;
  9import java.util.List;
 10import java.util.concurrent.CopyOnWriteArrayList;
 11
 12import eu.siacs.conversations.Config;
 13import eu.siacs.conversations.entities.Account;
 14import eu.siacs.conversations.entities.Message;
 15import eu.siacs.conversations.entities.Transferable;
 16import eu.siacs.conversations.services.AbstractConnectionManager;
 17import eu.siacs.conversations.services.XmppConnectionService;
 18import eu.siacs.conversations.xml.Namespace;
 19import eu.siacs.conversations.xml.Element;
 20import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 21import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
 22import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 23import rocks.xmpp.addr.Jid;
 24
 25public class JingleConnectionManager extends AbstractConnectionManager {
 26    private List<JingleConnection> connections = new CopyOnWriteArrayList<>();
 27
 28    private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
 29
 30    @SuppressLint("TrulyRandom")
 31    private SecureRandom random = new SecureRandom();
 32
 33    public JingleConnectionManager(XmppConnectionService service) {
 34        super(service);
 35    }
 36
 37    public void deliverPacket(Account account, JinglePacket packet) {
 38        if (packet.isAction("session-initiate")) {
 39            JingleConnection connection = new JingleConnection(this);
 40            connection.init(account, packet);
 41            connections.add(connection);
 42        } else {
 43            for (JingleConnection connection : connections) {
 44                if (connection.getAccount() == account
 45                        && connection.getSessionId().equals(
 46                        packet.getSessionId())
 47                        && connection.getCounterPart().equals(packet.getFrom())) {
 48                    connection.deliverPacket(packet);
 49                    return;
 50                }
 51            }
 52            Log.d(Config.LOGTAG, "unable to route jingle packet: " + packet);
 53            IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
 54            Element error = response.addChild("error");
 55            error.setAttribute("type", "cancel");
 56            error.addChild("item-not-found",
 57                    "urn:ietf:params:xml:ns:xmpp-stanzas");
 58            error.addChild("unknown-session", "urn:xmpp:jingle:errors:1");
 59            account.getXmppConnection().sendIqPacket(response, null);
 60        }
 61    }
 62
 63    public JingleConnection createNewConnection(Message message) {
 64        Transferable old = message.getTransferable();
 65        if (old != null) {
 66            old.cancel();
 67        }
 68        JingleConnection connection = new JingleConnection(this);
 69        mXmppConnectionService.markMessage(message, Message.STATUS_WAITING);
 70        connection.init(message);
 71        this.connections.add(connection);
 72        return connection;
 73    }
 74
 75    public JingleConnection createNewConnection(final JinglePacket packet) {
 76        JingleConnection connection = new JingleConnection(this);
 77        this.connections.add(connection);
 78        return connection;
 79    }
 80
 81    public void finishConnection(JingleConnection connection) {
 82        this.connections.remove(connection);
 83    }
 84
 85    public void getPrimaryCandidate(final Account account, final boolean initiator, final OnPrimaryCandidateFound listener) {
 86        if (Config.DISABLE_PROXY_LOOKUP) {
 87            listener.onPrimaryCandidateFound(false, null);
 88            return;
 89        }
 90        if (!this.primaryCandidates.containsKey(account.getJid().asBareJid())) {
 91            final Jid proxy = account.getXmppConnection().findDiscoItemByFeature(Namespace.BYTE_STREAMS);
 92            if (proxy != null) {
 93                IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
 94                iq.setTo(proxy);
 95                iq.query(Namespace.BYTE_STREAMS);
 96                account.getXmppConnection().sendIqPacket(iq, new OnIqPacketReceived() {
 97
 98                    @Override
 99                    public void onIqPacketReceived(Account account, IqPacket packet) {
100                        Element streamhost = packet.query().findChild("streamhost", Namespace.BYTE_STREAMS);
101                        final String host = streamhost == null ? null : streamhost.getAttribute("host");
102                        final String port = streamhost == null ? null : streamhost.getAttribute("port");
103                        if (host != null && port != null) {
104                            try {
105                                JingleCandidate candidate = new JingleCandidate(nextRandomId(), true);
106                                candidate.setHost(host);
107                                candidate.setPort(Integer.parseInt(port));
108                                candidate.setType(JingleCandidate.TYPE_PROXY);
109                                candidate.setJid(proxy);
110                                candidate.setPriority(655360 + (initiator ? 30 : 0));
111                                primaryCandidates.put(account.getJid().asBareJid(), candidate);
112                                listener.onPrimaryCandidateFound(true, candidate);
113                            } catch (final NumberFormatException e) {
114                                listener.onPrimaryCandidateFound(false, null);
115                                return;
116                            }
117                        } else {
118                            listener.onPrimaryCandidateFound(false, null);
119                        }
120                    }
121                });
122            } else {
123                listener.onPrimaryCandidateFound(false, null);
124            }
125
126        } else {
127            listener.onPrimaryCandidateFound(true,
128                    this.primaryCandidates.get(account.getJid().asBareJid()));
129        }
130    }
131
132    public String nextRandomId() {
133        return new BigInteger(50, random).toString(32);
134    }
135
136    public void deliverIbbPacket(Account account, IqPacket packet) {
137        String sid = null;
138        Element payload = null;
139        if (packet.hasChild("open", "http://jabber.org/protocol/ibb")) {
140            payload = packet.findChild("open", "http://jabber.org/protocol/ibb");
141            sid = payload.getAttribute("sid");
142        } else if (packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
143            payload = packet.findChild("data", "http://jabber.org/protocol/ibb");
144            sid = payload.getAttribute("sid");
145        } else if (packet.hasChild("close", "http://jabber.org/protocol/ibb")) {
146            payload = packet.findChild("close", "http://jabber.org/protocol/ibb");
147            sid = payload.getAttribute("sid");
148        }
149        if (sid != null) {
150            for (JingleConnection connection : connections) {
151                if (connection.getAccount() == account
152                        && connection.hasTransportId(sid)) {
153                    JingleTransport transport = connection.getTransport();
154                    if (transport instanceof JingleInBandTransport) {
155                        JingleInBandTransport inbandTransport = (JingleInBandTransport) transport;
156                        inbandTransport.deliverPayload(packet, payload);
157                        return;
158                    }
159                }
160            }
161        }
162        Log.d(Config.LOGTAG, "unable to deliver ibb packet: " + packet.toString());
163        account.getXmppConnection().sendIqPacket(packet.generateResponse(IqPacket.TYPE.ERROR), null);
164    }
165
166    public void cancelInTransmission() {
167        for (JingleConnection connection : this.connections) {
168            if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
169                connection.abort("connectivity-error");
170            }
171        }
172    }
173}