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)
This commit is contained in:
parent
b5caa8fa35
commit
1d79a677c8
|
@ -16,11 +16,13 @@ import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.utils.PhoneHelper;
|
import eu.siacs.conversations.utils.PhoneHelper;
|
||||||
|
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
||||||
|
|
||||||
public abstract class AbstractGenerator {
|
public abstract class AbstractGenerator {
|
||||||
private final String[] FEATURES = {
|
private final String[] FEATURES = {
|
||||||
"urn:xmpp:jingle:1",
|
"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:s5b:1",
|
||||||
"urn:xmpp:jingle:transports:ibb:1",
|
"urn:xmpp:jingle:transports:ibb:1",
|
||||||
"http://jabber.org/protocol/muc",
|
"http://jabber.org/protocol/muc",
|
||||||
|
|
|
@ -21,6 +21,7 @@ import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.entities.DownloadableFile;
|
import eu.siacs.conversations.entities.DownloadableFile;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
import eu.siacs.conversations.entities.Presence;
|
||||||
import eu.siacs.conversations.entities.Transferable;
|
import eu.siacs.conversations.entities.Transferable;
|
||||||
import eu.siacs.conversations.entities.TransferablePlaceholder;
|
import eu.siacs.conversations.entities.TransferablePlaceholder;
|
||||||
import eu.siacs.conversations.persistance.FileBackend;
|
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_TRANSMITTING = 5;
|
||||||
protected static final int JINGLE_STATUS_FAILED = 99;
|
protected static final int JINGLE_STATUS_FAILED = 99;
|
||||||
|
|
||||||
|
private Content.Version ftVersion = Content.Version.FT_3;
|
||||||
|
|
||||||
private int ibbBlockSize = 8192;
|
private int ibbBlockSize = 8192;
|
||||||
|
|
||||||
private int mJingleStatus = -1;
|
private int mJingleStatus = -1;
|
||||||
|
@ -238,12 +241,14 @@ public class JingleConnection implements Transferable {
|
||||||
this.contentCreator = "initiator";
|
this.contentCreator = "initiator";
|
||||||
this.contentName = this.mJingleConnectionManager.nextRandomId();
|
this.contentName = this.mJingleConnectionManager.nextRandomId();
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
this.account = message.getConversation().getAccount();
|
||||||
|
upgradeNamespace();
|
||||||
this.message.setTransferable(this);
|
this.message.setTransferable(this);
|
||||||
this.mStatus = Transferable.STATUS_UPLOADING;
|
this.mStatus = Transferable.STATUS_UPLOADING;
|
||||||
this.account = message.getConversation().getAccount();
|
|
||||||
this.initiator = this.account.getJid();
|
this.initiator = this.account.getJid();
|
||||||
this.responder = this.message.getCounterpart();
|
this.responder = this.message.getCounterpart();
|
||||||
this.sessionId = this.mJingleConnectionManager.nextRandomId();
|
this.sessionId = this.mJingleConnectionManager.nextRandomId();
|
||||||
|
this.transportId = this.mJingleConnectionManager.nextRandomId();
|
||||||
if (this.candidates.size() > 0) {
|
if (this.candidates.size() > 0) {
|
||||||
this.sendInitRequest();
|
this.sendInitRequest();
|
||||||
} else {
|
} 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<String> 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) {
|
public void init(Account account, JinglePacket packet) {
|
||||||
this.mJingleStatus = JINGLE_STATUS_INITIATED;
|
this.mJingleStatus = JINGLE_STATUS_INITIATED;
|
||||||
Conversation conversation = this.mXmppConnectionService
|
Conversation conversation = this.mXmppConnectionService
|
||||||
|
@ -307,7 +326,13 @@ public class JingleConnection implements Transferable {
|
||||||
this.contentName = content.getAttribute("name");
|
this.contentName = content.getAttribute("name");
|
||||||
this.transportId = content.getTransportId();
|
this.transportId = content.getTransportId();
|
||||||
this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
|
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);
|
mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null);
|
||||||
|
|
||||||
|
@ -431,24 +456,23 @@ public class JingleConnection implements Transferable {
|
||||||
this.file.setKeyAndIv(conversation.getSymmetricKey());
|
this.file.setKeyAndIv(conversation.getSymmetricKey());
|
||||||
pair = AbstractConnectionManager.createInputStream(this.file, false);
|
pair = AbstractConnectionManager.createInputStream(this.file, false);
|
||||||
this.file.setExpectedSize(pair.second);
|
this.file.setExpectedSize(pair.second);
|
||||||
content.setFileOffer(this.file, true);
|
content.setFileOffer(this.file, true, this.ftVersion);
|
||||||
} else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
|
} else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
|
||||||
this.file.setKey(mXmppAxolotlMessage.getInnerKey());
|
this.file.setKey(mXmppAxolotlMessage.getInnerKey());
|
||||||
this.file.setIv(mXmppAxolotlMessage.getIV());
|
this.file.setIv(mXmppAxolotlMessage.getIV());
|
||||||
pair = AbstractConnectionManager.createInputStream(this.file, true);
|
pair = AbstractConnectionManager.createInputStream(this.file, true);
|
||||||
this.file.setExpectedSize(pair.second);
|
this.file.setExpectedSize(pair.second);
|
||||||
content.setFileOffer(this.file, false).addChild(mXmppAxolotlMessage.toElement());
|
content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement());
|
||||||
} else {
|
} else {
|
||||||
pair = AbstractConnectionManager.createInputStream(this.file, false);
|
pair = AbstractConnectionManager.createInputStream(this.file, false);
|
||||||
this.file.setExpectedSize(pair.second);
|
this.file.setExpectedSize(pair.second);
|
||||||
content.setFileOffer(this.file, false);
|
content.setFileOffer(this.file, false, this.ftVersion);
|
||||||
}
|
}
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
cancel();
|
cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.mFileInputStream = pair.first;
|
this.mFileInputStream = pair.first;
|
||||||
this.transportId = this.mJingleConnectionManager.nextRandomId();
|
|
||||||
content.setTransportId(this.transportId);
|
content.setTransportId(this.transportId);
|
||||||
content.socks5transport().setChildren(getCandidatesAsElements());
|
content.socks5transport().setChildren(getCandidatesAsElements());
|
||||||
packet.setContent(content);
|
packet.setContent(content);
|
||||||
|
@ -488,7 +512,7 @@ public class JingleConnection implements Transferable {
|
||||||
public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) {
|
public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) {
|
||||||
final JinglePacket packet = bootstrapPacket("session-accept");
|
final JinglePacket packet = bootstrapPacket("session-accept");
|
||||||
final Content content = new Content(contentCreator,contentName);
|
final Content content = new Content(contentCreator,contentName);
|
||||||
content.setFileOffer(fileOffer);
|
content.setFileOffer(fileOffer, ftVersion);
|
||||||
content.setTransportId(transportId);
|
content.setTransportId(transportId);
|
||||||
if (success && candidate != null && !equalCandidateExists(candidate)) {
|
if (success && candidate != null && !equalCandidateExists(candidate)) {
|
||||||
final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
|
final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
|
||||||
|
@ -626,13 +650,20 @@ public class JingleConnection implements Transferable {
|
||||||
this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
|
this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
|
||||||
if (connection.needsActivation()) {
|
if (connection.needsActivation()) {
|
||||||
if (connection.getCandidate().isOurs()) {
|
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 "
|
Log.d(Config.LOGTAG, "candidate "
|
||||||
+ connection.getCandidate().getCid()
|
+ connection.getCandidate().getCid()
|
||||||
+ " was our proxy. going to activate");
|
+ " was our proxy. going to activate");
|
||||||
IqPacket activation = new IqPacket(IqPacket.TYPE.SET);
|
IqPacket activation = new IqPacket(IqPacket.TYPE.SET);
|
||||||
activation.setTo(connection.getCandidate().getJid());
|
activation.setTo(connection.getCandidate().getJid());
|
||||||
activation.query("http://jabber.org/protocol/bytestreams")
|
activation.query("http://jabber.org/protocol/bytestreams")
|
||||||
.setAttribute("sid", this.getSessionId());
|
.setAttribute("sid", sid);
|
||||||
activation.query().addChild("activate")
|
activation.query().addChild("activate")
|
||||||
.setContent(this.getCounterPart().toString());
|
.setContent(this.getCounterPart().toString());
|
||||||
mXmppConnectionService.sendIqPacket(account,activation,
|
mXmppConnectionService.sendIqPacket(account,activation,
|
||||||
|
@ -975,6 +1006,14 @@ public class JingleConnection implements Transferable {
|
||||||
mXmppConnectionService.updateConversationUi();
|
mXmppConnectionService.updateConversationUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getTransportId() {
|
||||||
|
return this.transportId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Content.Version getFtVersion() {
|
||||||
|
return this.ftVersion;
|
||||||
|
}
|
||||||
|
|
||||||
interface OnProxyActivated {
|
interface OnProxyActivated {
|
||||||
public void success();
|
public void success();
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import eu.siacs.conversations.entities.DownloadableFile;
|
||||||
import eu.siacs.conversations.persistance.FileBackend;
|
import eu.siacs.conversations.persistance.FileBackend;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
import eu.siacs.conversations.utils.SocksSocketFactory;
|
import eu.siacs.conversations.utils.SocksSocketFactory;
|
||||||
|
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
||||||
|
|
||||||
public class JingleSocks5Transport extends JingleTransport {
|
public class JingleSocks5Transport extends JingleTransport {
|
||||||
private JingleCandidate candidate;
|
private JingleCandidate candidate;
|
||||||
|
@ -40,7 +41,12 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
try {
|
try {
|
||||||
MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
|
MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
|
||||||
StringBuilder destBuilder = new StringBuilder();
|
StringBuilder destBuilder = new StringBuilder();
|
||||||
|
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());
|
destBuilder.append(jingleConnection.getSessionId());
|
||||||
|
} else {
|
||||||
|
destBuilder.append(jingleConnection.getTransportId());
|
||||||
|
}
|
||||||
if (candidate.isOurs()) {
|
if (candidate.isOurs()) {
|
||||||
destBuilder.append(jingleConnection.getAccount().getJid());
|
destBuilder.append(jingleConnection.getAccount().getJid());
|
||||||
destBuilder.append(jingleConnection.getCounterPart());
|
destBuilder.append(jingleConnection.getCounterPart());
|
||||||
|
|
|
@ -5,12 +5,23 @@ import eu.siacs.conversations.xml.Element;
|
||||||
|
|
||||||
public class Content extends 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 Content(String name) {
|
private final String namespace;
|
||||||
super(name);
|
|
||||||
|
Version(String namespace) {
|
||||||
|
this.namespace = namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String transportId;
|
||||||
|
|
||||||
public Content() {
|
public Content() {
|
||||||
super("content");
|
super("content");
|
||||||
}
|
}
|
||||||
|
@ -21,15 +32,28 @@ public class Content extends Element {
|
||||||
this.setAttribute("name", name);
|
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) {
|
public void setTransportId(String sid) {
|
||||||
this.transportId = sid;
|
this.transportId = sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Element setFileOffer(DownloadableFile actualFile, boolean otr) {
|
public Element setFileOffer(DownloadableFile actualFile, boolean otr, Version version) {
|
||||||
Element description = this.addChild("description",
|
Element description = this.addChild("description", version.namespace);
|
||||||
"urn:xmpp:jingle:apps:file-transfer:3");
|
Element file;
|
||||||
|
if (version == Version.FT_3) {
|
||||||
Element offer = description.addChild("offer");
|
Element offer = description.addChild("offer");
|
||||||
Element file = offer.addChild("file");
|
file = offer.addChild("file");
|
||||||
|
} else {
|
||||||
|
file = description.addChild("file");
|
||||||
|
}
|
||||||
file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize()));
|
file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize()));
|
||||||
if (otr) {
|
if (otr) {
|
||||||
file.addChild("name").setContent(actualFile.getName() + ".otr");
|
file.addChild("name").setContent(actualFile.getName() + ".otr");
|
||||||
|
@ -39,28 +63,30 @@ public class Content extends Element {
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Element getFileOffer() {
|
public Element getFileOffer(Version version) {
|
||||||
Element description = this.findChild("description",
|
Element description = this.findChild("description", version.namespace);
|
||||||
"urn:xmpp:jingle:apps:file-transfer:3");
|
|
||||||
if (description == null) {
|
if (description == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (version == Version.FT_3) {
|
||||||
Element offer = description.findChild("offer");
|
Element offer = description.findChild("offer");
|
||||||
if (offer == null) {
|
if (offer == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return offer.findChild("file");
|
return offer.findChild("file");
|
||||||
|
} else {
|
||||||
|
return description.findChild("file");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFileOffer(Element fileOffer) {
|
public void setFileOffer(Element fileOffer, Version version) {
|
||||||
Element description = this.findChild("description",
|
Element description = this.addChild("description", version.namespace);
|
||||||
"urn:xmpp:jingle:apps:file-transfer:3");
|
if (version == Version.FT_3) {
|
||||||
if (description == null) {
|
description.addChild("offer").addChild(fileOffer);
|
||||||
description = this.addChild("description",
|
} else {
|
||||||
"urn:xmpp:jingle:apps:file-transfer:3");
|
|
||||||
}
|
|
||||||
description.addChild(fileOffer);
|
description.addChild(fileOffer);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getTransportId() {
|
public String getTransportId() {
|
||||||
if (hasSocks5Transport()) {
|
if (hasSocks5Transport()) {
|
||||||
|
|
Loading…
Reference in a new issue