JingleConnectionManager.java

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