diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index 5f03fc899..6e8ac40c0 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -270,9 +270,10 @@ public class JingleConnection implements Transferable { IqPacket response; if (returnResult) { response = packet.generateResponse(IqPacket.TYPE.RESULT); - } else { response = packet.generateResponse(IqPacket.TYPE.ERROR); + 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); } @@ -933,8 +934,9 @@ public class JingleConnection implements Transferable { private boolean receiveTransportAccept(JinglePacket packet) { if (packet.getJingleContent().hasIbbTransport()) { - String receivedBlockSize = packet.getJingleContent().ibbTransport() - .getAttribute("block-size"); + final Element ibbTransport = packet.getJingleContent().ibbTransport(); + final String receivedBlockSize = ibbTransport.getAttribute("block-size"); + final String sid = ibbTransport.getAttribute("sid"); if (receivedBlockSize != null) { try { int bs = Integer.parseInt(receivedBlockSize); @@ -947,6 +949,10 @@ public class JingleConnection implements Transferable { } 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)); + } + //might be receive instead if we are not initiating if (initiating()) { this.transport.connect(onIbbTransportConnected); @@ -955,6 +961,7 @@ public class JingleConnection implements Transferable { } return true; } else { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received invalid transport-accept"); return false; } } 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 276040979..ac22614f8 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -23,223 +23,217 @@ import rocks.xmpp.addr.Jid; public class JingleInbandTransport extends JingleTransport { - private Account account; - private Jid counterpart; - private int blockSize; - private int seq = 0; - private String sessionId; + private Account account; + private Jid counterpart; + private int blockSize; + private int seq = 0; + private String sessionId; - private boolean established = false; + private boolean established = false; - private boolean connected = true; + private boolean connected = true; - private DownloadableFile file; - private JingleConnection connection; + private DownloadableFile file; + private JingleConnection connection; - private InputStream fileInputStream = null; - private InputStream innerInputStream = null; - private OutputStream fileOutputStream = null; - private long remainingSize = 0; - private long fileSize = 0; - private MessageDigest digest; + private InputStream fileInputStream = null; + private InputStream innerInputStream = null; + private OutputStream fileOutputStream = null; + private long remainingSize = 0; + private long fileSize = 0; + private MessageDigest digest; - private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged; + private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged; - private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (connected && packet.getType() == IqPacket.TYPE.RESULT) { - if (remainingSize > 0) { - sendNextBlock(); - } - } - } - }; + private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (connected && packet.getType() == IqPacket.TYPE.RESULT) { + if (remainingSize > 0) { + sendNextBlock(); + } + } + } + }; - public JingleInbandTransport(final JingleConnection connection, final String sid, final int blocksize) { - this.connection = connection; - this.account = connection.getAccount(); - this.counterpart = connection.getCounterPart(); - this.blockSize = blocksize; - this.sessionId = sid; - } - - private void sendClose() { - IqPacket iq = new IqPacket(IqPacket.TYPE.SET); - iq.setTo(this.counterpart); - Element close = iq.addChild("close", "http://jabber.org/protocol/ibb"); - close.setAttribute("sid", this.sessionId); - this.account.getXmppConnection().sendIqPacket(iq, null); - } - - public void connect(final OnTransportConnected callback) { - IqPacket iq = new IqPacket(IqPacket.TYPE.SET); - iq.setTo(this.counterpart); - Element open = iq.addChild("open", "http://jabber.org/protocol/ibb"); - open.setAttribute("sid", this.sessionId); - open.setAttribute("stanza", "iq"); - open.setAttribute("block-size", Integer.toString(this.blockSize)); - this.connected = true; - this.account.getXmppConnection().sendIqPacket(iq, - new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, - IqPacket packet) { - if (packet.getType() != IqPacket.TYPE.RESULT) { - callback.failed(); - } else { - callback.established(); - } - } - }); - } - - @Override - public void receive(DownloadableFile file, OnFileTransmissionStatusChanged callback) { - this.onFileTransmissionStatusChanged = callback; - this.file = file; - try { - this.digest = MessageDigest.getInstance("SHA-1"); - digest.reset(); - this.fileOutputStream = connection.getFileOutputStream(); - if (this.fileOutputStream == null) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": could not create output stream"); - callback.onFileTransferAborted(); - return; - } - this.remainingSize = this.fileSize = file.getExpectedSize(); - } catch (final NoSuchAlgorithmException | IOException e) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+" "+e.getMessage()); - callback.onFileTransferAborted(); - } + public JingleInbandTransport(final JingleConnection connection, final String sid, final int blocksize) { + this.connection = connection; + this.account = connection.getAccount(); + this.counterpart = connection.getCounterPart(); + this.blockSize = blocksize; + this.sessionId = sid; } - @Override - public void send(DownloadableFile file, OnFileTransmissionStatusChanged callback) { - this.onFileTransmissionStatusChanged = callback; - this.file = file; - try { - this.remainingSize = this.file.getExpectedSize(); - this.fileSize = this.remainingSize; - this.digest = MessageDigest.getInstance("SHA-1"); - this.digest.reset(); - fileInputStream = connection.getFileInputStream(); - if (fileInputStream == null) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": could no create input stream"); - callback.onFileTransferAborted(); - return; - } - innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream); - if (this.connected) { - this.sendNextBlock(); - } - } catch (Exception e) { - callback.onFileTransferAborted(); - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": "+e.getMessage()); - } - } + private void sendClose() { + IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + iq.setTo(this.counterpart); + Element close = iq.addChild("close", "http://jabber.org/protocol/ibb"); + close.setAttribute("sid", this.sessionId); + this.account.getXmppConnection().sendIqPacket(iq, null); + } - @Override - public void disconnect() { - this.connected = false; - FileBackend.close(fileOutputStream); - FileBackend.close(fileInputStream); - } + public void connect(final OnTransportConnected callback) { + IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + iq.setTo(this.counterpart); + Element open = iq.addChild("open", "http://jabber.org/protocol/ibb"); + open.setAttribute("sid", this.sessionId); + open.setAttribute("stanza", "iq"); + open.setAttribute("block-size", Integer.toString(this.blockSize)); + this.connected = true; + this.account.getXmppConnection().sendIqPacket(iq, (account, packet) -> { + if (packet.getType() != IqPacket.TYPE.RESULT) { + callback.failed(); + } else { + callback.established(); + } + }); + } - private void sendNextBlock() { - byte[] buffer = new byte[this.blockSize]; - try { - int count = innerInputStream.read(buffer); - if (count == -1) { - sendClose(); - file.setSha1Sum(digest.digest()); - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": sendNextBlock() count was -1"); - this.onFileTransmissionStatusChanged.onFileTransmitted(file); - fileInputStream.close(); - return; - } else if (count != buffer.length) { - int rem = innerInputStream.read(buffer,count,buffer.length-count); - if (rem > 0) { - count += rem; - } - } - this.remainingSize -= count; - this.digest.update(buffer,0,count); - String base64 = Base64.encodeToString(buffer,0,count, Base64.NO_WRAP); - IqPacket iq = new IqPacket(IqPacket.TYPE.SET); - iq.setTo(this.counterpart); - Element data = iq.addChild("data", "http://jabber.org/protocol/ibb"); - data.setAttribute("seq", Integer.toString(this.seq)); - data.setAttribute("block-size", Integer.toString(this.blockSize)); - data.setAttribute("sid", this.sessionId); - data.setContent(base64); - this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived); - this.account.getXmppConnection().r(); //don't fill up stanza queue too much - this.seq++; - connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); - if (this.remainingSize <= 0) { - sendClose(); - file.setSha1Sum(digest.digest()); - this.onFileTransmissionStatusChanged.onFileTransmitted(file); - fileInputStream.close(); - } - } catch (IOException e) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": io exception during sendNextBlock() "+e.getMessage()); - FileBackend.close(fileInputStream); - this.onFileTransmissionStatusChanged.onFileTransferAborted(); - } - } + @Override + public void receive(DownloadableFile file, OnFileTransmissionStatusChanged callback) { + this.onFileTransmissionStatusChanged = callback; + this.file = file; + try { + this.digest = MessageDigest.getInstance("SHA-1"); + digest.reset(); + this.fileOutputStream = connection.getFileOutputStream(); + if (this.fileOutputStream == null) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not create output stream"); + callback.onFileTransferAborted(); + return; + } + this.remainingSize = this.fileSize = file.getExpectedSize(); + } catch (final NoSuchAlgorithmException | IOException e) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + " " + e.getMessage()); + callback.onFileTransferAborted(); + } + } - private void receiveNextBlock(String data) { - try { - byte[] buffer = Base64.decode(data, Base64.NO_WRAP); - if (this.remainingSize < buffer.length) { - buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize); - } - this.remainingSize -= buffer.length; - this.fileOutputStream.write(buffer); - this.digest.update(buffer); - if (this.remainingSize <= 0) { - file.setSha1Sum(digest.digest()); - fileOutputStream.flush(); - fileOutputStream.close(); - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": receive next block nothing remaining"); - this.onFileTransmissionStatusChanged.onFileTransmitted(file); - } else { - connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); - } - } catch (Exception e) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": "+e.getMessage()); - FileBackend.close(fileOutputStream); - this.onFileTransmissionStatusChanged.onFileTransferAborted(); - } - } + @Override + public void send(DownloadableFile file, OnFileTransmissionStatusChanged callback) { + this.onFileTransmissionStatusChanged = callback; + this.file = file; + try { + this.remainingSize = this.file.getExpectedSize(); + this.fileSize = this.remainingSize; + this.digest = MessageDigest.getInstance("SHA-1"); + this.digest.reset(); + fileInputStream = connection.getFileInputStream(); + if (fileInputStream == null) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could no create input stream"); + callback.onFileTransferAborted(); + return; + } + innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream); + if (this.connected) { + this.sendNextBlock(); + } + } catch (Exception e) { + callback.onFileTransferAborted(); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + e.getMessage()); + } + } - public void deliverPayload(IqPacket packet, Element payload) { - if (payload.getName().equals("open")) { - if (!established) { - established = true; - connected = true; - this.receiveNextBlock(""); - this.account.getXmppConnection().sendIqPacket( - packet.generateResponse(IqPacket.TYPE.RESULT), null); - } else { - this.account.getXmppConnection().sendIqPacket( - packet.generateResponse(IqPacket.TYPE.ERROR), null); - } - } else if (connected && payload.getName().equals("data")) { - this.receiveNextBlock(payload.getContent()); - this.account.getXmppConnection().sendIqPacket( - packet.generateResponse(IqPacket.TYPE.RESULT), null); - } else if (connected && payload.getName().equals("close")) { - this.connected = false; - this.account.getXmppConnection().sendIqPacket( - packet.generateResponse(IqPacket.TYPE.RESULT), null); - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": received ibb close"); - } else { - Log.d(Config.LOGTAG,payload.toString()); - // TODO some sort of exception - } - } + @Override + public void disconnect() { + this.connected = false; + FileBackend.close(fileOutputStream); + FileBackend.close(fileInputStream); + } + + private void sendNextBlock() { + byte[] buffer = new byte[this.blockSize]; + try { + int count = innerInputStream.read(buffer); + if (count == -1) { + sendClose(); + file.setSha1Sum(digest.digest()); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sendNextBlock() count was -1"); + this.onFileTransmissionStatusChanged.onFileTransmitted(file); + fileInputStream.close(); + return; + } else if (count != buffer.length) { + int rem = innerInputStream.read(buffer, count, buffer.length - count); + if (rem > 0) { + count += rem; + } + } + this.remainingSize -= count; + this.digest.update(buffer, 0, count); + String base64 = Base64.encodeToString(buffer, 0, count, Base64.NO_WRAP); + IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + iq.setTo(this.counterpart); + Element data = iq.addChild("data", "http://jabber.org/protocol/ibb"); + data.setAttribute("seq", Integer.toString(this.seq)); + data.setAttribute("block-size", Integer.toString(this.blockSize)); + data.setAttribute("sid", this.sessionId); + data.setContent(base64); + this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived); + this.account.getXmppConnection().r(); //don't fill up stanza queue too much + this.seq++; + connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); + if (this.remainingSize <= 0) { + sendClose(); + file.setSha1Sum(digest.digest()); + this.onFileTransmissionStatusChanged.onFileTransmitted(file); + fileInputStream.close(); + } + } catch (IOException e) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": io exception during sendNextBlock() " + e.getMessage()); + FileBackend.close(fileInputStream); + this.onFileTransmissionStatusChanged.onFileTransferAborted(); + } + } + + private void receiveNextBlock(String data) { + try { + byte[] buffer = Base64.decode(data, Base64.NO_WRAP); + if (this.remainingSize < buffer.length) { + buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize); + } + this.remainingSize -= buffer.length; + this.fileOutputStream.write(buffer); + this.digest.update(buffer); + if (this.remainingSize <= 0) { + file.setSha1Sum(digest.digest()); + fileOutputStream.flush(); + fileOutputStream.close(); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": receive next block nothing remaining"); + this.onFileTransmissionStatusChanged.onFileTransmitted(file); + } else { + connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); + } + } catch (Exception e) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + e.getMessage()); + FileBackend.close(fileOutputStream); + this.onFileTransmissionStatusChanged.onFileTransferAborted(); + } + } + + public void deliverPayload(IqPacket packet, Element payload) { + if (payload.getName().equals("open")) { + if (!established) { + established = true; + connected = true; + this.receiveNextBlock(""); + this.account.getXmppConnection().sendIqPacket( + packet.generateResponse(IqPacket.TYPE.RESULT), null); + } else { + this.account.getXmppConnection().sendIqPacket( + packet.generateResponse(IqPacket.TYPE.ERROR), null); + } + } else if (connected && payload.getName().equals("data")) { + this.receiveNextBlock(payload.getContent()); + this.account.getXmppConnection().sendIqPacket( + packet.generateResponse(IqPacket.TYPE.RESULT), null); + } else if (connected && payload.getName().equals("close")) { + this.connected = false; + this.account.getXmppConnection().sendIqPacket( + packet.generateResponse(IqPacket.TYPE.RESULT), null); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received ibb close"); + } else { + Log.d(Config.LOGTAG, payload.toString()); + // TODO some sort of exception + } + } }