1 /*
2 * SSHTools - Java SSH2 API
3 *
4 * Copyright (C) 2002-2003 Lee David Painter and Contributors.
5 *
6 * Contributions made by:
7 *
8 * Brett Smith
9 * Richard Pernavas
10 * Erwin Bolwidt
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 */
26 package com.sshtools.j2ssh.connection;
27
28 import com.sshtools.j2ssh.io.IOStreamConnector;
29 import com.sshtools.j2ssh.transport.MessageNotAvailableException;
30 import com.sshtools.j2ssh.transport.MessageStoreEOFException;
31 import com.sshtools.j2ssh.transport.SshMessageStore;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.OutputStream;
39
40
41 /**
42 *
43 *
44 * @author $author$
45 * @version $Revision: 1.16 $
46 */
47 public abstract class IOChannel extends Channel {
48 private static Log log = LogFactory.getLog(IOChannel.class);
49
50 /** */
51 private SshMessageStore incoming = new SshMessageStore();
52
53 /** */
54 protected ChannelInputStream in;
55
56 /** */
57 protected ChannelOutputStream out;
58
59 /** */
60 protected InputStream boundInputStream = null;
61
62 /** */
63 protected OutputStream boundOutputStream = null;
64
65 //protected IOChannel boundIOChannel = null;
66
67 /** */
68 protected IOStreamConnector ios = null;
69
70 /**
71 *
72 *
73 * @param connection
74 * @param localChannelId
75 * @param senderChannelId
76 * @param initialWindowSize
77 * @param maximumPacketSize
78 *
79 * @throws IOException
80 */
81 protected void init(ConnectionProtocol connection, long localChannelId,
82 long senderChannelId, long initialWindowSize, long maximumPacketSize)
83 throws IOException {
84 this.in = new ChannelInputStream(incoming); //ChannelInputStream.createStandard(incoming);
85 this.out = new ChannelOutputStream(this);
86 super.init(connection, localChannelId, senderChannelId,
87 initialWindowSize, maximumPacketSize);
88 }
89
90 /**
91 *
92 *
93 * @throws IOException
94 */
95 protected void open() throws IOException {
96 super.open();
97
98 // If were bound send any outstanding messages sitting around
99 if (boundOutputStream != null) {
100 sendOutstandingMessages();
101 }
102
103 // Start the bound inputstream
104 if ((boundInputStream != null) && (ios == null)) {
105 ios.setCloseInput(false);
106 ios.setCloseOutput(false);
107 ios.connect(boundInputStream, out);
108 }
109 }
110
111 /**
112 *
113 *
114 * @return
115 */
116 public ChannelInputStream getInputStream() {
117 return in;
118 }
119
120 /**
121 *
122 *
123 * @return
124 */
125 public ChannelOutputStream getOutputStream() {
126 return out;
127 }
128
129 /**
130 *
131 *
132 * @param msg
133 *
134 * @throws IOException
135 */
136 protected void onChannelData(SshMsgChannelData msg)
137 throws IOException {
138 // Synchronize on the message store to ensure that another thread
139 // does not try to read its data. This will make sure that the incoming
140 // messages are not being flushed to an outputstream after a bind
141 synchronized (incoming) {
142 if (boundOutputStream != null) {
143 try {
144 boundOutputStream.write(msg.getChannelData());
145 } catch (IOException ex) {
146 log.info(
147 "Could not route data to the bound OutputStream; Closing channel.");
148 log.info(ex.getMessage());
149 close();
150 }
151 } else {
152 incoming.addMessage(msg);
153 }
154 }
155 }
156
157 /**
158 *
159 *
160 * @throws IOException
161 */
162 public void setLocalEOF() throws IOException {
163 super.setLocalEOF();
164
165 if (!out.isClosed()) {
166 out.close();
167 }
168 }
169
170 /**
171 *
172 *
173 * @throws IOException
174 */
175 protected void onChannelEOF() throws IOException {
176 if (!in.isClosed()) {
177 in.close();
178 }
179 }
180
181 /**
182 *
183 *
184 * @throws IOException
185 */
186 protected void onChannelClose() throws IOException {
187 // Close the input/output streams
188 if (!in.isClosed()) {
189 in.close();
190 }
191
192 if (!out.isClosed()) {
193 out.close();
194 }
195
196 // Close the bound channel
197
198 /* if(boundIOChannel!=null && !boundIOChannel.isClosed())
199 boundIOChannel.close();*/
200
201 // Close the IOStream connector if were bound
202 if (ios != null) {
203 ios.close();
204 }
205 }
206
207 /**
208 *
209 *
210 * @param msg
211 *
212 * @throws IOException
213 */
214 protected void onChannelExtData(SshMsgChannelExtendedData msg)
215 throws IOException {
216 // This class will not deal with extended data
217 // incoming.addMessage(msg);
218 }
219
220 /*public void bindIOChannel(IOChannel boundIOChannel) throws IOException {
221 this.boundIOChannel = boundIOChannel;
222 // If the bound channel is open then bind the outputstreams
223 if (boundIOChannel.getState().getValue() == ChannelState.CHANNEL_OPEN) {
224 throw new IOException("You cannot bind to an open channel");
225 }
226 // Create an event listener so we can listen
227 boundIOChannel.addEventListener(new ChannelEventListener() {
228 public void onChannelOpen(Channel channel) {
229 try {
230 bindOutputStream(IOChannel.this.boundIOChannel.getOutputStream());
231 IOChannel.this.boundIOChannel.bindOutputStream(getOutputStream());
232 }
233 catch (IOException ex) {
234 log.info("Failed to bind the channel");
235 }
236 }
237 public void onChannelEOF(Channel channel) {
238 try {
239 //setLocalEOF();
240 close();
241 }
242 catch (IOException ex) {
243 log.info("Failed to set the channel to EOF");
244 }
245 }
246 public void onChannelClose(Channel channel) {
247 try {
248 if(!isClosed())
249 close();
250 }
251 catch (IOException ex) {
252 log.info("Failed to close the channel");
253 }
254 }
255 public void onDataReceived(Channel channel, byte[] data) {
256 }
257 public void onDataSent(Channel channel, byte[] data) {
258 }
259 });
260 }*/
261 public void bindOutputStream(OutputStream boundOutputStream)
262 throws IOException {
263 // Synchronize on the incoming message store to ensure that no other
264 // messages are added whilst we transfer to a bound state
265 synchronized (incoming) {
266 this.boundOutputStream = boundOutputStream;
267
268 if (state.getValue() == ChannelState.CHANNEL_OPEN) {
269 sendOutstandingMessages();
270 }
271 }
272 }
273
274 /**
275 *
276 *
277 * @param boundInputStream
278 *
279 * @throws IOException
280 */
281 public void bindInputStream(InputStream boundInputStream)
282 throws IOException {
283 this.boundInputStream = boundInputStream;
284 this.ios = new IOStreamConnector();
285
286 if (state.getValue() == ChannelState.CHANNEL_OPEN) {
287 ios.setCloseInput(false);
288 ios.setCloseOutput(false);
289 ios.connect(boundInputStream, out);
290 }
291 }
292
293 private void sendOutstandingMessages() throws IOException {
294 if ((boundInputStream != null) && (boundOutputStream != null) &&
295 incoming.hasMessages()) {
296 while (true) {
297 try {
298 // Peek into the message store and look for the next message
299 SshMsgChannelData msg = (SshMsgChannelData) incoming.peekMessage(SshMsgChannelData.SSH_MSG_CHANNEL_DATA);
300
301 // Remove the message so we dont process again
302 incoming.removeMessage(msg);
303
304 // Write the message out to the bound OutputStream
305 try {
306 boundOutputStream.write(msg.getChannelData());
307 } catch (IOException ex1) {
308 //log.info("Could not write outstanding messages to the bound OutputStream: " +ex1.getMessage());
309 close();
310 }
311 } catch (MessageStoreEOFException ex) {
312 break;
313 } catch (MessageNotAvailableException ex) {
314 break;
315 } catch (InterruptedException ex) {
316 throw new IOException("The thread was interrupted");
317 }
318 }
319 }
320 }
321 }