From 1d79a677c88f6ac2c6d17a5ba7214a8846957659 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 29 Jun 2016 17:16:34 +0200 Subject: support jingle ft:4 to be compatible with swift Conversations and Gajim both have an implementation bug that sends the jingle session id instead of the transport id (compare XEP-260 2.2). This commit has a work around for this that remains buggy when using ft:3. If gajim is ever to fix this we will be incompatbile. gajim should implement ft:4 instead. (gajim to gajim is broken as well) --- .../conversations/generator/AbstractGenerator.java | 4 +- .../xmpp/jingle/JingleConnection.java | 55 ++++++++++++++--- .../xmpp/jingle/JingleSocks5Transport.java | 8 ++- .../conversations/xmpp/jingle/stanzas/Content.java | 70 +++++++++++++++------- 4 files changed, 105 insertions(+), 32 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index ff9a20c6..43ffe724 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -16,11 +16,13 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.PhoneHelper; +import eu.siacs.conversations.xmpp.jingle.stanzas.Content; public abstract class AbstractGenerator { private final String[] FEATURES = { "urn:xmpp:jingle:1", - "urn:xmpp:jingle:apps:file-transfer:3", + Content.Version.FT_3.getNamespace(), + Content.Version.FT_4.getNamespace(), "urn:xmpp:jingle:transports:s5b:1", "urn:xmpp:jingle:transports:ibb:1", "http://jabber.org/protocol/muc", diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index 97615a97..95c998d0 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -21,6 +21,7 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.entities.Presence; import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.TransferablePlaceholder; import eu.siacs.conversations.persistance.FileBackend; @@ -45,6 +46,8 @@ public class JingleConnection implements Transferable { protected static final int JINGLE_STATUS_TRANSMITTING = 5; protected static final int JINGLE_STATUS_FAILED = 99; + private Content.Version ftVersion = Content.Version.FT_3; + private int ibbBlockSize = 8192; private int mJingleStatus = -1; @@ -238,12 +241,14 @@ public class JingleConnection implements Transferable { this.contentCreator = "initiator"; this.contentName = this.mJingleConnectionManager.nextRandomId(); this.message = message; + this.account = message.getConversation().getAccount(); + upgradeNamespace(); this.message.setTransferable(this); this.mStatus = Transferable.STATUS_UPLOADING; - this.account = message.getConversation().getAccount(); this.initiator = this.account.getJid(); this.responder = this.message.getCounterpart(); this.sessionId = this.mJingleConnectionManager.nextRandomId(); + this.transportId = this.mJingleConnectionManager.nextRandomId(); if (this.candidates.size() > 0) { this.sendInitRequest(); } else { @@ -287,6 +292,20 @@ public class JingleConnection implements Transferable { } + private void upgradeNamespace() { + Jid jid = this.message.getCounterpart(); + String resource = jid != null ?jid.getResourcepart() : null; + if (resource != null) { + Presence presence = this.account.getRoster().getContact(jid).getPresences().getPresences().get(resource); + if (presence != null) { + List features = presence.getServiceDiscoveryResult().getFeatures(); + if (features.contains(Content.Version.FT_4.getNamespace())) { + this.ftVersion = Content.Version.FT_4; + } + } + } + } + public void init(Account account, JinglePacket packet) { this.mJingleStatus = JINGLE_STATUS_INITIATED; Conversation conversation = this.mXmppConnectionService @@ -307,7 +326,13 @@ public class JingleConnection implements Transferable { this.contentName = content.getAttribute("name"); this.transportId = content.getTransportId(); this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren())); - this.fileOffer = packet.getJingleContent().getFileOffer(); + this.ftVersion = content.getVersion(); + if (ftVersion == null) { + this.sendCancel(); + this.fail(); + return; + } + this.fileOffer = content.getFileOffer(this.ftVersion); mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null); @@ -431,24 +456,23 @@ public class JingleConnection implements Transferable { this.file.setKeyAndIv(conversation.getSymmetricKey()); pair = AbstractConnectionManager.createInputStream(this.file, false); this.file.setExpectedSize(pair.second); - content.setFileOffer(this.file, true); + content.setFileOffer(this.file, true, this.ftVersion); } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { this.file.setKey(mXmppAxolotlMessage.getInnerKey()); this.file.setIv(mXmppAxolotlMessage.getIV()); pair = AbstractConnectionManager.createInputStream(this.file, true); this.file.setExpectedSize(pair.second); - content.setFileOffer(this.file, false).addChild(mXmppAxolotlMessage.toElement()); + content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement()); } else { pair = AbstractConnectionManager.createInputStream(this.file, false); this.file.setExpectedSize(pair.second); - content.setFileOffer(this.file, false); + content.setFileOffer(this.file, false, this.ftVersion); } } catch (FileNotFoundException e) { cancel(); return; } this.mFileInputStream = pair.first; - this.transportId = this.mJingleConnectionManager.nextRandomId(); content.setTransportId(this.transportId); content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); @@ -488,7 +512,7 @@ public class JingleConnection implements Transferable { public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) { final JinglePacket packet = bootstrapPacket("session-accept"); final Content content = new Content(contentCreator,contentName); - content.setFileOffer(fileOffer); + content.setFileOffer(fileOffer, ftVersion); content.setTransportId(transportId); if (success && candidate != null && !equalCandidateExists(candidate)) { final JingleSocks5Transport socksConnection = new JingleSocks5Transport( @@ -626,13 +650,20 @@ public class JingleConnection implements Transferable { this.mJingleStatus = JINGLE_STATUS_TRANSMITTING; if (connection.needsActivation()) { if (connection.getCandidate().isOurs()) { + final String sid; + if (ftVersion == Content.Version.FT_3) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": use session ID instead of transport ID to activate proxy"); + sid = getSessionId(); + } else { + sid = getTransportId(); + } Log.d(Config.LOGTAG, "candidate " + connection.getCandidate().getCid() + " was our proxy. going to activate"); IqPacket activation = new IqPacket(IqPacket.TYPE.SET); activation.setTo(connection.getCandidate().getJid()); activation.query("http://jabber.org/protocol/bytestreams") - .setAttribute("sid", this.getSessionId()); + .setAttribute("sid", sid); activation.query().addChild("activate") .setContent(this.getCounterPart().toString()); mXmppConnectionService.sendIqPacket(account,activation, @@ -975,6 +1006,14 @@ public class JingleConnection implements Transferable { mXmppConnectionService.updateConversationUi(); } + public String getTransportId() { + return this.transportId; + } + + public Content.Version getFtVersion() { + return this.ftVersion; + } + interface OnProxyActivated { public void success(); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index 87fa4097..b46f345e 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -22,6 +22,7 @@ import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.SocksSocketFactory; +import eu.siacs.conversations.xmpp.jingle.stanzas.Content; public class JingleSocks5Transport extends JingleTransport { private JingleCandidate candidate; @@ -40,7 +41,12 @@ public class JingleSocks5Transport extends JingleTransport { try { MessageDigest mDigest = MessageDigest.getInstance("SHA-1"); StringBuilder destBuilder = new StringBuilder(); - destBuilder.append(jingleConnection.getSessionId()); + if (jingleConnection.getFtVersion() == Content.Version.FT_3) { + Log.d(Config.LOGTAG,this.connection.getAccount().getJid().toBareJid()+": using session Id instead of transport Id for proxy destination"); + destBuilder.append(jingleConnection.getSessionId()); + } else { + destBuilder.append(jingleConnection.getTransportId()); + } if (candidate.isOurs()) { destBuilder.append(jingleConnection.getAccount().getJid()); destBuilder.append(jingleConnection.getCounterPart()); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java index f752cc5d..49f52bf3 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java @@ -5,12 +5,23 @@ import eu.siacs.conversations.xml.Element; public class Content extends Element { - private String transportId; + public enum Version { + FT_3("urn:xmpp:jingle:apps:file-transfer:3"), + FT_4("urn:xmpp:jingle:apps:file-transfer:4"); + + private final String namespace; + + Version(String namespace) { + this.namespace = namespace; + } - private Content(String name) { - super(name); + public String getNamespace() { + return namespace; + } } + private String transportId; + public Content() { super("content"); } @@ -21,15 +32,28 @@ public class Content extends Element { this.setAttribute("name", name); } + public Version getVersion() { + if (hasChild("description", Version.FT_3.namespace)) { + return Version.FT_3; + } else if (hasChild("description" , Version.FT_4.namespace)) { + return Version.FT_4; + } + return null; + } + public void setTransportId(String sid) { this.transportId = sid; } - public Element setFileOffer(DownloadableFile actualFile, boolean otr) { - Element description = this.addChild("description", - "urn:xmpp:jingle:apps:file-transfer:3"); - Element offer = description.addChild("offer"); - Element file = offer.addChild("file"); + public Element setFileOffer(DownloadableFile actualFile, boolean otr, Version version) { + Element description = this.addChild("description", version.namespace); + Element file; + if (version == Version.FT_3) { + Element offer = description.addChild("offer"); + file = offer.addChild("file"); + } else { + file = description.addChild("file"); + } file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize())); if (otr) { file.addChild("name").setContent(actualFile.getName() + ".otr"); @@ -39,27 +63,29 @@ public class Content extends Element { return file; } - public Element getFileOffer() { - Element description = this.findChild("description", - "urn:xmpp:jingle:apps:file-transfer:3"); + public Element getFileOffer(Version version) { + Element description = this.findChild("description", version.namespace); if (description == null) { return null; } - Element offer = description.findChild("offer"); - if (offer == null) { - return null; + if (version == Version.FT_3) { + Element offer = description.findChild("offer"); + if (offer == null) { + return null; + } + return offer.findChild("file"); + } else { + return description.findChild("file"); } - return offer.findChild("file"); } - public void setFileOffer(Element fileOffer) { - Element description = this.findChild("description", - "urn:xmpp:jingle:apps:file-transfer:3"); - if (description == null) { - description = this.addChild("description", - "urn:xmpp:jingle:apps:file-transfer:3"); + public void setFileOffer(Element fileOffer, Version version) { + Element description = this.addChild("description", version.namespace); + if (version == Version.FT_3) { + description.addChild("offer").addChild(fileOffer); + } else { + description.addChild(fileOffer); } - description.addChild(fileOffer); } public String getTransportId() { -- cgit v1.2.3