refactor jingle code to use objects for TransportInfo

This commit is contained in:
Daniel Gultsch 2020-04-02 10:59:25 +02:00
parent eb22bd0499
commit 963ddd11c2
10 changed files with 263 additions and 164 deletions

View file

@ -29,6 +29,7 @@ public final class Namespace {
public static final String AVATAR_CONVERSION = "urn:xmpp:pep-vcard-conversion:0"; public static final String AVATAR_CONVERSION = "urn:xmpp:pep-vcard-conversion:0";
public static final String JINGLE_TRANSPORTS_S5B = "urn:xmpp:jingle:transports:s5b:1"; public static final String JINGLE_TRANSPORTS_S5B = "urn:xmpp:jingle:transports:s5b:1";
public static final String JINGLE_TRANSPORTS_IBB = "urn:xmpp:jingle:transports:ibb:1"; public static final String JINGLE_TRANSPORTS_IBB = "urn:xmpp:jingle:transports:ibb:1";
public static final String JINGLE_TRANSPORT_ICE_UDP = "urn:xmpp:jingle:transports:ice-udp:1";
public static final String IBB = "http://jabber.org/protocol/ibb"; public static final String IBB = "http://jabber.org/protocol/ibb";
public static final String PING = "urn:xmpp:ping"; public static final String PING = "urn:xmpp:ping";
public static final String PUSH = "urn:xmpp:push:0"; public static final String PUSH = "urn:xmpp:push:0";

View file

@ -101,22 +101,24 @@ public class JingleCandidate {
return this.type; return this.type;
} }
public static List<JingleCandidate> parse(List<Element> canditates) { public static List<JingleCandidate> parse(final List<Element> elements) {
List<JingleCandidate> parsedCandidates = new ArrayList<>(); final List<JingleCandidate> candidates = new ArrayList<>();
for (Element c : canditates) { for (final Element element : elements) {
parsedCandidates.add(JingleCandidate.parse(c)); if ("candidate".equals(element.getName())) {
candidates.add(JingleCandidate.parse(element));
} }
return parsedCandidates; }
return candidates;
} }
public static JingleCandidate parse(Element candidate) { public static JingleCandidate parse(Element element) {
JingleCandidate parsedCandidate = new JingleCandidate(candidate.getAttribute("cid"), false); final JingleCandidate candidate = new JingleCandidate(element.getAttribute("cid"), false);
parsedCandidate.setHost(candidate.getAttribute("host")); candidate.setHost(element.getAttribute("host"));
parsedCandidate.setJid(InvalidJid.getNullForInvalid(candidate.getAttributeAsJid("jid"))); candidate.setJid(InvalidJid.getNullForInvalid(element.getAttributeAsJid("jid")));
parsedCandidate.setType(candidate.getAttribute("type")); candidate.setType(element.getAttribute("type"));
parsedCandidate.setPriority(Integer.parseInt(candidate.getAttribute("priority"))); candidate.setPriority(Integer.parseInt(element.getAttribute("priority")));
parsedCandidate.setPort(Integer.parseInt(candidate.getAttribute("port"))); candidate.setPort(Integer.parseInt(element.getAttribute("port")));
return parsedCandidate; return candidate;
} }
public Element toElement() { public Element toElement() {

View file

@ -4,6 +4,11 @@ import android.util.Base64;
import android.util.Log; import android.util.Log;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -13,6 +18,7 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -39,8 +45,11 @@ import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.jingle.stanzas.Content; import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
import eu.siacs.conversations.xmpp.jingle.stanzas.FileTransferDescription; import eu.siacs.conversations.xmpp.jingle.stanzas.FileTransferDescription;
import eu.siacs.conversations.xmpp.jingle.stanzas.GenericTransportInfo;
import eu.siacs.conversations.xmpp.jingle.stanzas.IbbTransportInfo;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
import eu.siacs.conversations.xmpp.jingle.stanzas.S5BTransportInfo;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid; import rocks.xmpp.addr.Jid;
@ -54,7 +63,9 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
private static final int JINGLE_STATUS_FAILED = 99; private static final int JINGLE_STATUS_FAILED = 99;
private static final int JINGLE_STATUS_OFFERED = -1; private static final int JINGLE_STATUS_OFFERED = -1;
private int ibbBlockSize = 8192; private static final int MAX_IBB_BLOCK_SIZE = 8192;
private int ibbBlockSize = MAX_IBB_BLOCK_SIZE;
private int mJingleStatus = JINGLE_STATUS_OFFERED; //migrate to enum private int mJingleStatus = JINGLE_STATUS_OFFERED; //migrate to enum
private int mStatus = Transferable.STATUS_UNKNOWN; private int mStatus = Transferable.STATUS_UNKNOWN;
@ -72,7 +83,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
private String contentName; private String contentName;
private Content.Creator contentCreator; private Content.Creator contentCreator;
private Transport initialTransport; private Class<? extends GenericTransportInfo> initialTransport;
private boolean remoteSupportsOmemoJet; private boolean remoteSupportsOmemoJet;
private int mProgress = 0; private int mProgress = 0;
@ -276,8 +287,10 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
} else if (action == JinglePacket.Action.TRANSPORT_INFO) { } else if (action == JinglePacket.Action.TRANSPORT_INFO) {
receiveTransportInfo(packet); receiveTransportInfo(packet);
} else if (action == JinglePacket.Action.TRANSPORT_REPLACE) { } else if (action == JinglePacket.Action.TRANSPORT_REPLACE) {
if (packet.getJingleContent().hasIbbTransport()) { final Content content = packet.getJingleContent();
receiveFallbackToIbb(packet); final GenericTransportInfo transportInfo = content == null ? null : content.getTransport();
if (transportInfo instanceof IbbTransportInfo) {
receiveFallbackToIbb(packet, (IbbTransportInfo) transportInfo);
} else { } else {
Log.d(Config.LOGTAG, "trying to fallback to something unknown" + packet.toString()); Log.d(Config.LOGTAG, "trying to fallback to something unknown" + packet.toString());
respondToIq(packet, false); respondToIq(packet, false);
@ -333,7 +346,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
this.message = message; this.message = message;
final List<String> remoteFeatures = getRemoteFeatures(); final List<String> remoteFeatures = getRemoteFeatures();
final FileTransferDescription.Version remoteVersion = getAvailableFileTransferVersion(remoteFeatures); final FileTransferDescription.Version remoteVersion = getAvailableFileTransferVersion(remoteFeatures);
this.initialTransport = remoteFeatures.contains(Namespace.JINGLE_TRANSPORTS_S5B) ? Transport.SOCKS : Transport.IBB; this.initialTransport = remoteFeatures.contains(Namespace.JINGLE_TRANSPORTS_S5B) ? S5BTransportInfo.class : IbbTransportInfo.class;
this.remoteSupportsOmemoJet = remoteFeatures.contains(Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO); this.remoteSupportsOmemoJet = remoteFeatures.contains(Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO);
this.message.setTransferable(this); this.message.setTransferable(this);
this.mStatus = Transferable.STATUS_UPLOADING; this.mStatus = Transferable.STATUS_UPLOADING;
@ -341,7 +354,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
this.responder = this.id.counterPart; this.responder = this.id.counterPart;
this.transportId = JingleConnectionManager.nextRandomId(); this.transportId = JingleConnectionManager.nextRandomId();
this.setupDescription(remoteVersion); this.setupDescription(remoteVersion);
if (this.initialTransport == Transport.IBB) { if (this.initialTransport == IbbTransportInfo.class) {
this.sendInitRequest(); this.sendInitRequest();
} else { } else {
gatherAndConnectDirectCandidates(); gatherAndConnectDirectCandidates();
@ -425,32 +438,32 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
this.initiator = this.id.counterPart; this.initiator = this.id.counterPart;
this.responder = this.id.account.getJid(); this.responder = this.id.account.getJid();
final Content content = packet.getJingleContent(); final Content content = packet.getJingleContent();
final GenericTransportInfo transportInfo = content.getTransport();
this.contentCreator = content.getCreator(); this.contentCreator = content.getCreator();
this.initialTransport = content.hasSocks5Transport() ? Transport.SOCKS : Transport.IBB;
this.contentName = content.getAttribute("name"); this.contentName = content.getAttribute("name");
this.transportId = content.getTransportId();
if (transportInfo instanceof S5BTransportInfo) {
if (this.initialTransport == Transport.SOCKS) { final S5BTransportInfo s5BTransportInfo = (S5BTransportInfo) transportInfo;
this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren())); this.transportId = s5BTransportInfo.getTransportId();
} else if (this.initialTransport == Transport.IBB) { this.initialTransport = s5BTransportInfo.getClass();
final String receivedBlockSize = content.ibbTransport().getAttribute("block-size"); this.mergeCandidates(s5BTransportInfo.getCandidates());
if (receivedBlockSize != null) { } else if (transportInfo instanceof IbbTransportInfo) {
try { final IbbTransportInfo ibbTransportInfo = (IbbTransportInfo) transportInfo;
this.ibbBlockSize = Math.min(Integer.parseInt(receivedBlockSize), this.ibbBlockSize); this.initialTransport = ibbTransportInfo.getClass();
} catch (NumberFormatException e) { this.transportId = ibbTransportInfo.getTransportId();
Log.d(Config.LOGTAG, "number format exception " + e.getMessage()); final int remoteBlockSize = ibbTransportInfo.getBlockSize();
if (remoteBlockSize <= 0) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": remote party requested invalid ibb block size");
respondToIq(packet, false); respondToIq(packet, false);
this.fail(); this.fail();
return;
} }
this.ibbBlockSize = Math.min(MAX_IBB_BLOCK_SIZE, ibbTransportInfo.getBlockSize());
} else { } else {
Log.d(Config.LOGTAG, "received block size was null"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": remote tried to use unknown transport " + transportInfo.getNamespace());
respondToIq(packet, false); respondToIq(packet, false);
this.fail(); this.fail();
return; return;
} }
}
this.description = (FileTransferDescription) content.getDescription(); this.description = (FileTransferDescription) content.getDescription();
@ -562,7 +575,6 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
private void sendInitRequest() { private void sendInitRequest() {
final JinglePacket packet = this.bootstrapPacket(JinglePacket.Action.SESSION_INITIATE); final JinglePacket packet = this.bootstrapPacket(JinglePacket.Action.SESSION_INITIATE);
final Content content = new Content(this.contentCreator, this.contentName); final Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId);
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL && remoteSupportsOmemoJet) { if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL && remoteSupportsOmemoJet) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": remote announced support for JET"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": remote announced support for JET");
final Element security = new Element("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT); final Element security = new Element("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT);
@ -580,14 +592,13 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
fail(e.getMessage()); fail(e.getMessage());
return; return;
} }
content.setTransportId(this.transportId); if (this.initialTransport == IbbTransportInfo.class) {
if (this.initialTransport == Transport.IBB) { content.setTransport(new IbbTransportInfo(this.transportId, this.ibbBlockSize));
content.ibbTransport().setAttribute("block-size", Integer.toString(this.ibbBlockSize));
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": sending IBB offer"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": sending IBB offer");
} else { } else {
final List<Element> candidates = getCandidatesAsElements(); final Collection<JingleCandidate> candidates = getOurCandidates();
content.setTransport(new S5BTransportInfo(this.transportId, candidates));
Log.d(Config.LOGTAG, String.format("%s: sending S5B offer with %d candidates", id.account.getJid().asBareJid(), candidates.size())); Log.d(Config.LOGTAG, String.format("%s: sending S5B offer with %d candidates", id.account.getJid().asBareJid(), candidates.size()));
content.socks5transport().setChildren(candidates);
} }
packet.setJingleContent(content); packet.setJingleContent(content);
this.sendJinglePacket(packet, (account, response) -> { this.sendJinglePacket(packet, (account, response) -> {
@ -618,21 +629,15 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
this.sendJinglePacket(packet); this.sendJinglePacket(packet);
} }
private List<Element> getCandidatesAsElements() { public Collection<JingleCandidate> getOurCandidates() {
List<Element> elements = new ArrayList<>(); return Collections2.filter(this.candidates, c -> c != null && c.isOurs());
for (JingleCandidate c : this.candidates) {
if (c.isOurs()) {
elements.add(c.toElement());
}
}
return elements;
} }
private void sendAccept() { private void sendAccept() {
mJingleStatus = JINGLE_STATUS_ACCEPTED; mJingleStatus = JINGLE_STATUS_ACCEPTED;
this.mStatus = Transferable.STATUS_DOWNLOADING; this.mStatus = Transferable.STATUS_DOWNLOADING;
this.jingleConnectionManager.updateConversationUi(true); this.jingleConnectionManager.updateConversationUi(true);
if (initialTransport == Transport.SOCKS) { if (initialTransport == S5BTransportInfo.class) {
sendAcceptSocks(); sendAcceptSocks();
} else { } else {
sendAcceptIbb(); sendAcceptIbb();
@ -645,7 +650,6 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
final JinglePacket packet = bootstrapPacket(JinglePacket.Action.SESSION_ACCEPT); final JinglePacket packet = bootstrapPacket(JinglePacket.Action.SESSION_ACCEPT);
final Content content = new Content(contentCreator, contentName); final Content content = new Content(contentCreator, contentName);
content.setDescription(this.description); content.setDescription(this.description);
content.setTransportId(transportId);
if (success && candidate != null && !equalCandidateExists(candidate)) { if (success && candidate != null && !equalCandidateExists(candidate)) {
final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate); final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate);
connections.put(candidate.getCid(), socksConnection); connections.put(candidate.getCid(), socksConnection);
@ -654,7 +658,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
@Override @Override
public void failed() { public void failed() {
Log.d(Config.LOGTAG, "connection to our own proxy65 candidate failed"); Log.d(Config.LOGTAG, "connection to our own proxy65 candidate failed");
content.socks5transport().setChildren(getCandidatesAsElements()); content.setTransport(new S5BTransportInfo(transportId, getOurCandidates()));
packet.setJingleContent(content); packet.setJingleContent(content);
sendJinglePacket(packet); sendJinglePacket(packet);
connectNextCandidate(); connectNextCandidate();
@ -664,7 +668,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
public void established() { public void established() {
Log.d(Config.LOGTAG, "connected to proxy65 candidate"); Log.d(Config.LOGTAG, "connected to proxy65 candidate");
mergeCandidate(candidate); mergeCandidate(candidate);
content.socks5transport().setChildren(getCandidatesAsElements()); content.setTransport(new S5BTransportInfo(transportId, getOurCandidates()));
packet.setJingleContent(content); packet.setJingleContent(content);
sendJinglePacket(packet); sendJinglePacket(packet);
connectNextCandidate(); connectNextCandidate();
@ -672,7 +676,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
}); });
} else { } else {
Log.d(Config.LOGTAG, "did not find a proxy65 candidate for ourselves"); Log.d(Config.LOGTAG, "did not find a proxy65 candidate for ourselves");
content.socks5transport().setChildren(getCandidatesAsElements()); content.setTransport(new S5BTransportInfo(transportId, getOurCandidates()));
packet.setJingleContent(content); packet.setJingleContent(content);
sendJinglePacket(packet); sendJinglePacket(packet);
connectNextCandidate(); connectNextCandidate();
@ -685,8 +689,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
final JinglePacket packet = bootstrapPacket(JinglePacket.Action.SESSION_ACCEPT); final JinglePacket packet = bootstrapPacket(JinglePacket.Action.SESSION_ACCEPT);
final Content content = new Content(contentCreator, contentName); final Content content = new Content(contentCreator, contentName);
content.setDescription(this.description); content.setDescription(this.description);
content.setTransportId(transportId); content.setTransport(new IbbTransportInfo(this.transportId, this.ibbBlockSize));
content.ibbTransport().setAttribute("block-size", this.ibbBlockSize);
packet.setJingleContent(content); packet.setJingleContent(content);
this.transport.receive(file, onFileTransmissionStatusChanged); this.transport.receive(file, onFileTransmissionStatusChanged);
this.sendJinglePacket(packet); this.sendJinglePacket(packet);
@ -719,22 +722,21 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
} }
this.mJingleStatus = JINGLE_STATUS_ACCEPTED; this.mJingleStatus = JINGLE_STATUS_ACCEPTED;
xmppConnectionService.markMessage(message, Message.STATUS_UNSEND); xmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
Content content = packet.getJingleContent(); final Content content = packet.getJingleContent();
if (content.hasSocks5Transport()) { final GenericTransportInfo transportInfo = content.getTransport();
//TODO we want to fail if transportInfo doesnt match our intialTransport and/or our id
if (transportInfo instanceof S5BTransportInfo) {
final S5BTransportInfo s5BTransportInfo = (S5BTransportInfo) transportInfo;
respondToIq(packet, true); respondToIq(packet, true);
mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren())); //TODO calling merge is probably a bug because that might eliminate candidates of the other party and lead to us not sending accept/deny
//TODO: we probably just want to call add
mergeCandidates(s5BTransportInfo.getCandidates());
this.connectNextCandidate(); this.connectNextCandidate();
} else if (content.hasIbbTransport()) { } else if (transportInfo instanceof IbbTransportInfo) {
String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size"); final IbbTransportInfo ibbTransportInfo = (IbbTransportInfo) transportInfo;
if (receivedBlockSize != null) { final int remoteBlockSize = ibbTransportInfo.getBlockSize();
try { if (remoteBlockSize > 0) {
int bs = Integer.parseInt(receivedBlockSize); this.ibbBlockSize = Math.min(ibbBlockSize, remoteBlockSize);
if (bs > this.ibbBlockSize) {
this.ibbBlockSize = bs;
}
} catch (Exception e) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to parse block size in session-accept");
}
} }
respondToIq(packet, true); respondToIq(packet, true);
this.transport = new JingleInBandTransport(this, this.transportId, this.ibbBlockSize); this.transport = new JingleInBandTransport(this, this.transportId, this.ibbBlockSize);
@ -746,13 +748,15 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
private void receiveTransportInfo(JinglePacket packet) { private void receiveTransportInfo(JinglePacket packet) {
final Content content = packet.getJingleContent(); final Content content = packet.getJingleContent();
if (content.hasSocks5Transport()) { final GenericTransportInfo transportInfo = content.getTransport();
if (content.socks5transport().hasChild("activated")) { if (transportInfo instanceof S5BTransportInfo) {
final S5BTransportInfo s5BTransportInfo = (S5BTransportInfo) transportInfo;
if (s5BTransportInfo.hasChild("activated")) {
respondToIq(packet, true); respondToIq(packet, true);
if ((this.transport != null) && (this.transport instanceof JingleSocks5Transport)) { if ((this.transport != null) && (this.transport instanceof JingleSocks5Transport)) {
onProxyActivated.success(); onProxyActivated.success();
} else { } else {
String cid = content.socks5transport().findChild("activated").getAttribute("cid"); String cid = s5BTransportInfo.findChild("activated").getAttribute("cid");
Log.d(Config.LOGTAG, "received proxy activated (" + cid Log.d(Config.LOGTAG, "received proxy activated (" + cid
+ ")prior to choosing our own transport"); + ")prior to choosing our own transport");
JingleSocks5Transport connection = this.connections.get(cid); JingleSocks5Transport connection = this.connections.get(cid);
@ -764,18 +768,18 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
this.fail(); this.fail();
} }
} }
} else if (content.socks5transport().hasChild("proxy-error")) { } else if (s5BTransportInfo.hasChild("proxy-error")) {
respondToIq(packet, true); respondToIq(packet, true);
onProxyActivated.failed(); onProxyActivated.failed();
} else if (content.socks5transport().hasChild("candidate-error")) { } else if (s5BTransportInfo.hasChild("candidate-error")) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received candidate error"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received candidate error");
respondToIq(packet, true); respondToIq(packet, true);
this.receivedCandidate = true; this.receivedCandidate = true;
if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) { if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) {
this.connect(); this.connect();
} }
} else if (content.socks5transport().hasChild("candidate-used")) { } else if (s5BTransportInfo.hasChild("candidate-used")) {
String cid = content.socks5transport().findChild("candidate-used").getAttribute("cid"); String cid = s5BTransportInfo.findChild("candidate-used").getAttribute("cid");
if (cid != null) { if (cid != null) {
Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid); Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid);
JingleCandidate candidate = getCandidate(cid); JingleCandidate candidate = getCandidate(cid);
@ -912,15 +916,13 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
JinglePacket packet = this.bootstrapPacket(JinglePacket.Action.TRANSPORT_REPLACE); JinglePacket packet = this.bootstrapPacket(JinglePacket.Action.TRANSPORT_REPLACE);
Content content = new Content(this.contentCreator, this.contentName); Content content = new Content(this.contentCreator, this.contentName);
this.transportId = JingleConnectionManager.nextRandomId(); this.transportId = JingleConnectionManager.nextRandomId();
content.setTransportId(this.transportId); content.setTransport(new IbbTransportInfo(this.transportId, this.ibbBlockSize));
content.ibbTransport().setAttribute("block-size",
Integer.toString(this.ibbBlockSize));
packet.setJingleContent(content); packet.setJingleContent(content);
this.sendJinglePacket(packet); this.sendJinglePacket(packet);
} }
private void receiveFallbackToIbb(JinglePacket packet) { private void receiveFallbackToIbb(final JinglePacket packet, final IbbTransportInfo transportInfo) {
if (initiating()) { if (initiating()) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received out of order transport-replace (we were initiating)"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received out of order transport-replace (we were initiating)");
respondToIqWithOutOfOrder(packet); respondToIqWithOutOfOrder(packet);
@ -934,25 +936,19 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
} }
this.proxyActivationFailed = false; //fallback received; now we no longer need to accept another one; this.proxyActivationFailed = false; //fallback received; now we no longer need to accept another one;
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": receiving fallback to ibb"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": receiving fallback to ibb");
final String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size"); final int remoteBlockSize = transportInfo.getBlockSize();
if (receivedBlockSize != null) { if (remoteBlockSize > 0) {
try { this.ibbBlockSize = Math.min(MAX_IBB_BLOCK_SIZE, remoteBlockSize);
final int bs = Integer.parseInt(receivedBlockSize); } else {
if (bs < this.ibbBlockSize) {
this.ibbBlockSize = bs;
}
} catch (NumberFormatException e) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to parse block size in transport-replace"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to parse block size in transport-replace");
} }
} this.transportId = transportInfo.getTransportId(); //TODO: handle the case where this is null by the remote party
this.transportId = packet.getJingleContent().getTransportId();
this.transport = new JingleInBandTransport(this, this.transportId, this.ibbBlockSize); this.transport = new JingleInBandTransport(this, this.transportId, this.ibbBlockSize);
final JinglePacket answer = bootstrapPacket(JinglePacket.Action.TRANSPORT_ACCEPT); final JinglePacket answer = bootstrapPacket(JinglePacket.Action.TRANSPORT_ACCEPT);
final Content content = new Content(contentCreator, contentName); final Content content = new Content(contentCreator, contentName);
content.ibbTransport().setAttribute("block-size", this.ibbBlockSize); content.setTransport(new IbbTransportInfo(this.transportId, this.ibbBlockSize));
content.ibbTransport().setAttribute("sid", this.transportId);
answer.setJingleContent(content); answer.setJingleContent(content);
respondToIq(packet, true); respondToIq(packet, true);
@ -983,20 +979,15 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
return; return;
} }
this.proxyActivationFailed = false; //fallback accepted; now we no longer need to accept another one; this.proxyActivationFailed = false; //fallback accepted; now we no longer need to accept another one;
if (packet.getJingleContent().hasIbbTransport()) { final Content content = packet.getJingleContent();
final Element ibbTransport = packet.getJingleContent().ibbTransport(); final GenericTransportInfo transportInfo = content == null ? null : content.getTransport();
final String receivedBlockSize = ibbTransport.getAttribute("block-size"); if (transportInfo instanceof IbbTransportInfo) {
final String sid = ibbTransport.getAttribute("sid"); final IbbTransportInfo ibbTransportInfo = (IbbTransportInfo) transportInfo;
if (receivedBlockSize != null) { final int remoteBlockSize = ibbTransportInfo.getBlockSize();
try { if (remoteBlockSize > 0) {
int bs = Integer.parseInt(receivedBlockSize); this.ibbBlockSize = Math.min(MAX_IBB_BLOCK_SIZE, remoteBlockSize);
if (bs < this.ibbBlockSize) {
this.ibbBlockSize = bs;
}
} catch (NumberFormatException e) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to parse block size in transport-accept");
}
} }
final String sid = ibbTransportInfo.getTransportId();
this.transport = new JingleInBandTransport(this, this.transportId, this.ibbBlockSize); this.transport = new JingleInBandTransport(this, this.transportId, this.ibbBlockSize);
if (sid == null || !sid.equals(this.transportId)) { if (sid == null || !sid.equals(this.transportId)) {
@ -1138,8 +1129,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
private void sendProxyActivated(String cid) { private void sendProxyActivated(String cid) {
final JinglePacket packet = bootstrapPacket(JinglePacket.Action.TRANSPORT_INFO); final JinglePacket packet = bootstrapPacket(JinglePacket.Action.TRANSPORT_INFO);
final Content content = new Content(this.contentCreator, this.contentName); final Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId); content.setTransport(new S5BTransportInfo(this.transportId, new Element("activated").setAttribute("cid", cid)));
content.socks5transport().addChild("activated").setAttribute("cid", cid);
packet.setJingleContent(content); packet.setJingleContent(content);
this.sendJinglePacket(packet); this.sendJinglePacket(packet);
} }
@ -1147,17 +1137,15 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
private void sendProxyError() { private void sendProxyError() {
final JinglePacket packet = bootstrapPacket(JinglePacket.Action.TRANSPORT_INFO); final JinglePacket packet = bootstrapPacket(JinglePacket.Action.TRANSPORT_INFO);
final Content content = new Content(this.contentCreator, this.contentName); final Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId); content.setTransport(new S5BTransportInfo(this.transportId, new Element("proxy-error")));
content.socks5transport().addChild("proxy-error");
packet.setJingleContent(content); packet.setJingleContent(content);
this.sendJinglePacket(packet); this.sendJinglePacket(packet);
} }
private void sendCandidateUsed(final String cid) { private void sendCandidateUsed(final String cid) {
JinglePacket packet = bootstrapPacket(JinglePacket.Action.TRANSPORT_INFO); JinglePacket packet = bootstrapPacket(JinglePacket.Action.TRANSPORT_INFO);
Content content = new Content(this.contentCreator, this.contentName); final Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId); content.setTransport(new S5BTransportInfo(this.transportId, new Element("candidate-used").setAttribute("cid", cid)));
content.socks5transport().addChild("candidate-used").setAttribute("cid", cid);
packet.setJingleContent(content); packet.setJingleContent(content);
this.sentCandidate = true; this.sentCandidate = true;
if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) { if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
@ -1170,8 +1158,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": sending candidate error"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": sending candidate error");
JinglePacket packet = bootstrapPacket(JinglePacket.Action.TRANSPORT_INFO); JinglePacket packet = bootstrapPacket(JinglePacket.Action.TRANSPORT_INFO);
Content content = new Content(this.contentCreator, this.contentName); Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId); content.setTransport(new S5BTransportInfo(this.transportId, new Element("candidate-error")));
content.socks5transport().addChild("candidate-error");
packet.setJingleContent(content); packet.setJingleContent(content);
this.sentCandidate = true; this.sentCandidate = true;
this.sendJinglePacket(packet); this.sendJinglePacket(packet);

View file

@ -1,5 +0,0 @@
package eu.siacs.conversations.xmpp.jingle;
public enum Transport {
SOCKS, IBB
}

View file

@ -11,11 +11,6 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Namespace;
public class Content extends Element { public class Content extends Element {
private String transportId;
//refactor to getDescription and getTransport
//return either FileTransferDescription or GenericDescription or RtpDescription (all extend Description interface)
public Content(final Creator creator, final String name) { public Content(final Creator creator, final String name) {
super("content", Namespace.JINGLE); super("content", Namespace.JINGLE);
@ -70,43 +65,24 @@ public class Content extends Element {
return description == null ? null : description.getNamespace(); return description == null ? null : description.getNamespace();
} }
public String getTransportId() { public GenericTransportInfo getTransport() {
if (hasSocks5Transport()) { final Element transport = this.findChild("transport");
this.transportId = socks5transport().getAttribute("sid"); final String namespace = transport == null ? null : transport.getNamespace();
} else if (hasIbbTransport()) { if (Namespace.JINGLE_TRANSPORTS_IBB.equals(namespace)) {
this.transportId = ibbTransport().getAttribute("sid"); return IbbTransportInfo.upgrade(transport);
} else if (Namespace.JINGLE_TRANSPORTS_S5B.equals(namespace)) {
return S5BTransportInfo.upgrade(transport);
} else if (Namespace.JINGLE_TRANSPORT_ICE_UDP.equals(namespace)) {
return IceUdpTransportInfo.upgrade(transport);
} else if (transport != null) {
return GenericTransportInfo.upgrade(transport);
} else {
return null;
} }
return this.transportId;
} }
public void setTransportId(String sid) { public void setTransport(GenericTransportInfo transportInfo) {
this.transportId = sid; this.addChild(transportInfo);
}
public Element socks5transport() {
Element transport = this.findChild("transport", Namespace.JINGLE_TRANSPORTS_S5B);
if (transport == null) {
transport = this.addChild("transport", Namespace.JINGLE_TRANSPORTS_S5B);
transport.setAttribute("sid", this.transportId);
}
return transport;
}
public Element ibbTransport() {
Element transport = this.findChild("transport", Namespace.JINGLE_TRANSPORTS_IBB);
if (transport == null) {
transport = this.addChild("transport", Namespace.JINGLE_TRANSPORTS_IBB);
transport.setAttribute("sid", this.transportId);
}
return transport;
}
public boolean hasSocks5Transport() {
return this.hasChild("transport", Namespace.JINGLE_TRANSPORTS_S5B);
}
public boolean hasIbbTransport() {
return this.hasChild("transport", Namespace.JINGLE_TRANSPORTS_IBB);
} }
public enum Creator { public enum Creator {

View file

@ -6,7 +6,7 @@ import eu.siacs.conversations.xml.Element;
public class GenericDescription extends Element { public class GenericDescription extends Element {
protected GenericDescription(String name, final String namespace) { GenericDescription(String name, final String namespace) {
super(name, namespace); super(name, namespace);
} }

View file

@ -0,0 +1,20 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import com.google.common.base.Preconditions;
import eu.siacs.conversations.xml.Element;
public class GenericTransportInfo extends Element {
protected GenericTransportInfo(String name, String xmlns) {
super(name, xmlns);
}
public static GenericTransportInfo upgrade(final Element element) {
Preconditions.checkArgument("transport".equals(element.getName()));
final GenericTransportInfo transport = new GenericTransportInfo("transport", element.getNamespace());
transport.setAttributes(element.getAttributes());
transport.setChildren(element.getChildren());
return transport;
}
}

View file

@ -0,0 +1,46 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import com.google.common.base.Preconditions;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
public class IbbTransportInfo extends GenericTransportInfo {
private IbbTransportInfo(final String name, final String xmlns) {
super(name, xmlns);
}
public IbbTransportInfo(final String transportId, final int blockSize) {
super("transport", Namespace.JINGLE_TRANSPORTS_IBB);
Preconditions.checkNotNull(transportId, "Transport ID can not be null");
Preconditions.checkArgument(blockSize > 0, "Block size must be larger than 0");
this.setAttribute("block-size", blockSize);
this.setAttribute("sid", transportId);
}
public String getTransportId() {
return this.getAttribute("sid");
}
public int getBlockSize() {
final String blockSize = this.getAttribute("block-size");
if (blockSize == null) {
return 0;
}
try {
return Integer.parseInt(blockSize);
} catch (NumberFormatException e) {
return 0;
}
}
public static IbbTransportInfo upgrade(final Element element) {
Preconditions.checkArgument("transport".equals(element.getName()), "Name of provided element is not transport");
Preconditions.checkArgument(Namespace.JINGLE_TRANSPORTS_IBB.equals(element.getNamespace()), "Element does not match ibb transport namespace");
final IbbTransportInfo transportInfo = new IbbTransportInfo("transport", Namespace.JINGLE_TRANSPORTS_IBB);
transportInfo.setAttributes(element.getAttributes());
transportInfo.setChildren(element.getChildren());
return transportInfo;
}
}

View file

@ -0,0 +1,22 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import com.google.common.base.Preconditions;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
public class IceUdpTransportInfo extends GenericTransportInfo {
private IceUdpTransportInfo(final String name, final String xmlns) {
super(name, xmlns);
}
public static IceUdpTransportInfo upgrade(final Element element) {
Preconditions.checkArgument("transport".equals(element.getName()), "Name of provided element is not transport");
Preconditions.checkArgument(Namespace.JINGLE_TRANSPORT_ICE_UDP.equals(element.getNamespace()), "Element does not match ice-udp transport namespace");
final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo("transport", Namespace.JINGLE_TRANSPORT_ICE_UDP);
transportInfo.setAttributes(element.getAttributes());
transportInfo.setChildren(element.getChildren());
return transportInfo;
}
}

View file

@ -0,0 +1,50 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import com.google.common.base.Preconditions;
import java.util.Collection;
import java.util.List;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.jingle.JingleCandidate;
public class S5BTransportInfo extends GenericTransportInfo {
private S5BTransportInfo(final String name, final String xmlns) {
super(name, xmlns);
}
public String getTransportId() {
return this.getAttribute("sid");
}
public S5BTransportInfo(final String transportId, final Collection<JingleCandidate> candidates) {
super("transport", Namespace.JINGLE_TRANSPORTS_S5B);
Preconditions.checkNotNull(transportId,"transport id must not be null");
for(JingleCandidate candidate : candidates) {
this.addChild(candidate.toElement());
}
this.setAttribute("sid", transportId);
}
public S5BTransportInfo(final String transportId, final Element child) {
super("transport", Namespace.JINGLE_TRANSPORTS_S5B);
Preconditions.checkNotNull(transportId,"transport id must not be null");
this.addChild(child);
this.setAttribute("sid", transportId);
}
public List<JingleCandidate> getCandidates() {
return JingleCandidate.parse(this.getChildren());
}
public static S5BTransportInfo upgrade(final Element element) {
Preconditions.checkArgument("transport".equals(element.getName()), "Name of provided element is not transport");
Preconditions.checkArgument(Namespace.JINGLE_TRANSPORTS_S5B.equals(element.getNamespace()), "Element does not match s5b transport namespace");
final S5BTransportInfo transportInfo = new S5BTransportInfo("transport", Namespace.JINGLE_TRANSPORTS_S5B);
transportInfo.setAttributes(element.getAttributes());
transportInfo.setChildren(element.getChildren());
return transportInfo;
}
}