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.Downloadable;
 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.InvalidJidException;
 20import eu.siacs.conversations.xmpp.jid.Jid;
 21import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
 22import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 23
 24public class JingleConnectionManager extends AbstractConnectionManager {
 25	private List<JingleConnection> connections = new CopyOnWriteArrayList<>();
 26
 27	private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
 28
 29	@SuppressLint("TrulyRandom")
 30	private SecureRandom random = new SecureRandom();
 31
 32	public JingleConnectionManager(XmppConnectionService service) {
 33		super(service);
 34	}
 35
 36	public void deliverPacket(Account account, JinglePacket packet) {
 37		if (packet.isAction("session-initiate")) {
 38			JingleConnection connection = new JingleConnection(this);
 39			connection.init(account, packet);
 40			connections.add(connection);
 41		} else {
 42			for (JingleConnection connection : connections) {
 43				if (connection.getAccount() == account
 44						&& connection.getSessionId().equals(
 45								packet.getSessionId())
 46						&& connection.getCounterPart().equals(packet.getFrom())) {
 47					connection.deliverPacket(packet);
 48					return;
 49				}
 50			}
 51			IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
 52			Element error = response.addChild("error");
 53			error.setAttribute("type", "cancel");
 54			error.addChild("item-not-found",
 55					"urn:ietf:params:xml:ns:xmpp-stanzas");
 56			error.addChild("unknown-session", "urn:xmpp:jingle:errors:1");
 57			account.getXmppConnection().sendIqPacket(response, null);
 58		}
 59	}
 60
 61	public JingleConnection createNewConnection(Message message) {
 62		Downloadable old = message.getDownloadable();
 63		if (old != null) {
 64			old.cancel();
 65		}
 66		JingleConnection connection = new JingleConnection(this);
 67		mXmppConnectionService.markMessage(message,Message.STATUS_WAITING);
 68		connection.init(message);
 69		this.connections.add(connection);
 70		return connection;
 71	}
 72
 73	public JingleConnection createNewConnection(final JinglePacket packet) {
 74		JingleConnection connection = new JingleConnection(this);
 75		this.connections.add(connection);
 76		return connection;
 77	}
 78
 79	public void finishConnection(JingleConnection connection) {
 80		this.connections.remove(connection);
 81	}
 82
 83	public void getPrimaryCandidate(Account account,
 84			final OnPrimaryCandidateFound listener) {
 85		if (Config.NO_PROXY_LOOKUP) {
 86			listener.onPrimaryCandidateFound(false, null);
 87			return;
 88		}
 89		if (!this.primaryCandidates.containsKey(account.getJid().toBareJid())) {
 90			final Jid proxy = account.getXmppConnection().findDiscoItemByFeature(Xmlns.BYTE_STREAMS);
 91			if (proxy != null) {
 92				IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
 93				iq.setTo(proxy);
 94				iq.query(Xmlns.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",Xmlns.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 + 65535);
110								primaryCandidates.put(account.getJid().toBareJid(),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().toBareJid()));
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		}
145		if (sid != null) {
146			for (JingleConnection connection : connections) {
147				if (connection.getAccount() == account
148						&& connection.hasTransportId(sid)) {
149					JingleTransport transport = connection.getTransport();
150					if (transport instanceof JingleInbandTransport) {
151						JingleInbandTransport inbandTransport = (JingleInbandTransport) transport;
152						inbandTransport.deliverPayload(packet, payload);
153						return;
154					}
155				}
156			}
157			Log.d(Config.LOGTAG,"couldn't deliver payload: " + payload.toString());
158		} else {
159			Log.d(Config.LOGTAG, "no sid found in incoming ibb packet");
160		}
161	}
162
163	public void cancelInTransmission() {
164		for (JingleConnection connection : this.connections) {
165			if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
166				connection.cancel();
167			}
168		}
169	}
170}