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(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						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 ? 10 : 20));
110								primaryCandidates.put(account.getJid().asBareJid(),candidate);
111								listener.onPrimaryCandidateFound(true,candidate);
112							} catch (final NumberFormatException e) {
113								listener.onPrimaryCandidateFound(false,null);
114								return;
115							}
116						} else {
117							listener.onPrimaryCandidateFound(false,null);
118						}
119					}
120				});
121			} else {
122				listener.onPrimaryCandidateFound(false, null);
123			}
124
125		} else {
126			listener.onPrimaryCandidateFound(true,
127					this.primaryCandidates.get(account.getJid().asBareJid()));
128		}
129	}
130
131	public String nextRandomId() {
132		return new BigInteger(50, random).toString(32);
133	}
134
135	public void deliverIbbPacket(Account account, IqPacket packet) {
136		String sid = null;
137		Element payload = null;
138		if (packet.hasChild("open", "http://jabber.org/protocol/ibb")) {
139			payload = packet.findChild("open", "http://jabber.org/protocol/ibb");
140			sid = payload.getAttribute("sid");
141		} else if (packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
142			payload = packet.findChild("data", "http://jabber.org/protocol/ibb");
143			sid = payload.getAttribute("sid");
144		} else if (packet.hasChild("close","http://jabber.org/protocol/ibb")) {
145			payload = packet.findChild("close", "http://jabber.org/protocol/ibb");
146			sid = payload.getAttribute("sid");
147		}
148		if (sid != null) {
149			for (JingleConnection connection : connections) {
150				if (connection.getAccount() == account
151						&& connection.hasTransportId(sid)) {
152					JingleTransport transport = connection.getTransport();
153					if (transport instanceof JingleInbandTransport) {
154						JingleInbandTransport inbandTransport = (JingleInbandTransport) transport;
155						inbandTransport.deliverPayload(packet, payload);
156						return;
157					}
158				}
159			}
160			Log.d(Config.LOGTAG,"couldn't deliver payload: " + payload.toString());
161		} else {
162			Log.d(Config.LOGTAG, "no sid found in incoming ibb packet");
163		}
164	}
165
166	public void cancelInTransmission() {
167		for (JingleConnection connection : this.connections) {
168			if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
169				connection.abort();
170			}
171		}
172	}
173}