diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index ad2d94f8..b40bf6dd 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -48,6 +48,8 @@ public class JingleConnection { private boolean receivedCandidate = false; private boolean sentCandidate = false; + private JingleTransport transport = null; + private OnIqPacketReceived responseListener = new OnIqPacketReceived() { @Override @@ -59,6 +61,37 @@ public class JingleConnection { } }; + final OnFileTransmitted onFileTransmitted = new OnFileTransmitted() { + + @Override + public void onFileTransmitted(JingleFile file) { + if (responder.equals(account.getFullJid())) { + sendSuccess(); + mXmppConnectionService.markMessage(message, Message.STATUS_RECIEVED); + } + Log.d("xmppService","sucessfully transmitted file. sha1:"+file.getSha1Sum()); + } + }; + + private OnProxyActivated onProxyActivated = new OnProxyActivated() { + + @Override + public void success() { + if (initiator.equals(account.getFullJid())) { + Log.d("xmppService","we were initiating. sending file"); + transport.send(file,onFileTransmitted); + } else { + transport.receive(file,onFileTransmitted); + Log.d("xmppService","we were responding. receiving file"); + } + } + + @Override + public void failed() { + Log.d("xmppService","proxy activation failed"); + } + }; + public JingleConnection(JingleConnectionManager mJingleConnectionManager) { this.mJingleConnectionManager = mJingleConnectionManager; this.mXmppConnectionService = mJingleConnectionManager.getXmppConnectionService(); @@ -155,7 +188,7 @@ public class JingleConnection { this.sessionId = packet.getSessionId(); Content content = packet.getJingleContent(); this.transportId = content.getTransportId(); - this.mergeCandidates(JingleCandidate.parse(content.getCanditates())); + this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren())); this.fileOffer = packet.getJingleContent().getFileOffer(); if (fileOffer!=null) { this.file = this.mXmppConnectionService.getFileBackend().getJingleFile(message); @@ -184,10 +217,11 @@ public class JingleConnection { if (message.getType() == Message.TYPE_IMAGE) { content.setAttribute("creator", "initiator"); content.setAttribute("name", "a-file-offer"); + content.setTransportId(this.transportId); this.file = this.mXmppConnectionService.getFileBackend().getJingleFile(message); content.setFileOffer(this.file); this.transportId = this.mJingleConnectionManager.nextRandomId(); - content.setCandidates(this.transportId,getCandidatesAsElements()); + content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); this.sendJinglePacket(packet); this.status = STATUS_INITIATED; @@ -212,6 +246,7 @@ public class JingleConnection { final JinglePacket packet = bootstrapPacket("session-accept"); final Content content = new Content(); content.setFileOffer(fileOffer); + content.setTransportId(transportId); if ((success)&&(!equalCandidateExists(candidate))) { final SocksConnection socksConnection = new SocksConnection(JingleConnection.this, candidate); connections.put(candidate.getCid(), socksConnection); @@ -220,7 +255,7 @@ public class JingleConnection { @Override public void failed() { Log.d("xmppService","connection to our own primary candidate failed"); - content.setCandidates(transportId, getCandidatesAsElements()); + content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); sendJinglePacket(packet); } @@ -229,14 +264,14 @@ public class JingleConnection { public void established() { Log.d("xmppService","connected to primary candidate"); mergeCandidate(candidate); - content.setCandidates(transportId, getCandidatesAsElements()); + content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); sendJinglePacket(packet); } }); } else { Log.d("xmppService","did not find a primary candidate for ourself"); - content.setCandidates(transportId, getCandidatesAsElements()); + content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); sendJinglePacket(packet); } @@ -256,13 +291,13 @@ public class JingleConnection { } private void sendJinglePacket(JinglePacket packet) { - Log.d("xmppService",packet.toPrettyString()); + //Log.d("xmppService",packet.toString()); account.getXmppConnection().sendIqPacket(packet,responseListener); } private void accept(JinglePacket packet) { Content content = packet.getJingleContent(); - mergeCandidates(JingleCandidate.parse(content.getCanditates())); + mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren())); this.status = STATUS_ACCEPTED; this.connectNextCandidate(); IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT); @@ -271,30 +306,44 @@ public class JingleConnection { private void transportInfo(JinglePacket packet) { Content content = packet.getJingleContent(); - String cid = content.getUsedCandidate(); - IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT); - if (cid!=null) { - Log.d("xmppService","candidate used by counterpart:"+cid); - JingleCandidate candidate = getCandidate(cid); - candidate.flagAsUsedByCounterpart(); - this.receivedCandidate = true; - if ((status == STATUS_ACCEPTED)&&(this.sentCandidate)) { - this.connect(); + if (content.hasSocks5Transport()) { + if (content.socks5transport().hasChild("activated")) { + onProxyActivated.success(); + } else if (content.socks5transport().hasChild("activated")) { + onProxyActivated.failed(); + } else if (content.socks5transport().hasChild("candidate-error")) { + Log.d("xmppService","received candidate error"); + this.receivedCandidate = true; + if (status == STATUS_ACCEPTED) { + this.connect(); + } + } else if (content.socks5transport().hasChild("candidate-used")){ + String cid = content.socks5transport().findChild("candidate-used").getAttribute("cid"); + if (cid!=null) { + Log.d("xmppService","candidate used by counterpart:"+cid); + JingleCandidate candidate = getCandidate(cid); + candidate.flagAsUsedByCounterpart(); + this.receivedCandidate = true; + if ((status == STATUS_ACCEPTED)&&(this.sentCandidate)) { + this.connect(); + } else { + Log.d("xmppService","ignoring because file is already in transmission or we havent sent our candidate yet"); + } + } else { + Log.d("xmppService","couldn't read used candidate"); + } } else { - Log.d("xmppService","ignoring because file is already in transmission or we havent sent our candidate yet"); - } - } else if (content.hasCandidateError()) { - Log.d("xmppService","received candidate error"); - this.receivedCandidate = true; - if (status == STATUS_ACCEPTED) { - this.connect(); + Log.d("xmppService","empty transport"); } } + + IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT); account.getXmppConnection().sendIqPacket(response, null); } private void connect() { final SocksConnection connection = chooseConnection(); + this.transport = connection; if (connection==null) { Log.d("xmppService","could not find suitable candidate"); this.disconnect(); @@ -302,44 +351,33 @@ public class JingleConnection { this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED); } else { this.status = STATUS_TRANSMITTING; - final OnFileTransmitted callback = new OnFileTransmitted() { - - @Override - public void onFileTransmitted(JingleFile file) { - if (responder.equals(account.getFullJid())) { - sendSuccess(); - mXmppConnectionService.markMessage(message, Message.STATUS_RECIEVED); - } - Log.d("xmppService","sucessfully transmitted file. sha1:"+file.getSha1Sum()); - } - }; - if (connection.isProxy()&&(connection.getCandidate().isOurs())) { - Log.d("xmppService","candidate "+connection.getCandidate().getCid()+" was our proxy and needs activation"); - IqPacket activation = new IqPacket(IqPacket.TYPE_SET); - activation.setTo(connection.getCandidate().getJid()); - activation.query("http://jabber.org/protocol/bytestreams").setAttribute("sid", this.getSessionId()); - activation.query().addChild("activate").setContent(this.getCounterPart()); - this.account.getXmppConnection().sendIqPacket(activation, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - Log.d("xmppService","activation result: "+packet.toString()); - if (initiator.equals(account.getFullJid())) { - Log.d("xmppService","we were initiating. sending file"); - connection.send(file,callback); - } else { - connection.receive(file,callback); - Log.d("xmppService","we were responding. receiving file"); + if (connection.isProxy()) { + if (connection.getCandidate().isOurs()) { + Log.d("xmppService","candidate "+connection.getCandidate().getCid()+" was our proxy and needs activation"); + IqPacket activation = new IqPacket(IqPacket.TYPE_SET); + activation.setTo(connection.getCandidate().getJid()); + activation.query("http://jabber.org/protocol/bytestreams").setAttribute("sid", this.getSessionId()); + activation.query().addChild("activate").setContent(this.getCounterPart()); + this.account.getXmppConnection().sendIqPacket(activation, new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType()==IqPacket.TYPE_ERROR) { + onProxyActivated.failed(); + } else { + onProxyActivated.success(); + sendProxyActivated(connection.getCandidate().getCid()); + } } - } - }); + }); + } } else { if (initiator.equals(account.getFullJid())) { Log.d("xmppService","we were initiating. sending file"); - connection.send(file,callback); + connection.send(file,onFileTransmitted); } else { Log.d("xmppService","we were responding. receiving file"); - connection.receive(file,callback); + connection.receive(file,onFileTransmitted); } } } @@ -439,13 +477,26 @@ public class JingleConnection { } } + private void sendProxyActivated(String cid) { + JinglePacket packet = bootstrapPacket("transport-info"); + Content content = new Content(); + //TODO: put these into actual variables + content.setAttribute("creator", "initiator"); + content.setAttribute("name", "a-file-offer"); + content.setTransportId(this.transportId); + content.socks5transport().addChild("activated").setAttribute("cid", cid); + packet.setContent(content); + this.sendJinglePacket(packet); + } + private void sendCandidateUsed(final String cid) { JinglePacket packet = bootstrapPacket("transport-info"); Content content = new Content(); //TODO: put these into actual variables content.setAttribute("creator", "initiator"); content.setAttribute("name", "a-file-offer"); - content.setUsedCandidate(this.transportId, cid); + content.setTransportId(this.transportId); + content.setUsedCandidate(cid); packet.setContent(content); this.sendJinglePacket(packet); this.sentCandidate = true; @@ -460,7 +511,8 @@ public class JingleConnection { //TODO: put these into actual variables content.setAttribute("creator", "initiator"); content.setAttribute("name", "a-file-offer"); - content.setCandidateError(this.transportId); + content.setTransportId(this.transportId); + content.setCandidateError(); packet.setContent(content); this.sendJinglePacket(packet); this.sentCandidate = true; @@ -513,4 +565,9 @@ public class JingleConnection { } return null; } + + interface OnProxyActivated { + public void success(); + public void failed(); + } } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java new file mode 100644 index 00000000..347824f1 --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java @@ -0,0 +1,6 @@ +package eu.siacs.conversations.xmpp.jingle; + +public abstract class JingleTransport { + public abstract void receive(final JingleFile file, final OnFileTransmitted callback); + public abstract void send(final JingleFile file, final OnFileTransmitted callback); +} diff --git a/src/eu/siacs/conversations/xmpp/jingle/SocksConnection.java b/src/eu/siacs/conversations/xmpp/jingle/SocksConnection.java index 897c202f..1a945a65 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/SocksConnection.java +++ b/src/eu/siacs/conversations/xmpp/jingle/SocksConnection.java @@ -18,7 +18,7 @@ import eu.siacs.conversations.xml.Element; import android.util.Log; import android.widget.Button; -public class SocksConnection { +public class SocksConnection extends JingleTransport { private JingleCandidate candidate; private String destination; private OutputStream outputStream; diff --git a/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java b/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java index 3cd30251..dbd2a9fc 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java +++ b/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java @@ -1,12 +1,12 @@ package eu.siacs.conversations.xmpp.jingle.stanzas; -import java.util.ArrayList; -import java.util.List; - import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.jingle.JingleFile; public class Content extends Element { + + private String transportId; + private Content(String name) { super(name); } @@ -15,6 +15,10 @@ public class Content extends Element { super("content"); } + public void setTransportId(String sid) { + this.transportId = sid; + } + public void setFileOffer(JingleFile actualFile) { Element description = this.addChild("description", "urn:xmpp:jingle:apps:file-transfer:3"); Element offer = description.addChild("offer"); @@ -34,76 +38,7 @@ public class Content extends Element { } return offer.findChild("file"); } - - public void setCandidates(String transportId, List canditates) { - Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - if (transport==null) { - transport = this.addChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - } - transport.setAttribute("sid", transportId); - transport.clearChildren(); - for(Element canditate : canditates) { - transport.addChild(canditate); - } - } - public List getCanditates() { - Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - if (transport==null) { - return new ArrayList(); - } else { - return transport.getChildren(); - } - } - - public String getTransportId() { - Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - if (transport==null) { - return null; - } - return transport.getAttribute("sid"); - } - - public String getUsedCandidate() { - Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - if (transport==null) { - return null; - } - Element usedCandidate = transport.findChild("candidate-used"); - if (usedCandidate==null) { - return null; - } else { - return usedCandidate.getAttribute("cid"); - } - } - - public boolean hasCandidateError() { - Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - if (transport==null) { - return false; - } - return transport.hasChild("candidate-error"); - } - - public void setUsedCandidate(String transportId, String cid) { - Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - if (transport==null) { - transport = this.addChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - } - transport.setAttribute("sid", transportId); - transport.clearChildren(); - Element usedCandidate = transport.addChild("candidate-used"); - usedCandidate.setAttribute("cid",cid); - } - - public void addCandidate(Element candidate) { - Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - if (transport==null) { - transport = this.addChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - } - transport.addChild(candidate); - } - public void setFileOffer(Element fileOffer) { Element description = this.findChild("description", "urn:xmpp:jingle:apps:file-transfer:3"); if (description==null) { @@ -111,14 +46,50 @@ public class Content extends Element { } description.addChild(fileOffer); } + + public String getTransportId() { + if (hasSocks5Transport()) { + this.transportId = socks5transport().getAttribute("sid"); + } else if (hasIbbTransport()) { + this.transportId = ibbTransport().getAttribute("sid"); + } + return this.transportId; + } + + public void setUsedCandidate(String cid) { + socks5transport().clearChildren(); + Element usedCandidate = socks5transport().addChild("candidate-used"); + usedCandidate.setAttribute("cid",cid); + } - public void setCandidateError(String transportId) { + public void setCandidateError() { + socks5transport().clearChildren(); + socks5transport().addChild("candidate-error"); + } + + public Element socks5transport() { Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1"); if (transport==null) { transport = this.addChild("transport", "urn:xmpp:jingle:transports:s5b:1"); + transport.setAttribute("sid", this.transportId); } - transport.setAttribute("sid", transportId); - transport.clearChildren(); - transport.addChild("candidate-error"); + return transport; + } + + public Element ibbTransport() { + Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:ibb:1"); + if (transport==null) { + transport = this.addChild("transport", "urn:xmpp:jingle:transports:ibb:1"); + transport.setAttribute("sid", this.transportId); + } + return transport; + } + + public boolean hasSocks5Transport() { + return this.hasChild("transport", "urn:xmpp:jingle:transports:s5b:1"); + } + + public boolean hasIbbTransport() { + return this.hasChild("transport","urn:xmpp:jingle:transports:ibb:1"); } } diff --git a/src/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java b/src/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java index fdb53b36..55700609 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java +++ b/src/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java @@ -92,22 +92,4 @@ public class JinglePacket extends IqPacket { public boolean isAction(String action) { return action.equalsIgnoreCase(this.getAction()); } - - public String toPrettyString() { - StringBuilder output = new StringBuilder(); - output.append("["+getAction()+ " to:"+getTo()); - if (this.content!=null) { - if (this.content.getUsedCandidate()!=null) { - output.append(" [used-candidate="+this.content.getUsedCandidate()+"]"); - } else if (this.content.hasCandidateError()) { - output.append(" [candidate-error]"); - } else { - for(Element c : this.content.getCanditates()) { - output.append(" ["+c.getAttribute("host")+":"+c.getAttribute("port")+"]"); - } - } - } - output.append("]"); - return output.toString(); - } }