create 'Description' object
This commit is contained in:
parent
7538e387ec
commit
eb22bd0499
|
@ -20,14 +20,14 @@ import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.utils.PhoneHelper;
|
import eu.siacs.conversations.utils.PhoneHelper;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
import eu.siacs.conversations.xmpp.XmppConnection;
|
import eu.siacs.conversations.xmpp.XmppConnection;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.FileTransferDescription;
|
||||||
|
|
||||||
public abstract class AbstractGenerator {
|
public abstract class AbstractGenerator {
|
||||||
private final String[] FEATURES = {
|
private final String[] FEATURES = {
|
||||||
"urn:xmpp:jingle:1",
|
Namespace.JINGLE,
|
||||||
Content.Version.FT_3.getNamespace(),
|
FileTransferDescription.Version.FT_3.getNamespace(),
|
||||||
Content.Version.FT_4.getNamespace(),
|
FileTransferDescription.Version.FT_4.getNamespace(),
|
||||||
Content.Version.FT_5.getNamespace(),
|
FileTransferDescription.Version.FT_5.getNamespace(),
|
||||||
Namespace.JINGLE_TRANSPORTS_S5B,
|
Namespace.JINGLE_TRANSPORTS_S5B,
|
||||||
Namespace.JINGLE_TRANSPORTS_IBB,
|
Namespace.JINGLE_TRANSPORTS_IBB,
|
||||||
Namespace.JINGLE_ENCRYPTED_TRANSPORT,
|
Namespace.JINGLE_ENCRYPTED_TRANSPORT,
|
||||||
|
|
|
@ -18,6 +18,8 @@ import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
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.FileTransferDescription;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||||
import rocks.xmpp.addr.Jid;
|
import rocks.xmpp.addr.Jid;
|
||||||
|
@ -34,9 +36,17 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
public void deliverPacket(final Account account, final JinglePacket packet) {
|
public void deliverPacket(final Account account, final JinglePacket packet) {
|
||||||
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, packet);
|
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, packet);
|
||||||
if (packet.getAction() == JinglePacket.Action.SESSION_INITIATE) { //TODO check that id doesn't exist yet
|
if (packet.getAction() == JinglePacket.Action.SESSION_INITIATE) { //TODO check that id doesn't exist yet
|
||||||
JingleFileTransferConnection connection = new JingleFileTransferConnection(this, id);
|
final Content content = packet.getJingleContent();
|
||||||
connection.init(account, packet);
|
final String descriptionNamespace = content == null ? null : content.getDescriptionNamespace();
|
||||||
|
final AbstractJingleConnection connection;
|
||||||
|
if (FileTransferDescription.NAMESPACES.contains(descriptionNamespace)) {
|
||||||
|
connection = new JingleFileTransferConnection(this, id);
|
||||||
|
} else {
|
||||||
|
//TODO return feature-not-implemented
|
||||||
|
return;
|
||||||
|
}
|
||||||
connections.put(id, connection);
|
connections.put(id, connection);
|
||||||
|
connection.deliverPacket(packet);
|
||||||
} else {
|
} else {
|
||||||
final AbstractJingleConnection abstractJingleConnection = connections.get(id);
|
final AbstractJingleConnection abstractJingleConnection = connections.get(id);
|
||||||
if (abstractJingleConnection != null) {
|
if (abstractJingleConnection != null) {
|
||||||
|
|
|
@ -3,6 +3,8 @@ package eu.siacs.conversations.xmpp.jingle;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -36,6 +38,7 @@ import eu.siacs.conversations.xml.Element;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
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.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.stanzas.IqPacket;
|
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||||
|
@ -50,7 +53,6 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
private static final int JINGLE_STATUS_FINISHED = 4;
|
private static final int JINGLE_STATUS_FINISHED = 4;
|
||||||
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 Content.Version ftVersion = Content.Version.FT_3;
|
|
||||||
|
|
||||||
private int ibbBlockSize = 8192;
|
private int ibbBlockSize = 8192;
|
||||||
|
|
||||||
|
@ -63,7 +65,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
private ConcurrentHashMap<String, JingleSocks5Transport> connections = new ConcurrentHashMap<>();
|
private ConcurrentHashMap<String, JingleSocks5Transport> connections = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private String transportId;
|
private String transportId;
|
||||||
private Element fileOffer;
|
private FileTransferDescription description;
|
||||||
private DownloadableFile file = null;
|
private DownloadableFile file = null;
|
||||||
|
|
||||||
private boolean proxyActivationFailed = false;
|
private boolean proxyActivationFailed = false;
|
||||||
|
@ -130,7 +132,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
id.account.getPgpDecryptionService().decrypt(message, true);
|
id.account.getPgpDecryptionService().decrypt(message, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ftVersion == Content.Version.FT_5) { //older Conversations will break when receiving a session-info
|
if (description.getVersion() == FileTransferDescription.Version.FT_5) { //older Conversations will break when receiving a session-info
|
||||||
sendHash();
|
sendHash();
|
||||||
}
|
}
|
||||||
if (message.getEncryption() == Message.ENCRYPTION_PGP) {
|
if (message.getEncryption() == Message.ENCRYPTION_PGP) {
|
||||||
|
@ -236,7 +238,9 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
void deliverPacket(final JinglePacket packet) {
|
void deliverPacket(final JinglePacket packet) {
|
||||||
final JinglePacket.Action action = packet.getAction();
|
final JinglePacket.Action action = packet.getAction();
|
||||||
//TODO switch case
|
//TODO switch case
|
||||||
if (action == JinglePacket.Action.SESSION_TERMINATE) {
|
if (action == JinglePacket.Action.SESSION_INITIATE) {
|
||||||
|
init(packet);
|
||||||
|
} else if (action == JinglePacket.Action.SESSION_TERMINATE) {
|
||||||
Reason reason = packet.getReason();
|
Reason reason = packet.getReason();
|
||||||
if (reason != null) {
|
if (reason != null) {
|
||||||
if (reason.hasChild("cancel")) {
|
if (reason.hasChild("cancel")) {
|
||||||
|
@ -307,6 +311,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(final Message message) {
|
public void init(final Message message) {
|
||||||
|
Preconditions.checkArgument(message.isFileOrImage());
|
||||||
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
|
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
|
||||||
Conversation conversation = (Conversation) message.getConversation();
|
Conversation conversation = (Conversation) message.getConversation();
|
||||||
conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, xmppAxolotlMessage -> {
|
conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, xmppAxolotlMessage -> {
|
||||||
|
@ -321,13 +326,13 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(Message message, XmppAxolotlMessage xmppAxolotlMessage) {
|
private void init(final Message message, final XmppAxolotlMessage xmppAxolotlMessage) {
|
||||||
this.mXmppAxolotlMessage = xmppAxolotlMessage;
|
this.mXmppAxolotlMessage = xmppAxolotlMessage;
|
||||||
this.contentCreator = Content.Creator.INITIATOR;
|
this.contentCreator = Content.Creator.INITIATOR;
|
||||||
this.contentName = JingleConnectionManager.nextRandomId();
|
this.contentName = JingleConnectionManager.nextRandomId();
|
||||||
this.message = message;
|
this.message = message;
|
||||||
final List<String> remoteFeatures = getRemoteFeatures();
|
final List<String> remoteFeatures = getRemoteFeatures();
|
||||||
upgradeNamespace(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) ? Transport.SOCKS : Transport.IBB;
|
||||||
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);
|
||||||
|
@ -335,6 +340,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
this.initiator = this.id.account.getJid();
|
this.initiator = this.id.account.getJid();
|
||||||
this.responder = this.id.counterPart;
|
this.responder = this.id.counterPart;
|
||||||
this.transportId = JingleConnectionManager.nextRandomId();
|
this.transportId = JingleConnectionManager.nextRandomId();
|
||||||
|
this.setupDescription(remoteVersion);
|
||||||
if (this.initialTransport == Transport.IBB) {
|
if (this.initialTransport == Transport.IBB) {
|
||||||
this.sendInitRequest();
|
this.sendInitRequest();
|
||||||
} else {
|
} else {
|
||||||
|
@ -386,11 +392,13 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void upgradeNamespace(List<String> remoteFeatures) {
|
private FileTransferDescription.Version getAvailableFileTransferVersion(List<String> remoteFeatures) {
|
||||||
if (remoteFeatures.contains(Content.Version.FT_5.getNamespace())) {
|
if (remoteFeatures.contains(FileTransferDescription.Version.FT_5.getNamespace())) {
|
||||||
this.ftVersion = Content.Version.FT_5;
|
return FileTransferDescription.Version.FT_5;
|
||||||
} else if (remoteFeatures.contains(Content.Version.FT_4.getNamespace())) {
|
} else if (remoteFeatures.contains(FileTransferDescription.Version.FT_4.getNamespace())) {
|
||||||
this.ftVersion = Content.Version.FT_4;
|
return FileTransferDescription.Version.FT_4;
|
||||||
|
} else {
|
||||||
|
return FileTransferDescription.Version.FT_3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,11 +414,9 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(Account account, JinglePacket packet) { //should move to deliverPacket
|
private void init(JinglePacket packet) { //should move to deliverPacket
|
||||||
this.mJingleStatus = JINGLE_STATUS_INITIATED;
|
this.mJingleStatus = JINGLE_STATUS_INITIATED;
|
||||||
Conversation conversation = this.xmppConnectionService
|
final Conversation conversation = this.xmppConnectionService.findOrCreateConversation(id.account, id.counterPart.asBareJid(), false, false);
|
||||||
.findOrCreateConversation(account,
|
|
||||||
packet.getFrom().asBareJid(), false, false);
|
|
||||||
this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
|
this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
|
||||||
this.message.setStatus(Message.STATUS_RECEIVED);
|
this.message.setStatus(Message.STATUS_RECEIVED);
|
||||||
this.mStatus = Transferable.STATUS_OFFER;
|
this.mStatus = Transferable.STATUS_OFFER;
|
||||||
|
@ -445,14 +451,10 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.ftVersion = content.getVersion();
|
|
||||||
if (ftVersion == null) {
|
|
||||||
respondToIq(packet, false);
|
|
||||||
this.fail();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.fileOffer = content.getFileOffer(this.ftVersion);
|
|
||||||
|
|
||||||
|
this.description = (FileTransferDescription) content.getDescription();
|
||||||
|
|
||||||
|
final Element fileOffer = this.description.getFileOffer();
|
||||||
|
|
||||||
if (fileOffer != null) {
|
if (fileOffer != null) {
|
||||||
boolean remoteIsUsingJet = false;
|
boolean remoteIsUsingJet = false;
|
||||||
|
@ -536,71 +538,76 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupDescription(final FileTransferDescription.Version version) {
|
||||||
|
this.file = this.xmppConnectionService.getFileBackend().getFile(message, false);
|
||||||
|
final FileTransferDescription description;
|
||||||
|
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
|
||||||
|
this.file.setKey(mXmppAxolotlMessage.getInnerKey());
|
||||||
|
this.file.setIv(mXmppAxolotlMessage.getIV());
|
||||||
|
//legacy OMEMO encrypted file transfer reported file size of the encrypted file
|
||||||
|
//JET uses the file size of the plain text file. The difference is only 16 bytes (auth tag)
|
||||||
|
this.file.setExpectedSize(file.getSize() + (this.remoteSupportsOmemoJet ? 0 : 16));
|
||||||
|
if (remoteSupportsOmemoJet) {
|
||||||
|
description = FileTransferDescription.of(this.file, version, null);
|
||||||
|
} else {
|
||||||
|
description = FileTransferDescription.of(this.file, version, this.mXmppAxolotlMessage);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.file.setExpectedSize(file.getSize());
|
||||||
|
description = FileTransferDescription.of(this.file, version, null);
|
||||||
|
}
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
if (message.isFileOrImage()) {
|
content.setTransportId(this.transportId);
|
||||||
content.setTransportId(this.transportId);
|
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL && remoteSupportsOmemoJet) {
|
||||||
this.file = this.xmppConnectionService.getFileBackend().getFile(message, false);
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": remote announced support for JET");
|
||||||
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
|
final Element security = new Element("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT);
|
||||||
this.file.setKey(mXmppAxolotlMessage.getInnerKey());
|
security.setAttribute("name", this.contentName);
|
||||||
this.file.setIv(mXmppAxolotlMessage.getIV());
|
security.setAttribute("cipher", JET_OMEMO_CIPHER);
|
||||||
//legacy OMEMO encrypted file transfer reported file size of the encrypted file
|
security.setAttribute("type", AxolotlService.PEP_PREFIX);
|
||||||
//JET uses the file size of the plain text file. The difference is only 16 bytes (auth tag)
|
security.addChild(mXmppAxolotlMessage.toElement());
|
||||||
this.file.setExpectedSize(file.getSize() + (this.remoteSupportsOmemoJet ? 0 : 16));
|
content.addChild(security);
|
||||||
final Element file = content.setFileOffer(this.file, false, this.ftVersion);
|
|
||||||
if (remoteSupportsOmemoJet) {
|
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": remote announced support for JET");
|
|
||||||
final Element security = new Element("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT);
|
|
||||||
security.setAttribute("name", this.contentName);
|
|
||||||
security.setAttribute("cipher", JET_OMEMO_CIPHER);
|
|
||||||
security.setAttribute("type", AxolotlService.PEP_PREFIX);
|
|
||||||
security.addChild(mXmppAxolotlMessage.toElement());
|
|
||||||
content.addChild(security);
|
|
||||||
} else {
|
|
||||||
file.addChild(mXmppAxolotlMessage.toElement());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.file.setExpectedSize(file.getSize());
|
|
||||||
content.setFileOffer(this.file, false, this.ftVersion);
|
|
||||||
}
|
|
||||||
message.resetFileParams();
|
|
||||||
try {
|
|
||||||
this.mFileInputStream = new FileInputStream(file);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
content.setTransportId(this.transportId);
|
|
||||||
if (this.initialTransport == Transport.IBB) {
|
|
||||||
content.ibbTransport().setAttribute("block-size", Integer.toString(this.ibbBlockSize));
|
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": sending IBB offer");
|
|
||||||
} else {
|
|
||||||
final List<Element> candidates = getCandidatesAsElements();
|
|
||||||
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);
|
|
||||||
this.sendJinglePacket(packet, (account, response) -> {
|
|
||||||
if (response.getType() == IqPacket.TYPE.RESULT) {
|
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": other party received offer");
|
|
||||||
if (mJingleStatus == JINGLE_STATUS_OFFERED) {
|
|
||||||
mJingleStatus = JINGLE_STATUS_INITIATED;
|
|
||||||
xmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
|
|
||||||
} else {
|
|
||||||
Log.d(Config.LOGTAG, "received ack for offer when status was " + mJingleStatus);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fail(IqParser.extractErrorMessage(response));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
content.setDescription(this.description);
|
||||||
|
message.resetFileParams();
|
||||||
|
try {
|
||||||
|
this.mFileInputStream = new FileInputStream(file);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
content.setTransportId(this.transportId);
|
||||||
|
if (this.initialTransport == Transport.IBB) {
|
||||||
|
content.ibbTransport().setAttribute("block-size", Integer.toString(this.ibbBlockSize));
|
||||||
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": sending IBB offer");
|
||||||
|
} else {
|
||||||
|
final List<Element> candidates = getCandidatesAsElements();
|
||||||
|
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);
|
||||||
|
this.sendJinglePacket(packet, (account, response) -> {
|
||||||
|
if (response.getType() == IqPacket.TYPE.RESULT) {
|
||||||
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": other party received offer");
|
||||||
|
if (mJingleStatus == JINGLE_STATUS_OFFERED) {
|
||||||
|
mJingleStatus = JINGLE_STATUS_INITIATED;
|
||||||
|
xmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG, "received ack for offer when status was " + mJingleStatus);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fail(IqParser.extractErrorMessage(response));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendHash() {
|
private void sendHash() {
|
||||||
|
final Element checksum = new Element("checksum", description.getVersion().getNamespace());
|
||||||
final Element checksum = new Element("checksum", ftVersion.getNamespace());
|
|
||||||
checksum.setAttribute("creator", "initiator");
|
checksum.setAttribute("creator", "initiator");
|
||||||
checksum.setAttribute("name", "a-file-offer");
|
checksum.setAttribute("name", "a-file-offer");
|
||||||
Element hash = checksum.addChild("file").addChild("hash", "urn:xmpp:hashes:2");
|
Element hash = checksum.addChild("file").addChild("hash", "urn:xmpp:hashes:2");
|
||||||
|
@ -637,7 +644,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
this.jingleConnectionManager.getPrimaryCandidate(this.id.account, initiating(), (success, candidate) -> {
|
this.jingleConnectionManager.getPrimaryCandidate(this.id.account, initiating(), (success, candidate) -> {
|
||||||
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.setFileOffer(fileOffer, ftVersion);
|
content.setDescription(this.description);
|
||||||
content.setTransportId(transportId);
|
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);
|
||||||
|
@ -677,7 +684,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
this.transport = new JingleInBandTransport(this, this.transportId, this.ibbBlockSize);
|
this.transport = new JingleInBandTransport(this, this.transportId, this.ibbBlockSize);
|
||||||
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.setFileOffer(fileOffer, ftVersion);
|
content.setDescription(this.description);
|
||||||
content.setTransportId(transportId);
|
content.setTransportId(transportId);
|
||||||
content.ibbTransport().setAttribute("block-size", this.ibbBlockSize);
|
content.ibbTransport().setAttribute("block-size", this.ibbBlockSize);
|
||||||
packet.setJingleContent(content);
|
packet.setJingleContent(content);
|
||||||
|
@ -812,7 +819,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
if (connection.needsActivation()) {
|
if (connection.needsActivation()) {
|
||||||
if (connection.getCandidate().isOurs()) {
|
if (connection.getCandidate().isOurs()) {
|
||||||
final String sid;
|
final String sid;
|
||||||
if (ftVersion == Content.Version.FT_3) {
|
if (description.getVersion() == FileTransferDescription.Version.FT_3) {
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": use session ID instead of transport ID to activate proxy");
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": use session ID instead of transport ID to activate proxy");
|
||||||
sid = id.sessionId;
|
sid = id.sessionId;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1220,12 +1227,8 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
||||||
return this.transportId;
|
return this.transportId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Content.Version getFtVersion() {
|
public FileTransferDescription.Version getFtVersion() {
|
||||||
return this.ftVersion;
|
return this.description.getVersion();
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasTransportId(String sid) {
|
|
||||||
return sid.equals(this.transportId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleTransport getTransport() {
|
public JingleTransport getTransport() {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
import eu.siacs.conversations.utils.SocksSocketFactory;
|
import eu.siacs.conversations.utils.SocksSocketFactory;
|
||||||
import eu.siacs.conversations.utils.WakeLockHelper;
|
import eu.siacs.conversations.utils.WakeLockHelper;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
||||||
|
import eu.siacs.conversations.xmpp.jingle.stanzas.FileTransferDescription;
|
||||||
|
|
||||||
public class JingleSocks5Transport extends JingleTransport {
|
public class JingleSocks5Transport extends JingleTransport {
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
this.connection = jingleConnection;
|
this.connection = jingleConnection;
|
||||||
this.account = jingleConnection.getId().account;
|
this.account = jingleConnection.getId().account;
|
||||||
final StringBuilder destBuilder = new StringBuilder();
|
final StringBuilder destBuilder = new StringBuilder();
|
||||||
if (this.connection.getFtVersion() == Content.Version.FT_3) {
|
if (this.connection.getFtVersion() == FileTransferDescription.Version.FT_3) {
|
||||||
Log.d(Config.LOGTAG, this.account.getJid().asBareJid() + ": using session Id instead of transport Id for proxy destination");
|
Log.d(Config.LOGTAG, this.account.getJid().asBareJid() + ": using session Id instead of transport Id for proxy destination");
|
||||||
destBuilder.append(this.connection.getId().sessionId);
|
destBuilder.append(this.connection.getId().sessionId);
|
||||||
} else {
|
} else {
|
||||||
|
@ -132,7 +133,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
responseHeader = new byte[]{0x05, 0x00, 0x00, 0x03};
|
responseHeader = new byte[]{0x05, 0x00, 0x00, 0x03};
|
||||||
success = true;
|
success = true;
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG,this.account.getJid().asBareJid()+": destination mismatch. received "+receivedDestination+" (expected "+this.destination+")");
|
Log.d(Config.LOGTAG, this.account.getJid().asBareJid() + ": destination mismatch. received " + receivedDestination + " (expected " + this.destination + ")");
|
||||||
responseHeader = new byte[]{0x05, 0x04, 0x00, 0x03};
|
responseHeader = new byte[]{0x05, 0x04, 0x00, 0x03};
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
@ -143,7 +144,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
outputStream.write(response.array());
|
outputStream.write(response.array());
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
if (success) {
|
if (success) {
|
||||||
Log.d(Config.LOGTAG,this.account.getJid().asBareJid()+": successfully processed connection to candidate "+candidate.getHost()+":"+candidate.getPort());
|
Log.d(Config.LOGTAG, this.account.getJid().asBareJid() + ": successfully processed connection to candidate " + candidate.getHost() + ":" + candidate.getPort());
|
||||||
socket.setSoTimeout(0);
|
socket.setSoTimeout(0);
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.inputStream = inputStream;
|
this.inputStream = inputStream;
|
||||||
|
@ -216,7 +217,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
final Account account = this.account;
|
final Account account = this.account;
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid()+": failed sending file after "+transmitted+"/"+file.getExpectedSize()+" ("+ socket.getInetAddress()+":"+socket.getPort()+")", e);
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": failed sending file after " + transmitted + "/" + file.getExpectedSize() + " (" + socket.getInetAddress() + ":" + socket.getPort() + ")", e);
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
} finally {
|
} finally {
|
||||||
FileBackend.close(fileInputStream);
|
FileBackend.close(fileInputStream);
|
||||||
|
|
|
@ -39,58 +39,35 @@ public class Content extends Element {
|
||||||
return Creator.of(getAttribute("creator"));
|
return Creator.of(getAttribute("creator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Version getVersion() {
|
public Senders getSenders() {
|
||||||
if (hasChild("description", Version.FT_3.namespace)) {
|
return Senders.of(getAttribute("senders"));
|
||||||
return Version.FT_3;
|
|
||||||
} else if (hasChild("description", Version.FT_4.namespace)) {
|
|
||||||
return Version.FT_4;
|
|
||||||
} else if (hasChild("description", Version.FT_5.namespace)) {
|
|
||||||
return Version.FT_5;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Element setFileOffer(DownloadableFile actualFile, boolean otr, Version version) {
|
public void setSenders(Senders senders) {
|
||||||
Element description = this.addChild("description", version.namespace);
|
this.setAttribute("senders", senders.toString());
|
||||||
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");
|
|
||||||
} else {
|
|
||||||
file.addChild("name").setContent(actualFile.getName());
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Element getFileOffer(Version version) {
|
public GenericDescription getDescription() {
|
||||||
Element description = this.findChild("description", version.namespace);
|
final Element description = this.findChild("description");
|
||||||
if (description == null) {
|
if (description == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (version == Version.FT_3) {
|
final String xmlns = description.getNamespace();
|
||||||
Element offer = description.findChild("offer");
|
if (FileTransferDescription.NAMESPACES.contains(xmlns)) {
|
||||||
if (offer == null) {
|
return FileTransferDescription.upgrade(description);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return offer.findChild("file");
|
|
||||||
} else {
|
} else {
|
||||||
return description.findChild("file");
|
return GenericDescription.upgrade(description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFileOffer(Element fileOffer, Version version) {
|
public void setDescription(final GenericDescription description) {
|
||||||
Element description = this.addChild("description", version.namespace);
|
Preconditions.checkNotNull(description);
|
||||||
if (version == Version.FT_3) {
|
this.addChild(description);
|
||||||
description.addChild("offer").addChild(fileOffer);
|
}
|
||||||
} else {
|
|
||||||
description.addChild(fileOffer);
|
public String getDescriptionNamespace() {
|
||||||
}
|
final Element description = this.findChild("description");
|
||||||
|
return description == null ? null : description.getNamespace();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTransportId() {
|
public String getTransportId() {
|
||||||
|
@ -132,22 +109,6 @@ public class Content extends Element {
|
||||||
return this.hasChild("transport", Namespace.JINGLE_TRANSPORTS_IBB);
|
return this.hasChild("transport", Namespace.JINGLE_TRANSPORTS_IBB);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Version {
|
|
||||||
FT_3("urn:xmpp:jingle:apps:file-transfer:3"),
|
|
||||||
FT_4("urn:xmpp:jingle:apps:file-transfer:4"),
|
|
||||||
FT_5("urn:xmpp:jingle:apps:file-transfer:5");
|
|
||||||
|
|
||||||
private final String namespace;
|
|
||||||
|
|
||||||
Version(String namespace) {
|
|
||||||
this.namespace = namespace;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNamespace() {
|
|
||||||
return namespace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Creator {
|
public enum Creator {
|
||||||
INITIATOR, RESPONDER;
|
INITIATOR, RESPONDER;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
package eu.siacs.conversations.xmpp.jingle.stanzas;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
|
||||||
|
import eu.siacs.conversations.entities.DownloadableFile;
|
||||||
|
import eu.siacs.conversations.xml.Element;
|
||||||
|
|
||||||
|
public class FileTransferDescription extends GenericDescription {
|
||||||
|
|
||||||
|
public static List<String> NAMESPACES = Arrays.asList(
|
||||||
|
Version.FT_3.namespace,
|
||||||
|
Version.FT_4.namespace,
|
||||||
|
Version.FT_5.namespace
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
private FileTransferDescription(String name, String namespace) {
|
||||||
|
super(name, namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Version getVersion() {
|
||||||
|
final String namespace = getNamespace();
|
||||||
|
if (namespace.equals(Version.FT_3.namespace)) {
|
||||||
|
return Version.FT_3;
|
||||||
|
} else if (namespace.equals(Version.FT_4.namespace)) {
|
||||||
|
return Version.FT_4;
|
||||||
|
} else if (namespace.equals(Version.FT_5.namespace)) {
|
||||||
|
return Version.FT_5;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Unknown namespace");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Element getFileOffer() {
|
||||||
|
final Version version = getVersion();
|
||||||
|
if (version == Version.FT_3) {
|
||||||
|
final Element offer = this.findChild("offer");
|
||||||
|
return offer == null ? null : offer.findChild("file");
|
||||||
|
} else {
|
||||||
|
return this.findChild("file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileTransferDescription of(DownloadableFile file, Version version, XmppAxolotlMessage axolotlMessage) {
|
||||||
|
final FileTransferDescription description = new FileTransferDescription("description", version.getNamespace());
|
||||||
|
final Element fileElement;
|
||||||
|
if (version == Version.FT_3) {
|
||||||
|
Element offer = description.addChild("offer");
|
||||||
|
fileElement = offer.addChild("file");
|
||||||
|
} else {
|
||||||
|
fileElement = description.addChild("file");
|
||||||
|
}
|
||||||
|
fileElement.addChild("size").setContent(Long.toString(file.getExpectedSize()));
|
||||||
|
fileElement.addChild("name").setContent(file.getName());
|
||||||
|
if (axolotlMessage != null) {
|
||||||
|
fileElement.addChild(axolotlMessage.toElement());
|
||||||
|
}
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileTransferDescription upgrade(final Element element) {
|
||||||
|
Preconditions.checkArgument("description".equals(element.getName()), "Name of provided element is not description");
|
||||||
|
Preconditions.checkArgument(NAMESPACES.contains(element.getNamespace()), "Element does not match a file transfer namespace");
|
||||||
|
final FileTransferDescription description = new FileTransferDescription("description", element.getNamespace());
|
||||||
|
description.setAttributes(element.getAttributes());
|
||||||
|
description.setChildren(element.getChildren());
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Version {
|
||||||
|
FT_3("urn:xmpp:jingle:apps:file-transfer:3"),
|
||||||
|
FT_4("urn:xmpp:jingle:apps:file-transfer:4"),
|
||||||
|
FT_5("urn:xmpp:jingle:apps:file-transfer:5");
|
||||||
|
|
||||||
|
private final String namespace;
|
||||||
|
|
||||||
|
Version(String namespace) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 GenericDescription extends Element {
|
||||||
|
|
||||||
|
protected GenericDescription(String name, final String namespace) {
|
||||||
|
super(name, namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GenericDescription upgrade(final Element element) {
|
||||||
|
Preconditions.checkArgument("description".equals(element.getName()));
|
||||||
|
final GenericDescription description = new GenericDescription("description", element.getNamespace());
|
||||||
|
description.setAttributes(element.getAttributes());
|
||||||
|
description.setChildren(element.getChildren());
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue