diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 63d6a1911..b16da4517 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1353,7 +1353,7 @@ public class XmppConnectionService extends Service { || message.getConversation().getMode() == Conversation.MODE_MULTI) { mHttpConnectionManager.createNewUploadConnection(message, delay); } else { - mJingleConnectionManager.createNewConnection(message); + mJingleConnectionManager.startJingleFileTransfer(message); } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 036e35633..9d9ebf819 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -115,7 +115,7 @@ import eu.siacs.conversations.utils.TimeframeUtils; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.chatstate.ChatState; -import eu.siacs.conversations.xmpp.jingle.JingleConnection; +import eu.siacs.conversations.xmpp.jingle.JingleFileTransferConnection; import rocks.xmpp.addr.Jid; import static eu.siacs.conversations.ui.XmppActivity.EXTRA_ACCOUNT; @@ -1051,7 +1051,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke final boolean deleted = m.isDeleted(); final boolean encrypted = m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED || m.getEncryption() == Message.ENCRYPTION_PGP; - final boolean receiving = m.getStatus() == Message.STATUS_RECEIVED && (t instanceof JingleConnection || t instanceof HttpDownloadConnection); + final boolean receiving = m.getStatus() == Message.STATUS_RECEIVED && (t instanceof JingleFileTransferConnection || t instanceof HttpDownloadConnection); activity.getMenuInflater().inflate(R.menu.message_context, menu); menu.setHeaderTitle(R.string.message_options); MenuItem openWith = menu.findItem(R.id.open_with); diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java index 921a2f580..3f365642f 100644 --- a/src/main/java/eu/siacs/conversations/xml/Namespace.java +++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java @@ -29,6 +29,7 @@ public final class Namespace { 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_IBB = "urn:xmpp:jingle:transports:ibb:1"; + public static final String IBB = "http://jabber.org/protocol/ibb"; public static final String PING = "urn:xmpp:ping"; public static final String PUSH = "urn:xmpp:push:0"; public static final String COMMANDS = "http://jabber.org/protocol/commands"; diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java new file mode 100644 index 000000000..19ad2822a --- /dev/null +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java @@ -0,0 +1,71 @@ +package eu.siacs.conversations.xmpp.jingle; + +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; + +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; +import rocks.xmpp.addr.Jid; + +public abstract class AbstractJingleConnection { + + protected final JingleConnectionManager jingleConnectionManager; + protected final XmppConnectionService xmppConnectionService; + protected final Id id; + + public AbstractJingleConnection(final JingleConnectionManager jingleConnectionManager, final Id id) { + this.jingleConnectionManager = jingleConnectionManager; + this.xmppConnectionService = jingleConnectionManager.getXmppConnectionService(); + this.id = id; + } + + abstract void deliverPacket(JinglePacket jinglePacket); + + public Id getId() { + return id; + } + + + public static class Id { + public final Account account; + public final Jid counterPart; + public final String sessionId; + + private Id(final Account account, final Jid counterPart, final String sessionId) { + Preconditions.checkNotNull(counterPart); + Preconditions.checkArgument(counterPart.isFullJid()); + this.account = account; + this.counterPart = counterPart; + this.sessionId = sessionId; + } + + public static Id of(Account account, JinglePacket jinglePacket) { + return new Id(account, jinglePacket.getFrom(), jinglePacket.getSessionId()); + } + + public static Id of(Message message) { + return new Id( + message.getConversation().getAccount(), + message.getCounterpart(), + JingleConnectionManager.nextRandomId() + ); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Id id = (Id) o; + return Objects.equal(account.getJid(), id.account.getJid()) && + Objects.equal(counterPart, id.counterPart) && + Objects.equal(sessionId, id.sessionId); + } + + @Override + public int hashCode() { + return Objects.hashCode(account.getJid(), counterPart, sessionId); + } + } +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 60d6ebfe2..8c2139bb0 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -1,13 +1,13 @@ package eu.siacs.conversations.xmpp.jingle; -import android.annotation.SuppressLint; import android.util.Log; -import java.math.BigInteger; -import java.security.SecureRandom; +import com.google.common.base.Preconditions; + import java.util.HashMap; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; @@ -15,74 +15,63 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.XmppConnectionService; -import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket; import rocks.xmpp.addr.Jid; public class JingleConnectionManager extends AbstractConnectionManager { - private List connections = new CopyOnWriteArrayList<>(); + private Map connections = new ConcurrentHashMap<>(); private HashMap primaryCandidates = new HashMap<>(); - @SuppressLint("TrulyRandom") - private SecureRandom random = new SecureRandom(); - public JingleConnectionManager(XmppConnectionService service) { super(service); } - public void deliverPacket(Account account, JinglePacket packet) { - if (packet.isAction("session-initiate")) { - JingleConnection connection = new JingleConnection(this); + public void deliverPacket(final Account account, final JinglePacket packet) { + final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, packet); + if (packet.isAction("session-initiate")) { //TODO check that id doesn't exist yet + JingleFileTransferConnection connection = new JingleFileTransferConnection(this, id); connection.init(account, packet); - connections.add(connection); + connections.put(id, connection); } else { - for (JingleConnection connection : connections) { - if (connection.getAccount() == account - && connection.getSessionId().equals( - packet.getSessionId()) - && connection.getCounterPart().equals(packet.getFrom())) { - connection.deliverPacket(packet); - return; - } + final AbstractJingleConnection abstractJingleConnection = connections.get(id); + if (abstractJingleConnection != null) { + abstractJingleConnection.deliverPacket(packet); + } else { + Log.d(Config.LOGTAG, "unable to route jingle packet: " + packet); + IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR); + Element error = response.addChild("error"); + error.setAttribute("type", "cancel"); + error.addChild("item-not-found", + "urn:ietf:params:xml:ns:xmpp-stanzas"); + error.addChild("unknown-session", "urn:xmpp:jingle:errors:1"); + account.getXmppConnection().sendIqPacket(response, null); } - Log.d(Config.LOGTAG, "unable to route jingle packet: " + packet); - IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR); - Element error = response.addChild("error"); - error.setAttribute("type", "cancel"); - error.addChild("item-not-found", - "urn:ietf:params:xml:ns:xmpp-stanzas"); - error.addChild("unknown-session", "urn:xmpp:jingle:errors:1"); - account.getXmppConnection().sendIqPacket(response, null); } } - public JingleConnection createNewConnection(Message message) { - Transferable old = message.getTransferable(); + public void startJingleFileTransfer(final Message message) { + Preconditions.checkArgument(message.isFileOrImage(), "Message is not of type file or image"); + final Transferable old = message.getTransferable(); if (old != null) { old.cancel(); } - JingleConnection connection = new JingleConnection(this); + final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(message); + final JingleFileTransferConnection connection = new JingleFileTransferConnection(this, id); mXmppConnectionService.markMessage(message, Message.STATUS_WAITING); connection.init(message); - this.connections.add(connection); - return connection; + this.connections.put(id, connection); } - public JingleConnection createNewConnection(final JinglePacket packet) { - JingleConnection connection = new JingleConnection(this); - this.connections.add(connection); - return connection; + void finishConnection(final AbstractJingleConnection connection) { + this.connections.remove(connection.getId()); } - public void finishConnection(JingleConnection connection) { - this.connections.remove(connection); - } - - public void getPrimaryCandidate(final Account account, final boolean initiator, final OnPrimaryCandidateFound listener) { + void getPrimaryCandidate(final Account account, final boolean initiator, final OnPrimaryCandidateFound listener) { if (Config.DISABLE_PROXY_LOOKUP) { listener.onPrimaryCandidateFound(false, null); return; @@ -97,7 +86,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - Element streamhost = packet.query().findChild("streamhost", Namespace.BYTE_STREAMS); + final Element streamhost = packet.query().findChild("streamhost", Namespace.BYTE_STREAMS); final String host = streamhost == null ? null : streamhost.getAttribute("host"); final String port = streamhost == null ? null : streamhost.getAttribute("port"); if (host != null && port != null) { @@ -112,7 +101,6 @@ public class JingleConnectionManager extends AbstractConnectionManager { listener.onPrimaryCandidateFound(true, candidate); } catch (final NumberFormatException e) { listener.onPrimaryCandidateFound(false, null); - return; } } else { listener.onPrimaryCandidateFound(false, null); @@ -129,31 +117,30 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } - public String nextRandomId() { - return new BigInteger(50, random).toString(32); + static String nextRandomId() { + return UUID.randomUUID().toString(); } public void deliverIbbPacket(Account account, IqPacket packet) { String sid = null; Element payload = null; - if (packet.hasChild("open", "http://jabber.org/protocol/ibb")) { - payload = packet.findChild("open", "http://jabber.org/protocol/ibb"); + if (packet.hasChild("open", Namespace.IBB)) { + payload = packet.findChild("open", Namespace.IBB); sid = payload.getAttribute("sid"); - } else if (packet.hasChild("data", "http://jabber.org/protocol/ibb")) { - payload = packet.findChild("data", "http://jabber.org/protocol/ibb"); + } else if (packet.hasChild("data", Namespace.IBB)) { + payload = packet.findChild("data", Namespace.IBB); sid = payload.getAttribute("sid"); - } else if (packet.hasChild("close", "http://jabber.org/protocol/ibb")) { - payload = packet.findChild("close", "http://jabber.org/protocol/ibb"); + } else if (packet.hasChild("close", Namespace.IBB)) { + payload = packet.findChild("close", Namespace.IBB); sid = payload.getAttribute("sid"); } if (sid != null) { - for (JingleConnection connection : connections) { - if (connection.getAccount() == account - && connection.hasTransportId(sid)) { - JingleTransport transport = connection.getTransport(); + for (final AbstractJingleConnection connection : this.connections.values()) { + if (connection instanceof JingleFileTransferConnection) { + final JingleFileTransferConnection fileTransfer = (JingleFileTransferConnection) connection; + final JingleTransport transport = fileTransfer.getTransport(); if (transport instanceof JingleInBandTransport) { - JingleInBandTransport inbandTransport = (JingleInBandTransport) transport; - inbandTransport.deliverPayload(packet, payload); + ((JingleInBandTransport) transport).deliverPayload(packet, payload); return; } } @@ -164,10 +151,10 @@ public class JingleConnectionManager extends AbstractConnectionManager { } public void cancelInTransmission() { - for (JingleConnection connection : this.connections) { - if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) { + for (AbstractJingleConnection connection : this.connections.values()) { + /*if (connection.getJingleStatus() == JingleFileTransferConnection.JINGLE_STATUS_TRANSMITTING) { connection.abort("connectivity-error"); - } + }*/ } } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java similarity index 83% rename from src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java rename to src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java index 3b2909cc7..6586474da 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java @@ -3,6 +3,7 @@ package eu.siacs.conversations.xmpp.jingle; import android.util.Base64; import android.util.Log; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -41,27 +42,22 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; import eu.siacs.conversations.xmpp.stanzas.IqPacket; import rocks.xmpp.addr.Jid; -public class JingleConnection implements Transferable { +public class JingleFileTransferConnection extends AbstractJingleConnection implements Transferable { + private static final int JINGLE_STATUS_TRANSMITTING = 5; private static final String JET_OMEMO_CIPHER = "urn:xmpp:ciphers:aes-128-gcm-nopadding"; - private static final int JINGLE_STATUS_INITIATED = 0; private static final int JINGLE_STATUS_ACCEPTED = 1; private static final int JINGLE_STATUS_FINISHED = 4; - static final int JINGLE_STATUS_TRANSMITTING = 5; private static final int JINGLE_STATUS_FAILED = 99; private static final int JINGLE_STATUS_OFFERED = -1; - private JingleConnectionManager mJingleConnectionManager; - private XmppConnectionService mXmppConnectionService; private Content.Version ftVersion = Content.Version.FT_3; private int ibbBlockSize = 8192; - private int mJingleStatus = JINGLE_STATUS_OFFERED; + private int mJingleStatus = JINGLE_STATUS_OFFERED; //migrate to enum private int mStatus = Transferable.STATUS_UNKNOWN; private Message message; - private String sessionId; - private Account account; private Jid initiator; private Jid responder; private List candidates = new ArrayList<>(); @@ -98,7 +94,7 @@ public class JingleConnection implements Transferable { if (mJingleStatus != JINGLE_STATUS_FAILED && mJingleStatus != JINGLE_STATUS_FINISHED) { fail(IqParser.extractErrorMessage(packet)); } else { - Log.d(Config.LOGTAG,"ignoring late delivery of jingle packet to jingle session with status="+mJingleStatus+": "+packet.toString()); + Log.d(Config.LOGTAG, "ignoring late delivery of jingle packet to jingle session with status=" + mJingleStatus + ": " + packet.toString()); } } }; @@ -109,32 +105,32 @@ public class JingleConnection implements Transferable { public void onFileTransmitted(DownloadableFile file) { if (responding()) { if (expectedHash.length > 0 && !Arrays.equals(expectedHash, file.getSha1Sum())) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": hashes did not match"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": hashes did not match"); } - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": file transmitted(). we are responding"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": file transmitted(). we are responding"); sendSuccess(); - mXmppConnectionService.getFileBackend().updateFileParams(message); - mXmppConnectionService.databaseBackend.createMessage(message); - mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVED); + xmppConnectionService.getFileBackend().updateFileParams(message); + xmppConnectionService.databaseBackend.createMessage(message); + xmppConnectionService.markMessage(message, Message.STATUS_RECEIVED); if (acceptedAutomatically) { message.markUnread(); if (message.getEncryption() == Message.ENCRYPTION_PGP) { - account.getPgpDecryptionService().decrypt(message, true); + id.account.getPgpDecryptionService().decrypt(message, true); } else { - mXmppConnectionService.getFileBackend().updateMediaScanner(file, () -> JingleConnection.this.mXmppConnectionService.getNotificationService().push(message)); + xmppConnectionService.getFileBackend().updateMediaScanner(file, () -> JingleFileTransferConnection.this.xmppConnectionService.getNotificationService().push(message)); } Log.d(Config.LOGTAG, "successfully transmitted file:" + file.getAbsolutePath() + " (" + CryptoHelper.bytesToHex(file.getSha1Sum()) + ")"); return; } else if (message.getEncryption() == Message.ENCRYPTION_PGP) { - account.getPgpDecryptionService().decrypt(message, true); + id.account.getPgpDecryptionService().decrypt(message, true); } } else { if (ftVersion == Content.Version.FT_5) { //older Conversations will break when receiving a session-info sendHash(); } if (message.getEncryption() == Message.ENCRYPTION_PGP) { - account.getPgpDecryptionService().decrypt(message, false); + id.account.getPgpDecryptionService().decrypt(message, false); } if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { file.delete(); @@ -142,14 +138,14 @@ public class JingleConnection implements Transferable { } Log.d(Config.LOGTAG, "successfully transmitted file:" + file.getAbsolutePath() + " (" + CryptoHelper.bytesToHex(file.getSha1Sum()) + ")"); if (message.getEncryption() != Message.ENCRYPTION_PGP) { - mXmppConnectionService.getFileBackend().updateMediaScanner(file); + xmppConnectionService.getFileBackend().updateMediaScanner(file); } } @Override public void onFileTransferAborted() { - JingleConnection.this.sendSessionTerminate("connectivity-error"); - JingleConnection.this.fail(); + JingleFileTransferConnection.this.sendSessionTerminate("connectivity-error"); + JingleFileTransferConnection.this.fail(); } }; private OnTransportConnected onIbbTransportConnected = new OnTransportConnected() { @@ -160,16 +156,16 @@ public class JingleConnection implements Transferable { @Override public void established() { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ibb transport connected. sending file"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ibb transport connected. sending file"); mJingleStatus = JINGLE_STATUS_TRANSMITTING; - JingleConnection.this.transport.send(file, onFileTransmissionStatusChanged); + JingleFileTransferConnection.this.transport.send(file, onFileTransmissionStatusChanged); } }; private OnProxyActivated onProxyActivated = new OnProxyActivated() { @Override public void success() { - if (initiator.equals(account.getJid())) { + if (initiator.equals(id.account.getJid())) { Log.d(Config.LOGTAG, "we were initiating. sending file"); transport.send(file, onFileTransmissionStatusChanged); } else { @@ -180,7 +176,7 @@ public class JingleConnection implements Transferable { @Override public void failed() { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": proxy activation failed"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": proxy activation failed"); proxyActivationFailed = true; if (initiating()) { sendFallbackToIbb(); @@ -188,18 +184,28 @@ public class JingleConnection implements Transferable { } }; - public JingleConnection(JingleConnectionManager mJingleConnectionManager) { - this.mJingleConnectionManager = mJingleConnectionManager; - this.mXmppConnectionService = mJingleConnectionManager - .getXmppConnectionService(); + public JingleFileTransferConnection(JingleConnectionManager jingleConnectionManager, Id id) { + super(jingleConnectionManager, id); + } + + private static long parseLong(final Element element, final long l) { + final String input = element == null ? null : element.getContent(); + if (input == null) { + return l; + } + try { + return Long.parseLong(input); + } catch (Exception e) { + return l; + } } private boolean responding() { - return responder != null && responder.equals(account.getJid()); + return responder != null && responder.equals(id.account.getJid()); } private boolean initiating() { - return initiator.equals(account.getJid()); + return initiator.equals(id.account.getJid()); } InputStream getFileInputStream() { @@ -211,25 +217,19 @@ public class JingleConnection implements Transferable { Log.d(Config.LOGTAG, "file object was not assigned"); return null; } - this.file.getParentFile().mkdirs(); - this.file.createNewFile(); + final File parent = this.file.getParentFile(); + if (parent != null && parent.mkdirs()) { + Log.d(Config.LOGTAG,"created parent directories for file "+file.getAbsolutePath()); + } + if (this.file.createNewFile()) { + Log.d(Config.LOGTAG,"created output file "+file.getAbsolutePath()); + } this.mFileOutputStream = AbstractConnectionManager.createOutputStream(this.file, false, true); return this.mFileOutputStream; } - public String getSessionId() { - return this.sessionId; - } - - public Account getAccount() { - return this.account; - } - - public Jid getCounterPart() { - return this.message.getCounterpart(); - } - - void deliverPacket(JinglePacket packet) { + @Override + void deliverPacket(final JinglePacket packet) { if (packet.isAction("session-terminate")) { Reason reason = packet.getReason(); if (reason != null) { @@ -289,7 +289,7 @@ public class JingleConnection implements Transferable { final Element error = response.addChild("error").setAttribute("type", "cancel"); error.addChild("not-acceptable", "urn:ietf:params:xml:ns:xmpp-stanzas"); } - mXmppConnectionService.sendIqPacket(account, response, null); + xmppConnectionService.sendIqPacket(id.account, response, null); } private void respondToIqWithOutOfOrder(final IqPacket packet) { @@ -297,7 +297,7 @@ public class JingleConnection implements Transferable { final Element error = response.addChild("error").setAttribute("type", "wait"); error.addChild("unexpected-request", "urn:ietf:params:xml:ns:xmpp-stanzas"); error.addChild("out-of-order", "urn:xmpp:jingle:errors:1"); - mXmppConnectionService.sendIqPacket(account, response, null); + xmppConnectionService.sendIqPacket(id.account, response, null); } public void init(final Message message) { @@ -318,24 +318,22 @@ public class JingleConnection implements Transferable { private void init(Message message, XmppAxolotlMessage xmppAxolotlMessage) { this.mXmppAxolotlMessage = xmppAxolotlMessage; this.contentCreator = "initiator"; - this.contentName = this.mJingleConnectionManager.nextRandomId(); + this.contentName = JingleConnectionManager.nextRandomId(); this.message = message; - this.account = message.getConversation().getAccount(); final List remoteFeatures = getRemoteFeatures(); upgradeNamespace(remoteFeatures); this.initialTransport = remoteFeatures.contains(Namespace.JINGLE_TRANSPORTS_S5B) ? Transport.SOCKS : Transport.IBB; this.remoteSupportsOmemoJet = remoteFeatures.contains(Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO); this.message.setTransferable(this); this.mStatus = Transferable.STATUS_UPLOADING; - this.initiator = this.account.getJid(); - this.responder = this.message.getCounterpart(); - this.sessionId = this.mJingleConnectionManager.nextRandomId(); - this.transportId = this.mJingleConnectionManager.nextRandomId(); + this.initiator = this.id.account.getJid(); + this.responder = this.id.counterPart; + this.transportId = JingleConnectionManager.nextRandomId(); if (this.initialTransport == Transport.IBB) { this.sendInitRequest(); } else { gatherAndConnectDirectCandidates(); - this.mJingleConnectionManager.getPrimaryCandidate(account, initiating(), (success, candidate) -> { + this.jingleConnectionManager.getPrimaryCandidate(id.account, initiating(), (success, candidate) -> { if (success) { final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate); connections.put(candidate.getCid(), socksConnection); @@ -367,10 +365,10 @@ public class JingleConnection implements Transferable { private void gatherAndConnectDirectCandidates() { final List directCandidates; if (Config.USE_DIRECT_JINGLE_CANDIDATES) { - if (account.isOnion() || mXmppConnectionService.useTorToConnect()) { + if (id.account.isOnion() || xmppConnectionService.useTorToConnect()) { directCandidates = Collections.emptyList(); } else { - directCandidates = DirectConnectionUtils.getLocalCandidates(account.getJid()); + directCandidates = DirectConnectionUtils.getLocalCandidates(id.account.getJid()); } } else { directCandidates = Collections.emptyList(); @@ -391,10 +389,10 @@ public class JingleConnection implements Transferable { } private List getRemoteFeatures() { - Jid jid = this.message.getCounterpart(); + final Jid jid = this.id.counterPart; String resource = jid != null ? jid.getResource() : null; if (resource != null) { - Presence presence = this.account.getRoster().getContact(jid).getPresences().getPresences().get(resource); + Presence presence = this.id.account.getRoster().getContact(jid).getPresences().getPresences().get(resource); ServiceDiscoveryResult result = presence != null ? presence.getServiceDiscoveryResult() : null; return result == null ? Collections.emptyList() : result.getFeatures(); } else { @@ -402,22 +400,19 @@ public class JingleConnection implements Transferable { } } - public void init(Account account, JinglePacket packet) { + public void init(Account account, JinglePacket packet) { //should move to deliverPacket this.mJingleStatus = JINGLE_STATUS_INITIATED; - Conversation conversation = this.mXmppConnectionService + Conversation conversation = this.xmppConnectionService .findOrCreateConversation(account, packet.getFrom().asBareJid(), false, false); this.message = new Message(conversation, "", Message.ENCRYPTION_NONE); this.message.setStatus(Message.STATUS_RECEIVED); this.mStatus = Transferable.STATUS_OFFER; this.message.setTransferable(this); - final Jid from = packet.getFrom(); - this.message.setCounterpart(from); - this.account = account; - this.initiator = packet.getFrom(); - this.responder = this.account.getJid(); - this.sessionId = packet.getSessionId(); - Content content = packet.getJingleContent(); + this.message.setCounterpart(this.id.counterPart); + this.initiator = this.id.counterPart; + this.responder = this.id.account.getJid(); + final Content content = packet.getJingleContent(); this.contentCreator = content.getAttribute("creator"); this.initialTransport = content.hasSocks5Transport() ? Transport.SOCKS : Transport.IBB; this.contentName = content.getAttribute("name"); @@ -459,7 +454,7 @@ public class JingleConnection implements Transferable { if (encrypted == null) { final Element security = content.findChild("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT); if (security != null && AxolotlService.PEP_PREFIX.equals(security.getAttribute("type"))) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received jingle file offer with JET"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received jingle file offer with JET"); encrypted = security.findChild("encrypted", AxolotlService.PEP_PREFIX); remoteIsUsingJet = true; } @@ -490,10 +485,10 @@ public class JingleConnection implements Transferable { long size = parseLong(fileSize, 0); message.setBody(Long.toString(size)); conversation.add(message); - mJingleConnectionManager.updateConversationUi(true); - this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false); + jingleConnectionManager.updateConversationUi(true); + this.file = this.xmppConnectionService.getFileBackend().getFile(message, false); if (mXmppAxolotlMessage != null) { - XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage, false); + XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = id.account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage, false); if (transportMessage != null) { message.setEncryption(Message.ENCRYPTION_AXOLOTL); this.file.setKey(transportMessage.getKey()); @@ -511,11 +506,11 @@ public class JingleConnection implements Transferable { respondToIq(packet, true); - if (account.getRoster().getContact(from).showInContactList() - && mJingleConnectionManager.hasStoragePermission() - && size < this.mJingleConnectionManager.getAutoAcceptFileSize() - && mXmppConnectionService.isDataSaverDisabled()) { - Log.d(Config.LOGTAG, "auto accepting file from " + from); + if (id.account.getRoster().getContact(id.counterPart).showInContactList() + && jingleConnectionManager.hasStoragePermission() + && size < this.jingleConnectionManager.getAutoAcceptFileSize() + && xmppConnectionService.isDataSaverDisabled()) { + Log.d(Config.LOGTAG, "auto accepting file from " + id.counterPart); this.acceptedAutomatically = true; this.sendAccept(); } else { @@ -524,9 +519,9 @@ public class JingleConnection implements Transferable { "not auto accepting new file offer with size: " + size + " allowed size:" - + this.mJingleConnectionManager + + this.jingleConnectionManager .getAutoAcceptFileSize()); - this.mXmppConnectionService.getNotificationService().push(message); + this.xmppConnectionService.getNotificationService().push(message); } Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize()); return; @@ -535,24 +530,12 @@ public class JingleConnection implements Transferable { } } - private static long parseLong(final Element element, final long l) { - final String input = element == null ? null : element.getContent(); - if (input == null) { - return l; - } - try { - return Long.parseLong(input); - } catch (Exception e) { - return l; - } - } - private void sendInitRequest() { JinglePacket packet = this.bootstrapPacket("session-initiate"); Content content = new Content(this.contentCreator, this.contentName); if (message.isFileOrImage()) { content.setTransportId(this.transportId); - this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false); + this.file = this.xmppConnectionService.getFileBackend().getFile(message, false); if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { this.file.setKey(mXmppAxolotlMessage.getInnerKey()); this.file.setIv(mXmppAxolotlMessage.getIV()); @@ -561,7 +544,7 @@ public class JingleConnection implements Transferable { this.file.setExpectedSize(file.getSize() + (this.remoteSupportsOmemoJet ? 0 : 16)); final Element file = content.setFileOffer(this.file, false, this.ftVersion); if (remoteSupportsOmemoJet) { - Log.d(Config.LOGTAG, 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); security.setAttribute("name", this.contentName); security.setAttribute("cipher", JET_OMEMO_CIPHER); @@ -585,19 +568,19 @@ public class JingleConnection implements Transferable { content.setTransportId(this.transportId); if (this.initialTransport == Transport.IBB) { content.ibbTransport().setAttribute("block-size", Integer.toString(this.ibbBlockSize)); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending IBB offer"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": sending IBB offer"); } else { final List candidates = getCandidatesAsElements(); - Log.d(Config.LOGTAG, String.format("%s: sending S5B offer with %d candidates", 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.setContent(content); this.sendJinglePacket(packet, (account, response) -> { if (response.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": other party received offer"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": other party received offer"); if (mJingleStatus == JINGLE_STATUS_OFFERED) { mJingleStatus = JINGLE_STATUS_INITIATED; - mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED); + xmppConnectionService.markMessage(message, Message.STATUS_OFFERED); } else { Log.d(Config.LOGTAG, "received ack for offer when status was " + mJingleStatus); } @@ -628,7 +611,7 @@ public class JingleConnection implements Transferable { private void sendAccept() { mJingleStatus = JINGLE_STATUS_ACCEPTED; this.mStatus = Transferable.STATUS_DOWNLOADING; - this.mJingleConnectionManager.updateConversationUi(true); + this.jingleConnectionManager.updateConversationUi(true); if (initialTransport == Transport.SOCKS) { sendAcceptSocks(); } else { @@ -638,7 +621,7 @@ public class JingleConnection implements Transferable { private void sendAcceptSocks() { gatherAndConnectDirectCandidates(); - this.mJingleConnectionManager.getPrimaryCandidate(this.account, initiating(), (success, candidate) -> { + this.jingleConnectionManager.getPrimaryCandidate(this.id.account, initiating(), (success, candidate) -> { final JinglePacket packet = bootstrapPacket("session-accept"); final Content content = new Content(contentCreator, contentName); content.setFileOffer(fileOffer, ftVersion); @@ -692,34 +675,34 @@ public class JingleConnection implements Transferable { private JinglePacket bootstrapPacket(String action) { JinglePacket packet = new JinglePacket(); packet.setAction(action); - packet.setFrom(account.getJid()); - packet.setTo(this.message.getCounterpart()); - packet.setSessionId(this.sessionId); + packet.setFrom(id.account.getJid()); + packet.setTo(id.counterPart); + packet.setSessionId(this.id.sessionId); packet.setInitiator(this.initiator); return packet; } private void sendJinglePacket(JinglePacket packet) { - mXmppConnectionService.sendIqPacket(account, packet, responseListener); + xmppConnectionService.sendIqPacket(id.account, packet, responseListener); } private void sendJinglePacket(JinglePacket packet, OnIqPacketReceived callback) { - mXmppConnectionService.sendIqPacket(account, packet, callback); + xmppConnectionService.sendIqPacket(id.account, packet, callback); } private void receiveAccept(JinglePacket packet) { if (responding()) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received out of order session-accept (we were responding)"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received out of order session-accept (we were responding)"); respondToIqWithOutOfOrder(packet); return; } if (this.mJingleStatus != JINGLE_STATUS_INITIATED) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received out of order session-accept"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received out of order session-accept"); respondToIqWithOutOfOrder(packet); return; } this.mJingleStatus = JINGLE_STATUS_ACCEPTED; - mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND); + xmppConnectionService.markMessage(message, Message.STATUS_UNSEND); Content content = packet.getJingleContent(); if (content.hasSocks5Transport()) { respondToIq(packet, true); @@ -734,7 +717,7 @@ public class JingleConnection implements Transferable { this.ibbBlockSize = bs; } } catch (Exception e) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to parse block size in session-accept"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to parse block size in session-accept"); } } respondToIq(packet, true); @@ -769,7 +752,7 @@ public class JingleConnection implements Transferable { respondToIq(packet, true); onProxyActivated.failed(); } else if (content.socks5transport().hasChild("candidate-error")) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received candidate error"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received candidate error"); respondToIq(packet, true); this.receivedCandidate = true; if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) { @@ -808,21 +791,21 @@ public class JingleConnection implements Transferable { final JingleSocks5Transport connection = chooseConnection(); this.transport = connection; if (connection == null) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not find suitable candidate"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": could not find suitable candidate"); this.disconnectSocks5Connections(); if (initiating()) { this.sendFallbackToIbb(); } } else { final JingleCandidate candidate = connection.getCandidate(); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": elected candidate " + candidate.getHost() + ":" + candidate.getPort()); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": elected candidate " + candidate.getHost() + ":" + candidate.getPort()); 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().asBareJid() + ": use session ID instead of transport ID to activate proxy"); - sid = getSessionId(); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": use session ID instead of transport ID to activate proxy"); + sid = id.sessionId; } else { sid = getTransportId(); } @@ -834,10 +817,10 @@ public class JingleConnection implements Transferable { activation.query("http://jabber.org/protocol/bytestreams") .setAttribute("sid", sid); activation.query().addChild("activate") - .setContent(this.getCounterPart().toString()); - mXmppConnectionService.sendIqPacket(account, activation, (account, response) -> { + .setContent(this.id.counterPart.toEscapedString()); + xmppConnectionService.sendIqPacket(this.id.account, activation, (account, response) -> { if (response.getType() != IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + response.toString()); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": " + response.toString()); sendProxyError(); onProxyActivated.failed(); } else { @@ -904,15 +887,15 @@ public class JingleConnection implements Transferable { this.mJingleStatus = JINGLE_STATUS_FINISHED; this.message.setStatus(Message.STATUS_RECEIVED); this.message.setTransferable(null); - this.mXmppConnectionService.updateMessage(message, false); - this.mJingleConnectionManager.finishConnection(this); + this.xmppConnectionService.updateMessage(message, false); + this.jingleConnectionManager.finishConnection(this); } private void sendFallbackToIbb() { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending fallback to ibb"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": sending fallback to ibb"); JinglePacket packet = this.bootstrapPacket("transport-replace"); Content content = new Content(this.contentCreator, this.contentName); - this.transportId = this.mJingleConnectionManager.nextRandomId(); + this.transportId = this.jingleConnectionManager.nextRandomId(); content.setTransportId(this.transportId); content.ibbTransport().setAttribute("block-size", Integer.toString(this.ibbBlockSize)); @@ -923,18 +906,18 @@ public class JingleConnection implements Transferable { private void receiveFallbackToIbb(JinglePacket packet) { if (initiating()) { - Log.d(Config.LOGTAG, 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); return; } final boolean validState = mJingleStatus == JINGLE_STATUS_ACCEPTED || (proxyActivationFailed && mJingleStatus == JINGLE_STATUS_TRANSMITTING); if (!validState) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received out of order transport-replace"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received out of order transport-replace"); respondToIqWithOutOfOrder(packet); return; } this.proxyActivationFailed = false; //fallback received; now we no longer need to accept another one; - Log.d(Config.LOGTAG, 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"); if (receivedBlockSize != null) { try { @@ -943,7 +926,7 @@ public class JingleConnection implements Transferable { this.ibbBlockSize = bs; } } catch (NumberFormatException e) { - Log.d(Config.LOGTAG, 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 = packet.getJingleContent().getTransportId(); @@ -961,7 +944,7 @@ public class JingleConnection implements Transferable { if (initiating()) { this.sendJinglePacket(answer, (account, response) -> { if (response.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + " recipient ACKed our transport-accept. creating ibb"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + " recipient ACKed our transport-accept. creating ibb"); transport.connect(onIbbTransportConnected); } }); @@ -973,13 +956,13 @@ public class JingleConnection implements Transferable { private void receiveTransportAccept(JinglePacket packet) { if (responding()) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received out of order transport-accept (we were responding)"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received out of order transport-accept (we were responding)"); respondToIqWithOutOfOrder(packet); return; } final boolean validState = mJingleStatus == JINGLE_STATUS_ACCEPTED || (proxyActivationFailed && mJingleStatus == JINGLE_STATUS_TRANSMITTING); if (!validState) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received out of order transport-accept"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received out of order transport-accept"); respondToIqWithOutOfOrder(packet); return; } @@ -995,13 +978,13 @@ public class JingleConnection implements Transferable { this.ibbBlockSize = bs; } } catch (NumberFormatException e) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to parse block size in transport-accept"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to parse block size in transport-accept"); } } this.transport = new JingleInBandTransport(this, this.transportId, this.ibbBlockSize); if (sid == null || !sid.equals(this.transportId)) { - Log.w(Config.LOGTAG, String.format("%s: sid in transport-accept (%s) did not match our sid (%s) ", account.getJid().asBareJid(), sid, transportId)); + Log.w(Config.LOGTAG, String.format("%s: sid in transport-accept (%s) did not match our sid (%s) ", id.account.getJid().asBareJid(), sid, transportId)); } respondToIq(packet, true); //might be receive instead if we are not initiating @@ -1009,7 +992,7 @@ public class JingleConnection implements Transferable { this.transport.connect(onIbbTransportConnected); } } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received invalid transport-accept"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received invalid transport-accept"); respondToIq(packet, false); } } @@ -1017,15 +1000,15 @@ public class JingleConnection implements Transferable { private void receiveSuccess() { if (initiating()) { this.mJingleStatus = JINGLE_STATUS_FINISHED; - this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_RECEIVED); + this.xmppConnectionService.markMessage(this.message, Message.STATUS_SEND_RECEIVED); this.disconnectSocks5Connections(); if (this.transport instanceof JingleInBandTransport) { this.transport.disconnect(); } this.message.setTransferable(null); - this.mJingleConnectionManager.finishConnection(this); + this.jingleConnectionManager.finishConnection(this); } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received session-terminate/success while responding"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received session-terminate/success while responding"); } } @@ -1041,15 +1024,15 @@ public class JingleConnection implements Transferable { this.transport.disconnect(); } sendSessionTerminate(reason); - this.mJingleConnectionManager.finishConnection(this); + this.jingleConnectionManager.finishConnection(this); if (responding()) { this.message.setTransferable(new TransferablePlaceholder(cancelled ? Transferable.STATUS_CANCELLED : Transferable.STATUS_FAILED)); if (this.file != null) { file.delete(); } - this.mJingleConnectionManager.updateConversationUi(true); + this.jingleConnectionManager.updateConversationUi(true); } else { - this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : null); + this.xmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : null); this.message.setTransferable(null); } } @@ -1072,15 +1055,15 @@ public class JingleConnection implements Transferable { if (this.file != null) { file.delete(); } - this.mJingleConnectionManager.updateConversationUi(true); + this.jingleConnectionManager.updateConversationUi(true); } else { - this.mXmppConnectionService.markMessage(this.message, + this.xmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : errorMessage); this.message.setTransferable(null); } } - this.mJingleConnectionManager.finishConnection(this); + this.jingleConnectionManager.finishConnection(this); } private void sendSessionTerminate(String reason) { @@ -1168,7 +1151,7 @@ public class JingleConnection implements Transferable { } private void sendCandidateError() { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending candidate error"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": sending candidate error"); JinglePacket packet = bootstrapPacket("transport-info"); Content content = new Content(this.contentCreator, this.contentName); content.setTransportId(this.transportId); @@ -1221,7 +1204,7 @@ public class JingleConnection implements Transferable { void updateProgress(int i) { this.mProgress = i; - mJingleConnectionManager.updateConversationUi(false); + jingleConnectionManager.updateConversationUi(false); } public String getTransportId() { @@ -1241,7 +1224,7 @@ public class JingleConnection implements Transferable { } public boolean start() { - if (account.getStatus() == Account.State.ONLINE) { + if (id.account.getStatus() == Account.State.ONLINE) { if (mJingleStatus == JINGLE_STATUS_INITIATED) { new Thread(this::sendAccept).start(); } @@ -1271,7 +1254,7 @@ public class JingleConnection implements Transferable { } public AbstractConnectionManager getConnectionManager() { - return this.mJingleConnectionManager; + return this.jingleConnectionManager; } interface OnProxyActivated { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInBandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInBandTransport.java index 4182da08c..c5eaca072 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInBandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInBandTransport.java @@ -33,7 +33,7 @@ public class JingleInBandTransport extends JingleTransport { private boolean connected = true; private DownloadableFile file; - private final JingleConnection connection; + private final JingleFileTransferConnection connection; private InputStream fileInputStream = null; private InputStream innerInputStream = null; @@ -60,10 +60,10 @@ public class JingleInBandTransport extends JingleTransport { } }; - JingleInBandTransport(final JingleConnection connection, final String sid, final int blockSize) { + JingleInBandTransport(final JingleFileTransferConnection connection, final String sid, final int blockSize) { this.connection = connection; - this.account = connection.getAccount(); - this.counterpart = connection.getCounterPart(); + this.account = connection.getId().account; + this.counterpart = connection.getId().counterPart; this.blockSize = blockSize; this.sessionId = sid; } 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 e6b23ad18..2c9f11a56 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -31,8 +31,9 @@ public class JingleSocks5Transport extends JingleTransport { private static final int SOCKET_TIMEOUT_PROXY = 5000; private final JingleCandidate candidate; - private final JingleConnection connection; + private final JingleFileTransferConnection connection; private final String destination; + private final Account account; private OutputStream outputStream; private InputStream inputStream; private boolean isEstablished = false; @@ -40,7 +41,7 @@ public class JingleSocks5Transport extends JingleTransport { private ServerSocket serverSocket; private Socket socket; - JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) { + JingleSocks5Transport(JingleFileTransferConnection jingleConnection, JingleCandidate candidate) { final MessageDigest messageDigest; try { messageDigest = MessageDigest.getInstance("SHA-1"); @@ -49,19 +50,20 @@ public class JingleSocks5Transport extends JingleTransport { } this.candidate = candidate; this.connection = jingleConnection; + this.account = jingleConnection.getId().account; final StringBuilder destBuilder = new StringBuilder(); - if (jingleConnection.getFtVersion() == Content.Version.FT_3) { - Log.d(Config.LOGTAG, this.connection.getAccount().getJid().asBareJid() + ": using session Id instead of transport Id for proxy destination"); - destBuilder.append(jingleConnection.getSessionId()); + if (this.connection.getFtVersion() == Content.Version.FT_3) { + Log.d(Config.LOGTAG, this.account.getJid().asBareJid() + ": using session Id instead of transport Id for proxy destination"); + destBuilder.append(this.connection.getId().sessionId); } else { - destBuilder.append(jingleConnection.getTransportId()); + destBuilder.append(this.connection.getTransportId()); } if (candidate.isOurs()) { - destBuilder.append(jingleConnection.getAccount().getJid()); - destBuilder.append(jingleConnection.getCounterPart()); + destBuilder.append(this.account.getJid()); + destBuilder.append(this.connection.getId().counterPart); } else { - destBuilder.append(jingleConnection.getCounterPart()); - destBuilder.append(jingleConnection.getAccount().getJid()); + destBuilder.append(this.connection.getId().counterPart); + destBuilder.append(this.account.getJid()); } messageDigest.reset(); this.destination = CryptoHelper.bytesToHex(messageDigest.digest(destBuilder.toString().getBytes())); @@ -130,7 +132,7 @@ public class JingleSocks5Transport extends JingleTransport { responseHeader = new byte[]{0x05, 0x00, 0x00, 0x03}; success = true; } else { - Log.d(Config.LOGTAG,connection.getAccount().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}; success = false; } @@ -141,7 +143,7 @@ public class JingleSocks5Transport extends JingleTransport { outputStream.write(response.array()); outputStream.flush(); if (success) { - Log.d(Config.LOGTAG,connection.getAccount().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); this.socket = socket; this.inputStream = inputStream; @@ -160,7 +162,7 @@ public class JingleSocks5Transport extends JingleTransport { new Thread(() -> { final int timeout = candidate.getType() == JingleCandidate.TYPE_DIRECT ? SOCKET_TIMEOUT_DIRECT : SOCKET_TIMEOUT_PROXY; try { - final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect(); + final boolean useTor = this.account.isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect(); if (useTor) { socket = SocksSocketFactory.createSocketOverTor(candidate.getHost(), candidate.getPort()); } else { @@ -185,7 +187,7 @@ public class JingleSocks5Transport extends JingleTransport { public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) { new Thread(() -> { InputStream fileInputStream = null; - final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId()); + final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getId().sessionId); long transmitted = 0; try { wakeLock.acquire(); @@ -193,7 +195,7 @@ public class JingleSocks5Transport extends JingleTransport { digest.reset(); fileInputStream = connection.getFileInputStream(); if (fileInputStream == null) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create input stream"); + Log.d(Config.LOGTAG, this.account.getJid().asBareJid() + ": could not create input stream"); callback.onFileTransferAborted(); return; } @@ -213,7 +215,7 @@ public class JingleSocks5Transport extends JingleTransport { callback.onFileTransmitted(file); } } catch (Exception e) { - final Account account = connection.getAccount(); + final Account account = this.account; Log.d(Config.LOGTAG, account.getJid().asBareJid()+": failed sending file after "+transmitted+"/"+file.getExpectedSize()+" ("+ socket.getInetAddress()+":"+socket.getPort()+")", e); callback.onFileTransferAborted(); } finally { @@ -227,7 +229,7 @@ public class JingleSocks5Transport extends JingleTransport { public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) { new Thread(() -> { OutputStream fileOutputStream = null; - final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_" + connection.getSessionId()); + final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_" + connection.getId().sessionId); try { wakeLock.acquire(); MessageDigest digest = MessageDigest.getInstance("SHA-1"); @@ -237,7 +239,7 @@ public class JingleSocks5Transport extends JingleTransport { fileOutputStream = connection.getFileOutputStream(); if (fileOutputStream == null) { callback.onFileTransferAborted(); - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create output stream"); + Log.d(Config.LOGTAG, this.account.getJid().asBareJid() + ": could not create output stream"); return; } double size = file.getExpectedSize(); @@ -248,7 +250,7 @@ public class JingleSocks5Transport extends JingleTransport { count = inputStream.read(buffer); if (count == -1) { callback.onFileTransferAborted(); - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": file ended prematurely with " + remainingSize + " bytes remaining"); + Log.d(Config.LOGTAG, this.account.getJid().asBareJid() + ": file ended prematurely with " + remainingSize + " bytes remaining"); return; } else { fileOutputStream.write(buffer, 0, count); @@ -262,7 +264,7 @@ public class JingleSocks5Transport extends JingleTransport { file.setSha1Sum(digest.digest()); callback.onFileTransmitted(file); } catch (Exception e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": " + e.getMessage()); + Log.d(Config.LOGTAG, this.account.getJid().asBareJid() + ": " + e.getMessage()); callback.onFileTransferAborted(); } finally { WakeLockHelper.release(wakeLock); 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 3696756cf..e98a7e49e 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 @@ -6,6 +6,10 @@ import eu.siacs.conversations.xml.Namespace; public class Content extends Element { + + //refactor to getDescription and getTransport + //return either FileTransferDescription or GenericDescription or RtpDescription (all extend Description interface) + public enum Version { FT_3("urn:xmpp:jingle:apps:file-transfer:3"), FT_4("urn:xmpp:jingle:apps:file-transfer:4"), diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java index 53f0fd6cd..fe9a2ab56 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java @@ -7,109 +7,115 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket; import rocks.xmpp.addr.Jid; public class JinglePacket extends IqPacket { - Content content = null; - Reason reason = null; - Element checksum = null; - Element jingle = new Element("jingle"); - @Override - public Element addChild(Element child) { - if ("jingle".equals(child.getName())) { - Element contentElement = child.findChild("content"); - if (contentElement != null) { - this.content = new Content(); - this.content.setChildren(contentElement.getChildren()); - this.content.setAttributes(contentElement.getAttributes()); - } - Element reasonElement = child.findChild("reason"); - if (reasonElement != null) { - this.reason = new Reason(); - this.reason.setChildren(reasonElement.getChildren()); - this.reason.setAttributes(reasonElement.getAttributes()); - } - this.checksum = child.findChild("checksum"); - this.jingle.setAttributes(child.getAttributes()); - } - return child; - } - public JinglePacket setContent(Content content) { - this.content = content; - return this; - } + //get rid of that BS and set/get directly + Content content = null; + Reason reason = null; + Element checksum = null; + Element jingle = new Element("jingle"); - public Content getJingleContent() { - if (this.content == null) { - this.content = new Content(); - } - return this.content; - } + //get rid of what ever that is; maybe throw illegal state to ensure we are only calling setContent etc + @Override + public Element addChild(Element child) { + if ("jingle".equals(child.getName())) { + Element contentElement = child.findChild("content"); + if (contentElement != null) { + this.content = new Content(); + this.content.setChildren(contentElement.getChildren()); + this.content.setAttributes(contentElement.getAttributes()); + } + Element reasonElement = child.findChild("reason"); + if (reasonElement != null) { + this.reason = new Reason(); + this.reason.setChildren(reasonElement.getChildren()); + this.reason.setAttributes(reasonElement.getAttributes()); + } + this.checksum = child.findChild("checksum"); + this.jingle.setAttributes(child.getAttributes()); + } + return child; + } - public JinglePacket setReason(Reason reason) { - this.reason = reason; - return this; - } + public JinglePacket setContent(Content content) { //take content interface + this.content = content; + return this; + } - public Reason getReason() { - return this.reason; - } + public Content getJingleContent() { + if (this.content == null) { + this.content = new Content(); + } + return this.content; + } - public Element getChecksum() { - return this.checksum; - } + public Reason getReason() { + return this.reason; + } - private void build() { - this.children.clear(); - this.jingle.clearChildren(); - this.jingle.setAttribute("xmlns", "urn:xmpp:jingle:1"); - if (this.content != null) { - jingle.addChild(this.content); - } - if (this.reason != null) { - jingle.addChild(this.reason); - } - if (this.checksum != null) { - jingle.addChild(checksum); - } - this.children.add(jingle); - this.setAttribute("type", "set"); - } + public JinglePacket setReason(Reason reason) { + this.reason = reason; + return this; + } - public String getSessionId() { - return this.jingle.getAttribute("sid"); - } + public Element getChecksum() { + return this.checksum; + } - public void setSessionId(String sid) { - this.jingle.setAttribute("sid", sid); - } + //should be unnecessary if we set and get directly + private void build() { + this.children.clear(); + this.jingle.clearChildren(); + this.jingle.setAttribute("xmlns", "urn:xmpp:jingle:1"); + if (this.content != null) { + jingle.addChild(this.content); + } + if (this.reason != null) { + jingle.addChild(this.reason); + } + if (this.checksum != null) { + jingle.addChild(checksum); + } + this.children.add(jingle); + this.setAttribute("type", "set"); + } - @Override - public String toString() { - this.build(); - return super.toString(); - } + public String getSessionId() { + return this.jingle.getAttribute("sid"); + } - public void setAction(String action) { - this.jingle.setAttribute("action", action); - } + public void setSessionId(String sid) { + this.jingle.setAttribute("sid", sid); + } - public String getAction() { - return this.jingle.getAttribute("action"); - } + @Override + public String toString() { + this.build(); + return super.toString(); + } - public void setInitiator(final Jid initiator) { - this.jingle.setAttribute("initiator", initiator.toString()); - } + //use enum for action + public String getAction() { + return this.jingle.getAttribute("action"); + } - public boolean isAction(String action) { - return action.equalsIgnoreCase(this.getAction()); - } + public void setAction(String action) { + this.jingle.setAttribute("action", action); + } - public void addChecksum(byte[] sha1Sum, String namespace) { - this.checksum = new Element("checksum",namespace); - checksum.setAttribute("creator","initiator"); - checksum.setAttribute("name","a-file-offer"); - Element hash = checksum.addChild("file").addChild("hash","urn:xmpp:hashes:2"); - hash.setAttribute("algo","sha-1").setContent(Base64.encodeToString(sha1Sum,Base64.NO_WRAP)); - } + public void setInitiator(final Jid initiator) { + this.jingle.setAttribute("initiator", initiator.toString()); + } + + public boolean isAction(String action) { + return action.equalsIgnoreCase(this.getAction()); + } + + public void addChecksum(byte[] sha1Sum, String namespace) { + this.checksum = new Element("checksum", namespace); + checksum.setAttribute("creator", "initiator"); + checksum.setAttribute("name", "a-file-offer"); + Element hash = checksum.addChild("file").addChild("hash", "urn:xmpp:hashes:2"); + hash.setAttribute("algo", "sha-1").setContent(Base64.encodeToString(sha1Sum, Base64.NO_WRAP)); + } }