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.Transferable;
 13import eu.siacs.conversations.entities.Message;
 14import eu.siacs.conversations.services.AbstractConnectionManager;
 15import eu.siacs.conversations.services.XmppConnectionService;
 16import eu.siacs.conversations.utils.Xmlns;
 17import eu.siacs.conversations.xml.Element;
 18import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 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		Transferable old = message.getTransferable();
 62		if (old != null) {
 63			old.cancel();
 64		}
 65		JingleConnection connection = new JingleConnection(this);
 66		mXmppConnectionService.markMessage(message,Message.STATUS_WAITING);
 67		connection.init(message);
 68		this.connections.add(connection);
 69		return connection;
 70	}
 71
 72	public JingleConnection createNewConnection(final JinglePacket packet) {
 73		JingleConnection connection = new JingleConnection(this);
 74		this.connections.add(connection);
 75		return connection;
 76	}
 77
 78	public void finishConnection(JingleConnection connection) {
 79		this.connections.remove(connection);
 80	}
 81
 82	public void getPrimaryCandidate(Account account,
 83			final OnPrimaryCandidateFound listener) {
 84		if (Config.NO_PROXY_LOOKUP) {
 85			listener.onPrimaryCandidateFound(false, null);
 86			return;
 87		}
 88		if (!this.primaryCandidates.containsKey(account.getJid().toBareJid())) {
 89			final Jid proxy = account.getXmppConnection().findDiscoItemByFeature(Xmlns.BYTE_STREAMS);
 90			if (proxy != null) {
 91				IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
 92				iq.setTo(proxy);
 93				iq.query(Xmlns.BYTE_STREAMS);
 94				account.getXmppConnection().sendIqPacket(iq,new OnIqPacketReceived() {
 95
 96					@Override
 97					public void onIqPacketReceived(Account account, IqPacket packet) {
 98						Element streamhost = packet.query().findChild("streamhost",Xmlns.BYTE_STREAMS);
 99						final String host = streamhost == null ? null : streamhost.getAttribute("host");
100						final String port = streamhost == null ? null : streamhost.getAttribute("port");
101						if (host != null && port != null) {
102							try {
103								JingleCandidate candidate = new JingleCandidate(nextRandomId(), true);
104								candidate.setHost(host);
105								candidate.setPort(Integer.parseInt(port));
106								candidate.setType(JingleCandidate.TYPE_PROXY);
107								candidate.setJid(proxy);
108								candidate.setPriority(655360 + 65535);
109								primaryCandidates.put(account.getJid().toBareJid(),candidate);
110								listener.onPrimaryCandidateFound(true,candidate);
111							} catch (final NumberFormatException e) {
112								listener.onPrimaryCandidateFound(false,null);
113								return;
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().toBareJid()));
127		}
128	}
129
130	public String nextRandomId() {
131		return new BigInteger(50, random).toString(32);
132	}
133
134	public void deliverIbbPacket(Account account, IqPacket packet) {
135		String sid = null;
136		Element payload = null;
137		if (packet.hasChild("open", "http://jabber.org/protocol/ibb")) {
138			payload = packet.findChild("open", "http://jabber.org/protocol/ibb");
139			sid = payload.getAttribute("sid");
140		} else if (packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
141			payload = packet.findChild("data", "http://jabber.org/protocol/ibb");
142			sid = payload.getAttribute("sid");
143		}
144		if (sid != null) {
145			for (JingleConnection connection : connections) {
146				if (connection.getAccount() == account
147						&& connection.hasTransportId(sid)) {
148					JingleTransport transport = connection.getTransport();
149					if (transport instanceof JingleInbandTransport) {
150						JingleInbandTransport inbandTransport = (JingleInbandTransport) transport;
151						inbandTransport.deliverPayload(packet, payload);
152						return;
153					}
154				}
155			}
156			Log.d(Config.LOGTAG,"couldn't deliver payload: " + payload.toString());
157		} else {
158			Log.d(Config.LOGTAG, "no sid found in incoming ibb packet");
159		}
160	}
161
162	public void cancelInTransmission() {
163		for (JingleConnection connection : this.connections) {
164			if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
165				connection.cancel();
166			}
167		}
168	}
169}