JingleConnectionManager.java

  1package eu.siacs.conversations.xmpp.jingle;
  2
  3import android.util.Log;
  4
  5import com.google.common.base.Preconditions;
  6
  7import java.util.HashMap;
  8import java.util.Map;
  9import java.util.UUID;
 10import java.util.concurrent.ConcurrentHashMap;
 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.Element;
 19import eu.siacs.conversations.xml.Namespace;
 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 Map<AbstractJingleConnection.Id, AbstractJingleConnection> connections = new ConcurrentHashMap<>();
 27
 28    private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
 29
 30    public JingleConnectionManager(XmppConnectionService service) {
 31        super(service);
 32    }
 33
 34    public void deliverPacket(final Account account, final JinglePacket packet) {
 35        final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, packet);
 36        if (packet.getAction() == JinglePacket.Action.SESSION_INITIATE) { //TODO check that id doesn't exist yet
 37            JingleFileTransferConnection connection = new JingleFileTransferConnection(this, id);
 38            connection.init(account, packet);
 39            connections.put(id, connection);
 40        } else {
 41            final AbstractJingleConnection abstractJingleConnection = connections.get(id);
 42            if (abstractJingleConnection != null) {
 43                abstractJingleConnection.deliverPacket(packet);
 44            } else {
 45                Log.d(Config.LOGTAG, "unable to route jingle packet: " + packet);
 46                IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
 47                Element error = response.addChild("error");
 48                error.setAttribute("type", "cancel");
 49                error.addChild("item-not-found",
 50                        "urn:ietf:params:xml:ns:xmpp-stanzas");
 51                error.addChild("unknown-session", "urn:xmpp:jingle:errors:1");
 52                account.getXmppConnection().sendIqPacket(response, null);
 53            }
 54        }
 55    }
 56
 57    public void startJingleFileTransfer(final Message message) {
 58        Preconditions.checkArgument(message.isFileOrImage(), "Message is not of type file or image");
 59        final Transferable old = message.getTransferable();
 60        if (old != null) {
 61            old.cancel();
 62        }
 63        final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(message);
 64        final JingleFileTransferConnection connection = new JingleFileTransferConnection(this, id);
 65        mXmppConnectionService.markMessage(message, Message.STATUS_WAITING);
 66        connection.init(message);
 67        this.connections.put(id, connection);
 68    }
 69
 70    void finishConnection(final AbstractJingleConnection connection) {
 71        this.connections.remove(connection.getId());
 72    }
 73
 74    void getPrimaryCandidate(final Account account, final boolean initiator, final OnPrimaryCandidateFound listener) {
 75        if (Config.DISABLE_PROXY_LOOKUP) {
 76            listener.onPrimaryCandidateFound(false, null);
 77            return;
 78        }
 79        if (!this.primaryCandidates.containsKey(account.getJid().asBareJid())) {
 80            final Jid proxy = account.getXmppConnection().findDiscoItemByFeature(Namespace.BYTE_STREAMS);
 81            if (proxy != null) {
 82                IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
 83                iq.setTo(proxy);
 84                iq.query(Namespace.BYTE_STREAMS);
 85                account.getXmppConnection().sendIqPacket(iq, new OnIqPacketReceived() {
 86
 87                    @Override
 88                    public void onIqPacketReceived(Account account, IqPacket packet) {
 89                        final Element streamhost = packet.query().findChild("streamhost", Namespace.BYTE_STREAMS);
 90                        final String host = streamhost == null ? null : streamhost.getAttribute("host");
 91                        final String port = streamhost == null ? null : streamhost.getAttribute("port");
 92                        if (host != null && port != null) {
 93                            try {
 94                                JingleCandidate candidate = new JingleCandidate(nextRandomId(), true);
 95                                candidate.setHost(host);
 96                                candidate.setPort(Integer.parseInt(port));
 97                                candidate.setType(JingleCandidate.TYPE_PROXY);
 98                                candidate.setJid(proxy);
 99                                candidate.setPriority(655360 + (initiator ? 30 : 0));
100                                primaryCandidates.put(account.getJid().asBareJid(), candidate);
101                                listener.onPrimaryCandidateFound(true, candidate);
102                            } catch (final NumberFormatException e) {
103                                listener.onPrimaryCandidateFound(false, null);
104                            }
105                        } else {
106                            listener.onPrimaryCandidateFound(false, null);
107                        }
108                    }
109                });
110            } else {
111                listener.onPrimaryCandidateFound(false, null);
112            }
113
114        } else {
115            listener.onPrimaryCandidateFound(true,
116                    this.primaryCandidates.get(account.getJid().asBareJid()));
117        }
118    }
119
120    static String nextRandomId() {
121        return UUID.randomUUID().toString();
122    }
123
124    public void deliverIbbPacket(Account account, IqPacket packet) {
125        final String sid;
126        final Element payload;
127        if (packet.hasChild("open", Namespace.IBB)) {
128            payload = packet.findChild("open", Namespace.IBB);
129            sid = payload.getAttribute("sid");
130        } else if (packet.hasChild("data", Namespace.IBB)) {
131            payload = packet.findChild("data", Namespace.IBB);
132            sid = payload.getAttribute("sid");
133        } else if (packet.hasChild("close", Namespace.IBB)) {
134            payload = packet.findChild("close", Namespace.IBB);
135            sid = payload.getAttribute("sid");
136        } else {
137            payload = null;
138            sid = null;
139        }
140        if (sid != null) {
141            for (final AbstractJingleConnection connection : this.connections.values()) {
142                if (connection instanceof JingleFileTransferConnection) {
143                    final JingleFileTransferConnection fileTransfer = (JingleFileTransferConnection) connection;
144                    final JingleTransport transport = fileTransfer.getTransport();
145                    if (transport instanceof JingleInBandTransport) {
146                        final JingleInBandTransport inBandTransport = (JingleInBandTransport) transport;
147                        if (inBandTransport.matches(account, sid)) {
148                            inBandTransport.deliverPayload(packet, payload);
149                        }
150                        return;
151                    }
152                }
153            }
154        }
155        Log.d(Config.LOGTAG, "unable to deliver ibb packet: " + packet.toString());
156        account.getXmppConnection().sendIqPacket(packet.generateResponse(IqPacket.TYPE.ERROR), null);
157    }
158
159    public void cancelInTransmission() {
160        for (AbstractJingleConnection connection : this.connections.values()) {
161            /*if (connection.getJingleStatus() == JingleFileTransferConnection.JINGLE_STATUS_TRANSMITTING) {
162                connection.abort("connectivity-error");
163            }*/
164        }
165    }
166}