1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/** 2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * you may not use this file except in compliance with the License. 4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * You may obtain a copy of the License at 5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * http://www.apache.org/licenses/LICENSE-2.0 7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Unless required by applicable law or agreed to in writing, software 9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * distributed under the License is distributed on an "AS IS" BASIS, 10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * See the License for the specific language governing permissions and 12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * limitations under the License. 13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpackage org.jivesoftware.smackx.bytestreams.ibb; 15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.IOException; 17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.InputStream; 18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.OutputStream; 19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.net.SocketTimeoutException; 20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.BlockingQueue; 21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.LinkedBlockingQueue; 22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.TimeUnit; 23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.Connection; 25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.PacketListener; 26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.XMPPException; 27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.filter.AndFilter; 28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.filter.PacketFilter; 29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.filter.PacketTypeFilter; 30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.IQ; 31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.Message; 32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.Packet; 33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.PacketExtension; 34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.XMPPError; 35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.util.StringUtils; 36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.util.SyncPacketSend; 37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.BytestreamSession; 38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.ibb.packet.Close; 39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.ibb.packet.Data; 40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; 41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.ibb.packet.Open; 42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/** 44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * InBandBytestreamSession class represents an In-Band Bytestream session. 45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p> 46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * In-band bytestreams are bidirectional and this session encapsulates the streams for both 47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * directions. 48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p> 49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Note that closing the In-Band Bytestream session will close both streams. If both streams are 50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * closed individually the session will be closed automatically once the second stream is closed. 51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Use the {@link #setCloseBothStreamsEnabled(boolean)} method if both streams should be closed 52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * automatically if one of them is closed. 53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @author Henning Staib 55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpublic class InBandBytestreamSession implements BytestreamSession { 57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* XMPP connection */ 59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private final Connection connection; 60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* the In-Band Bytestream open request for this session */ 62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private final Open byteStreamRequest; 63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* 65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the input stream for this session (either IQIBBInputStream or MessageIBBInputStream) 66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private IBBInputStream inputStream; 68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* 70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the output stream for this session (either IQIBBOutputStream or MessageIBBOutputStream) 71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private IBBOutputStream outputStream; 73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* JID of the remote peer */ 75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private String remoteJID; 76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* flag to close both streams if one of them is closed */ 78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private boolean closeBothStreamsEnabled = false; 79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* flag to indicate if session is closed */ 81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private boolean isClosed = false; 82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Constructor. 85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param connection the XMPP connection 87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param byteStreamRequest the In-Band Bytestream open request for this session 88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param remoteJID JID of the remote peer 89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected InBandBytestreamSession(Connection connection, Open byteStreamRequest, 91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen String remoteJID) { 92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.connection = connection; 93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.byteStreamRequest = byteStreamRequest; 94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.remoteJID = remoteJID; 95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // initialize streams dependent to the uses stanza type 97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen switch (byteStreamRequest.getStanza()) { 98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen case IQ: 99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.inputStream = new IQIBBInputStream(); 100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.outputStream = new IQIBBOutputStream(); 101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen break; 102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen case MESSAGE: 103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.inputStream = new MessageIBBInputStream(); 104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.outputStream = new MessageIBBOutputStream(); 105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen break; 106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public InputStream getInputStream() { 111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return this.inputStream; 112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public OutputStream getOutputStream() { 115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return this.outputStream; 116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public int getReadTimeout() { 119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return this.inputStream.readTimeout; 120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void setReadTimeout(int timeout) { 123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (timeout < 0) { 124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IllegalArgumentException("Timeout must be >= 0"); 125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.inputStream.readTimeout = timeout; 127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns whether both streams should be closed automatically if one of the streams is closed. 131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Default is <code>false</code>. 132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return <code>true</code> if both streams will be closed if one of the streams is closed, 134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <code>false</code> if both streams can be closed independently. 135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public boolean isCloseBothStreamsEnabled() { 137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return closeBothStreamsEnabled; 138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Sets whether both streams should be closed automatically if one of the streams is closed. 142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Default is <code>false</code>. 143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param closeBothStreamsEnabled <code>true</code> if both streams should be closed if one of 145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the streams is closed, <code>false</code> if both streams should be closed 146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * independently 147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void setCloseBothStreamsEnabled(boolean closeBothStreamsEnabled) { 149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.closeBothStreamsEnabled = closeBothStreamsEnabled; 150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void close() throws IOException { 153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen closeByLocal(true); // close input stream 154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen closeByLocal(false); // close output stream 155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * This method is invoked if a request to close the In-Band Bytestream has been received. 159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param closeRequest the close request from the remote peer 161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected void closeByPeer(Close closeRequest) { 163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* 165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * close streams without flushing them, because stream is already considered closed on the 166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * remote peers side 167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.inputStream.closeInternal(); 169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.inputStream.cleanup(); 170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.outputStream.closeInternal(false); 171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // acknowledge close request 173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen IQ confirmClose = IQ.createResultIQ(closeRequest); 174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.connection.sendPacket(confirmClose); 175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * This method is invoked if one of the streams has been closed locally, if an error occurred 180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * locally or if the whole session should be closed. 181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws IOException if an error occurs while sending the close request 183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected synchronized void closeByLocal(boolean in) throws IOException { 185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.isClosed) { 186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.closeBothStreamsEnabled) { 190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.inputStream.closeInternal(); 191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.outputStream.closeInternal(true); 192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen else { 194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (in) { 195d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.inputStream.closeInternal(); 196d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 197d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen else { 198d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // close stream but try to send any data left 199d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.outputStream.closeInternal(true); 200d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 201d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 202d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 203d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.inputStream.isClosed && this.outputStream.isClosed) { 204d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.isClosed = true; 205d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 206d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // send close request 207d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Close close = new Close(this.byteStreamRequest.getSessionID()); 208d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen close.setTo(this.remoteJID); 209d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 210d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen SyncPacketSend.getReply(this.connection, close); 211d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 212d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (XMPPException e) { 213d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IOException("Error while closing stream: " + e.getMessage()); 214d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 215d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 216d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.inputStream.cleanup(); 217d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 218d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // remove session from manager 219d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen InBandBytestreamManager.getByteStreamManager(this.connection).getSessions().remove(this); 220d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 221d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 222d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 223d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 224d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 225d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * IBBInputStream class is the base implementation of an In-Band Bytestream input stream. 226d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Subclasses of this input stream must provide a packet listener along with a packet filter to 227d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * collect the In-Band Bytestream data packets. 228d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 229d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private abstract class IBBInputStream extends InputStream { 230d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 231d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* the data packet listener to fill the data queue */ 232d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private final PacketListener dataPacketListener; 233d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 234d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* queue containing received In-Band Bytestream data packets */ 235d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected final BlockingQueue<DataPacketExtension> dataQueue = new LinkedBlockingQueue<DataPacketExtension>(); 236d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 237d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* buffer containing the data from one data packet */ 238d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private byte[] buffer; 239d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 240d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* pointer to the next byte to read from buffer */ 241d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private int bufferPointer = -1; 242d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 243d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* data packet sequence (range from 0 to 65535) */ 244d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private long seq = -1; 245d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 246d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* flag to indicate if input stream is closed */ 247d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private boolean isClosed = false; 248d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 249d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* flag to indicate if close method was invoked */ 250d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private boolean closeInvoked = false; 251d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 252d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* timeout for read operations */ 253d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private int readTimeout = 0; 254d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 255d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 256d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Constructor. 257d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 258d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public IBBInputStream() { 259d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // add data packet listener to connection 260d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.dataPacketListener = getDataPacketListener(); 261d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.addPacketListener(this.dataPacketListener, getDataPacketFilter()); 262d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 263d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 264d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 265d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns the packet listener that processes In-Band Bytestream data packets. 266d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 267d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return the data packet listener 268d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 269d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected abstract PacketListener getDataPacketListener(); 270d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 271d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 272d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns the packet filter that accepts In-Band Bytestream data packets. 273d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 274d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return the data packet filter 275d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 276d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected abstract PacketFilter getDataPacketFilter(); 277d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 278d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public synchronized int read() throws IOException { 279d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen checkClosed(); 280d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 281d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // if nothing read yet or whole buffer has been read fill buffer 282d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (bufferPointer == -1 || bufferPointer >= buffer.length) { 283d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // if no data available and stream was closed return -1 284d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!loadBuffer()) { 285d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return -1; 286d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 287d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 288d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 289d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // return byte and increment buffer pointer 290d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return ((int) buffer[bufferPointer++]) & 0xff; 291d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 292d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 293d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public synchronized int read(byte[] b, int off, int len) throws IOException { 294d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (b == null) { 295d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new NullPointerException(); 296d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 297d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) 298d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen || ((off + len) < 0)) { 299d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IndexOutOfBoundsException(); 300d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 301d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen else if (len == 0) { 302d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return 0; 303d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 304d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 305d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen checkClosed(); 306d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 307d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // if nothing read yet or whole buffer has been read fill buffer 308d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (bufferPointer == -1 || bufferPointer >= buffer.length) { 309d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // if no data available and stream was closed return -1 310d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!loadBuffer()) { 311d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return -1; 312d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 313d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 314d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 315d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // if more bytes wanted than available return all available 316d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen int bytesAvailable = buffer.length - bufferPointer; 317d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (len > bytesAvailable) { 318d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen len = bytesAvailable; 319d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 320d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 321d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen System.arraycopy(buffer, bufferPointer, b, off, len); 322d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen bufferPointer += len; 323d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return len; 324d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 325d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 326d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public synchronized int read(byte[] b) throws IOException { 327d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return read(b, 0, b.length); 328d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 329d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 330d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 331d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * This method blocks until a data packet is received, the stream is closed or the current 332d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * thread is interrupted. 333d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 334d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return <code>true</code> if data was received, otherwise <code>false</code> 335d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws IOException if data packets are out of sequence 336d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 337d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private synchronized boolean loadBuffer() throws IOException { 338d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 339d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // wait until data is available or stream is closed 340d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataPacketExtension data = null; 341d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 342d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.readTimeout == 0) { 343d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen while (data == null) { 344d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (isClosed && this.dataQueue.isEmpty()) { 345d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 346d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 347d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen data = this.dataQueue.poll(1000, TimeUnit.MILLISECONDS); 348d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 349d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 350d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen else { 351d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen data = this.dataQueue.poll(this.readTimeout, TimeUnit.MILLISECONDS); 352d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (data == null) { 353d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new SocketTimeoutException(); 354d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 355d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 356d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 357d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (InterruptedException e) { 358d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Restore the interrupted status 359d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Thread.currentThread().interrupt(); 360d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 361d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 362d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 363d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // handle sequence overflow 364d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.seq == 65535) { 365d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.seq = -1; 366d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 367d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 368d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // check if data packets sequence is successor of last seen sequence 369d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen long seq = data.getSeq(); 370d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (seq - 1 != this.seq) { 371d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // packets out of order; close stream/session 372d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen InBandBytestreamSession.this.close(); 373d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IOException("Packets out of sequence"); 374d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 375d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen else { 376d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.seq = seq; 377d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 378d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 379d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // set buffer to decoded data 380d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen buffer = data.getDecodedData(); 381d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen bufferPointer = 0; 382d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return true; 383d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 384d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 385d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 386d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Checks if this stream is closed and throws an IOException if necessary 387d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 388d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws IOException if stream is closed and no data should be read anymore 389d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 390d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private void checkClosed() throws IOException { 391d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* throw no exception if there is data available, but not if close method was invoked */ 392d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if ((isClosed && this.dataQueue.isEmpty()) || closeInvoked) { 393d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // clear data queue in case additional data was received after stream was closed 394d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.dataQueue.clear(); 395d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IOException("Stream is closed"); 396d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 397d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 398d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 399d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public boolean markSupported() { 400d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 401d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 402d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 403d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void close() throws IOException { 404d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (isClosed) { 405d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 406d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 407d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 408d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.closeInvoked = true; 409d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 410d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen InBandBytestreamSession.this.closeByLocal(true); 411d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 412d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 413d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 414d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * This method sets the close flag and removes the data packet listener. 415d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 416d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private void closeInternal() { 417d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (isClosed) { 418d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 419d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 420d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen isClosed = true; 421d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 422d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 423d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 424d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Invoked if the session is closed. 425d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 426d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private void cleanup() { 427d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.removePacketListener(this.dataPacketListener); 428d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 429d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 430d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 431d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 432d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 433d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * IQIBBInputStream class implements IBBInputStream to be used with IQ stanzas encapsulating the 434d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * data packets. 435d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 436d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private class IQIBBInputStream extends IBBInputStream { 437d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 438d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected PacketListener getDataPacketListener() { 439d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return new PacketListener() { 440d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 441d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private long lastSequence = -1; 442d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 443d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void processPacket(Packet packet) { 444d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // get data packet extension 445d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataPacketExtension data = (DataPacketExtension) packet.getExtension( 446d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataPacketExtension.ELEMENT_NAME, 447d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen InBandBytestreamManager.NAMESPACE); 448d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 449d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* 450d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * check if sequence was not used already (see XEP-0047 Section 2.2) 451d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 452d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (data.getSeq() <= this.lastSequence) { 453d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen IQ unexpectedRequest = IQ.createErrorResponse((IQ) packet, new XMPPError( 454d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen XMPPError.Condition.unexpected_request)); 455d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.sendPacket(unexpectedRequest); 456d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 457d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 458d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 459d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 460d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // check if encoded data is valid (see XEP-0047 Section 2.2) 461d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (data.getDecodedData() == null) { 462d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // data is invalid; respond with bad-request error 463d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen IQ badRequest = IQ.createErrorResponse((IQ) packet, new XMPPError( 464d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen XMPPError.Condition.bad_request)); 465d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.sendPacket(badRequest); 466d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 467d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 468d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 469d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // data is valid; add to data queue 470d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen dataQueue.offer(data); 471d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 472d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // confirm IQ 473d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen IQ confirmData = IQ.createResultIQ((IQ) packet); 474d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.sendPacket(confirmData); 475d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 476d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // set last seen sequence 477d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.lastSequence = data.getSeq(); 478d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.lastSequence == 65535) { 479d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.lastSequence = -1; 480d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 481d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 482d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 483d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 484d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }; 485d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 486d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 487d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected PacketFilter getDataPacketFilter() { 488d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* 489d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * filter all IQ stanzas having type 'SET' (represented by Data class), containing a 490d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * data packet extension, matching session ID and recipient 491d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 492d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return new AndFilter(new PacketTypeFilter(Data.class), new IBBDataPacketFilter()); 493d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 494d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 495d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 496d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 497d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 498d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * MessageIBBInputStream class implements IBBInputStream to be used with message stanzas 499d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * encapsulating the data packets. 500d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 501d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private class MessageIBBInputStream extends IBBInputStream { 502d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 503d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected PacketListener getDataPacketListener() { 504d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return new PacketListener() { 505d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 506d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void processPacket(Packet packet) { 507d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // get data packet extension 508d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataPacketExtension data = (DataPacketExtension) packet.getExtension( 509d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataPacketExtension.ELEMENT_NAME, 510d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen InBandBytestreamManager.NAMESPACE); 511d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 512d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // check if encoded data is valid 513d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (data.getDecodedData() == null) { 514d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* 515d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * TODO once a majority of XMPP server implementation support XEP-0079 516d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Advanced Message Processing the invalid message could be answered with an 517d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * appropriate error. For now we just ignore the packet. Subsequent packets 518d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * with an increased sequence will cause the input stream to close the 519d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * stream/session. 520d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 521d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 522d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 523d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 524d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // data is valid; add to data queue 525d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen dataQueue.offer(data); 526d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 527d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // TODO confirm packet once XMPP servers support XEP-0079 528d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 529d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 530d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }; 531d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 532d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 533d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @Override 534d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected PacketFilter getDataPacketFilter() { 535d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* 536d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * filter all message stanzas containing a data packet extension, matching session ID 537d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * and recipient 538d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 539d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return new AndFilter(new PacketTypeFilter(Message.class), new IBBDataPacketFilter()); 540d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 541d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 542d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 543d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 544d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 545d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * IBBDataPacketFilter class filters all packets from the remote peer of this session, 546d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * containing an In-Band Bytestream data packet extension whose session ID matches this sessions 547d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * ID. 548d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 549d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private class IBBDataPacketFilter implements PacketFilter { 550d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 551d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public boolean accept(Packet packet) { 552d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // sender equals remote peer 553d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!packet.getFrom().equalsIgnoreCase(remoteJID)) { 554d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 555d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 556d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 557d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // stanza contains data packet extension 558d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen PacketExtension packetExtension = packet.getExtension(DataPacketExtension.ELEMENT_NAME, 559d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen InBandBytestreamManager.NAMESPACE); 560d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (packetExtension == null || !(packetExtension instanceof DataPacketExtension)) { 561d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 562d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 563d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 564d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // session ID equals this session ID 565d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataPacketExtension data = (DataPacketExtension) packetExtension; 566d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!data.getSessionID().equals(byteStreamRequest.getSessionID())) { 567d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 568d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 569d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 570d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return true; 571d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 572d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 573d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 574d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 575d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 576d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * IBBOutputStream class is the base implementation of an In-Band Bytestream output stream. 577d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Subclasses of this output stream must provide a method to send data over XMPP stream. 578d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 579d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private abstract class IBBOutputStream extends OutputStream { 580d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 581d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* buffer with the size of this sessions block size */ 582d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected final byte[] buffer; 583d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 584d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* pointer to next byte to write to buffer */ 585d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected int bufferPointer = 0; 586d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 587d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* data packet sequence (range from 0 to 65535) */ 588d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected long seq = 0; 589d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 590d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* flag to indicate if output stream is closed */ 591d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected boolean isClosed = false; 592d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 593d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 594d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Constructor. 595d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 596d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public IBBOutputStream() { 597d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.buffer = new byte[(byteStreamRequest.getBlockSize()/4)*3]; 598d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 599d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 600d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 601d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Writes the given data packet to the XMPP stream. 602d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 603d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param data the data packet 604d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws IOException if an I/O error occurred while sending or if the stream is closed 605d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 606d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected abstract void writeToXML(DataPacketExtension data) throws IOException; 607d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 608d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public synchronized void write(int b) throws IOException { 609d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.isClosed) { 610d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IOException("Stream is closed"); 611d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 612d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 613d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // if buffer is full flush buffer 614d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (bufferPointer >= buffer.length) { 615d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen flushBuffer(); 616d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 617d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 618d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen buffer[bufferPointer++] = (byte) b; 619d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 620d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 621d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public synchronized void write(byte b[], int off, int len) throws IOException { 622d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (b == null) { 623d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new NullPointerException(); 624d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 625d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) 626d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen || ((off + len) < 0)) { 627d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IndexOutOfBoundsException(); 628d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 629d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen else if (len == 0) { 630d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 631d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 632d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 633d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.isClosed) { 634d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IOException("Stream is closed"); 635d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 636d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 637d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // is data to send greater than buffer size 638d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (len >= buffer.length) { 639d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 640d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // "byte" off the first chunk to write out 641d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writeOut(b, off, buffer.length); 642d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 643d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // recursively call this method with the lesser amount 644d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen write(b, off + buffer.length, len - buffer.length); 645d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 646d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen else { 647d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writeOut(b, off, len); 648d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 649d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 650d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 651d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public synchronized void write(byte[] b) throws IOException { 652d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen write(b, 0, b.length); 653d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 654d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 655d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 656d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Fills the buffer with the given data and sends it over the XMPP stream if the buffers 657d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * capacity has been reached. This method is only called from this class so it is assured 658d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * that the amount of data to send is <= buffer capacity 659d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 660d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param b the data 661d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param off the data 662d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param len the number of bytes to write 663d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws IOException if an I/O error occurred while sending or if the stream is closed 664d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 665d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private synchronized void writeOut(byte b[], int off, int len) throws IOException { 666d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.isClosed) { 667d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IOException("Stream is closed"); 668d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 669d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 670d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // set to 0 in case the next 'if' block is not executed 671d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen int available = 0; 672d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 673d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // is data to send greater that buffer space left 674d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (len > buffer.length - bufferPointer) { 675d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // fill buffer to capacity and send it 676d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen available = buffer.length - bufferPointer; 677d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen System.arraycopy(b, off, buffer, bufferPointer, available); 678d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen bufferPointer += available; 679d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen flushBuffer(); 680d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 681d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 682d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // copy the data left to buffer 683d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen System.arraycopy(b, off + available, buffer, bufferPointer, len - available); 684d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen bufferPointer += len - available; 685d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 686d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 687d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public synchronized void flush() throws IOException { 688d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.isClosed) { 689d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IOException("Stream is closed"); 690d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 691d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen flushBuffer(); 692d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 693d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 694d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private synchronized void flushBuffer() throws IOException { 695d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 696d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // do nothing if no data to send available 697d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (bufferPointer == 0) { 698d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 699d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 700d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 701d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // create data packet 702d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen String enc = StringUtils.encodeBase64(buffer, 0, bufferPointer, false); 703d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataPacketExtension data = new DataPacketExtension(byteStreamRequest.getSessionID(), 704d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.seq, enc); 705d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 706d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // write to XMPP stream 707d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writeToXML(data); 708d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 709d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // reset buffer pointer 710d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen bufferPointer = 0; 711d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 712d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // increment sequence, considering sequence overflow 713d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.seq = (this.seq + 1 == 65535 ? 0 : this.seq + 1); 714d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 715d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 716d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 717d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void close() throws IOException { 718d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (isClosed) { 719d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 720d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 721d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen InBandBytestreamSession.this.closeByLocal(false); 722d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 723d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 724d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 725d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Sets the close flag and optionally flushes the stream. 726d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 727d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param flush if <code>true</code> flushes the stream 728d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 729d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected void closeInternal(boolean flush) { 730d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.isClosed) { 731d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 732d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 733d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.isClosed = true; 734d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 735d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 736d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (flush) { 737d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen flushBuffer(); 738d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 739d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 740d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (IOException e) { 741d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* 742d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * ignore, because writeToXML() will not throw an exception if stream is already 743d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * closed 744d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 745d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 746d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 747d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 748d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 749d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 750d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 751d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * IQIBBOutputStream class implements IBBOutputStream to be used with IQ stanzas encapsulating 752d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the data packets. 753d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 754d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private class IQIBBOutputStream extends IBBOutputStream { 755d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 756d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @Override 757d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected synchronized void writeToXML(DataPacketExtension data) throws IOException { 758d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // create IQ stanza containing data packet 759d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen IQ iq = new Data(data); 760d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen iq.setTo(remoteJID); 761d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 762d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 763d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen SyncPacketSend.getReply(connection, iq); 764d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 765d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (XMPPException e) { 766d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // close session unless it is already closed 767d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!this.isClosed) { 768d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen InBandBytestreamSession.this.close(); 769d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IOException("Error while sending Data: " + e.getMessage()); 770d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 771d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 772d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 773d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 774d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 775d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 776d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 777d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 778d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * MessageIBBOutputStream class implements IBBOutputStream to be used with message stanzas 779d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * encapsulating the data packets. 780d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 781d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private class MessageIBBOutputStream extends IBBOutputStream { 782d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 783d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @Override 784d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected synchronized void writeToXML(DataPacketExtension data) { 785d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // create message stanza containing data packet 786d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Message message = new Message(remoteJID); 787d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen message.addExtension(data); 788d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 789d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.sendPacket(message); 790d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 791d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 792d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 793d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 794d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 795d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen} 796