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