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			IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
 53			Element error = response.addChild("error");
 54			error.setAttribute("type", "cancel");
 55			error.addChild("item-not-found",
 56					"urn:ietf:params:xml:ns:xmpp-stanzas");
 57			error.addChild("unknown-session", "urn:xmpp:jingle:errors:1");
 58			account.getXmppConnection().sendIqPacket(response, null);
 59		}
 60	}
 61
 62	public JingleConnection createNewConnection(Message message) {
 63		Transferable old = message.getTransferable();
 64		if (old != null) {
 65			old.cancel();
 66		}
 67		JingleConnection connection = new JingleConnection(this);
 68		mXmppConnectionService.markMessage(message,Message.STATUS_WAITING);
 69		connection.init(message);
 70		this.connections.add(connection);
 71		return connection;
 72	}
 73
 74	public JingleConnection createNewConnection(final JinglePacket packet) {
 75		JingleConnection connection = new JingleConnection(this);
 76		this.connections.add(connection);
 77		return connection;
 78	}
 79
 80	public void finishConnection(JingleConnection connection) {
 81		this.connections.remove(connection);
 82	}
 83
 84	public void getPrimaryCandidate(Account account,
 85			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 + 65535);
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			Log.d(Config.LOGTAG,"couldn't deliver payload: " + payload.toString());
162		} else {
163			Log.d(Config.LOGTAG, "no sid found in incoming ibb packet");
164		}
165	}
166
167	public void cancelInTransmission() {
168		for (JingleConnection connection : this.connections) {
169			if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
170				connection.abort();
171			}
172		}
173	}
174}