JingleConnection.java

  1package eu.siacs.conversations.xmpp.jingle;
  2
  3import java.io.File;
  4import java.util.ArrayList;
  5import java.util.HashMap;
  6import java.util.List;
  7
  8import android.util.Log;
  9
 10import eu.siacs.conversations.entities.Account;
 11import eu.siacs.conversations.entities.Message;
 12import eu.siacs.conversations.services.XmppConnectionService;
 13import eu.siacs.conversations.xml.Element;
 14import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 15import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
 16import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
 17import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 18
 19public class JingleConnection {
 20
 21	private JingleConnectionManager mJingleConnectionManager;
 22	private XmppConnectionService mXmppConnectionService;
 23	
 24	public static final int STATUS_INITIATED = 0;
 25	public static final int STATUS_ACCEPTED = 1;
 26	public static final int STATUS_TERMINATED = 2;
 27	public static final int STATUS_FAILED = 99;
 28	
 29	private int status = -1;
 30	private Message message;
 31	private String sessionId;
 32	private Account account;
 33	private String initiator;
 34	private String responder;
 35	private List<Element> candidates = new ArrayList<Element>();
 36	private HashMap<String, SocksConnection> connections = new HashMap<String, SocksConnection>();
 37	
 38	private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
 39		
 40		@Override
 41		public void onIqPacketReceived(Account account, IqPacket packet) {
 42			if (packet.getType() == IqPacket.TYPE_ERROR) {
 43				mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
 44				status = STATUS_FAILED;
 45			}
 46		}
 47	};
 48	
 49	public JingleConnection(JingleConnectionManager mJingleConnectionManager) {
 50		this.mJingleConnectionManager = mJingleConnectionManager;
 51		this.mXmppConnectionService = mJingleConnectionManager.getXmppConnectionService();
 52		this.sessionId = this.mJingleConnectionManager.nextRandomId();
 53	}
 54	
 55	public String getSessionId() {
 56		return this.sessionId;
 57	}
 58	
 59	public String getAccountJid() {
 60		return this.account.getJid();
 61	}
 62	
 63	public String getCounterPart() {
 64		return this.message.getCounterpart();
 65	}
 66	
 67	public void deliverPacket(JinglePacket packet) {
 68		
 69		if (packet.isAction("session-terminate")) {
 70			if (status == STATUS_INITIATED) {
 71				mXmppConnectionService.markMessage(message, Message.STATUS_SEND_REJECTED);
 72			}
 73			status = STATUS_TERMINATED;
 74		} else if (packet.isAction("session-accept")) {
 75			accept(packet);
 76		} else if (packet.isAction("transport-info")) {
 77			transportInfo(packet);
 78		} else {
 79			Log.d("xmppService","packet arrived in connection. action was "+packet.getAction());
 80		}
 81	}
 82	
 83	public void init(Message message) {
 84		this.message = message;
 85		this.account = message.getConversation().getAccount();
 86		this.initiator = this.account.getFullJid();
 87		this.responder = this.message.getCounterpart();
 88		if (this.candidates.size() > 0) {
 89			this.sendInitRequest();
 90		} else {
 91			this.mJingleConnectionManager.getPrimaryCandidate(account, new OnPrimaryCandidateFound() {
 92				
 93				@Override
 94				public void onPrimaryCandidateFound(boolean success, Element canditate) {
 95					if (success) {
 96						candidates.add(canditate);
 97					}
 98					sendInitRequest();
 99				}
100			});
101		}
102		
103	}
104	
105	private void sendInitRequest() {
106		JinglePacket packet = this.bootstrapPacket();
107		packet.setAction("session-initiate");
108		packet.setInitiator(this.account.getFullJid());
109		Content content = new Content();
110		if (message.getType() == Message.TYPE_IMAGE) {
111			content.setAttribute("creator", "initiator");
112			content.setAttribute("name", "a-file-offer");
113			content.offerFile(this.mXmppConnectionService.getFileBackend().getImageFile(message));
114			content.setCandidates(this.mJingleConnectionManager.nextRandomId(),this.candidates);
115			packet.setContent(content);
116			Log.d("xmppService",packet.toString());
117			account.getXmppConnection().sendIqPacket(packet, this.responseListener);
118			this.status = STATUS_INITIATED;
119		}
120	}
121	
122	private JinglePacket bootstrapPacket() {
123		JinglePacket packet = new JinglePacket();
124		packet.setFrom(account.getFullJid());
125		packet.setTo(this.message.getCounterpart()); //fixme, not right in all cases;
126		packet.setSessionId(this.sessionId);
127		return packet;
128	}
129	
130	private void accept(JinglePacket packet) {
131		Log.d("xmppService","session-accept: "+packet.toString());
132		Content content = packet.getJingleContent();
133		this.candidates.addAll(content.getCanditates());
134		this.status = STATUS_ACCEPTED;
135		this.connectWithCandidates();
136		IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT);
137		Log.d("xmppService","response "+response.toString());
138		account.getXmppConnection().sendIqPacket(response, null);
139	}
140	
141	private void transportInfo(JinglePacket packet) {
142		Content content = packet.getJingleContent();
143		Log.d("xmppService","transport info : "+content.toString());
144		String cid = content.getUsedCandidate();
145		if (cid!=null) {
146			final File file = this.mXmppConnectionService.getFileBackend().getImageFile(this.message);
147			final SocksConnection connection = this.connections.get(cid);
148			if (connection.isProxy()) {
149				IqPacket activation = new IqPacket(IqPacket.TYPE_SET);
150				activation.setTo(connection.getJid());
151				activation.query("http://jabber.org/protocol/bytestreams").setAttribute("sid", this.getSessionId());
152				activation.query().addChild("activate").setContent(this.getResponder());
153				Log.d("xmppService","connection is proxy. need to activate "+activation.toString());
154				this.account.getXmppConnection().sendIqPacket(activation, new OnIqPacketReceived() {
155					
156					@Override
157					public void onIqPacketReceived(Account account, IqPacket packet) {
158						Log.d("xmppService","activation result: "+packet.toString());
159						connection.send(file);
160					}
161				});
162			} else {
163				connection.send(file);
164			}
165		}
166	}
167	
168	private void connectWithCandidates() {
169		for(Element canditate : this.candidates) {
170			String host = canditate.getAttribute("host");
171			int port = Integer.parseInt(canditate.getAttribute("port"));
172			String type = canditate.getAttribute("type");
173			String jid = canditate.getAttribute("jid");
174			SocksConnection socksConnection = new SocksConnection(this, host, jid, port,type);
175			socksConnection.connect();
176			this.connections.put(canditate.getAttribute("cid"), socksConnection);
177		}
178	}
179	
180	private void sendCandidateUsed(String cid) {
181		
182	}
183
184	public String getInitiator() {
185		return this.initiator;
186	}
187	
188	public String getResponder() {
189		return this.responder;
190	}
191}