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