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