Logo Search packages:      
Sourcecode: jenkins-trilead-ssh2 version File versions  Download package

ChannelManager.java

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.HashMap;
import java.util.Vector;

import com.trilead.ssh2.ChannelCondition;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketChannelOpenConfirmation;
import com.trilead.ssh2.packets.PacketChannelOpenFailure;
import com.trilead.ssh2.packets.PacketChannelTrileadPing;
import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest;
import com.trilead.ssh2.packets.PacketGlobalForwardRequest;
import com.trilead.ssh2.packets.PacketGlobalTrileadPing;
import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel;
import com.trilead.ssh2.packets.PacketOpenSessionChannel;
import com.trilead.ssh2.packets.PacketSessionExecCommand;
import com.trilead.ssh2.packets.PacketSessionPtyRequest;
import com.trilead.ssh2.packets.PacketSessionStartShell;
import com.trilead.ssh2.packets.PacketSessionSubsystemRequest;
import com.trilead.ssh2.packets.PacketSessionX11Request;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.packets.TypesReader;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;

/**
 * ChannelManager. Please read the comments in Channel.java.
 * <p>
 * Besides the crypto part, this is the core of the library.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: ChannelManager.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
 */
00037 public class ChannelManager implements MessageHandler
{
      private static final Logger log = Logger.getLogger(ChannelManager.class);

      private HashMap x11_magic_cookies = new HashMap();

      private TransportManager tm;

      private Vector channels = new Vector();
      private int nextLocalChannel = 100;
      private boolean shutdown = false;
      private int globalSuccessCounter = 0;
      private int globalFailedCounter = 0;

      private HashMap remoteForwardings = new HashMap();

      private Vector listenerThreads = new Vector();

      private boolean listenerThreadsAllowed = true;

      public ChannelManager(TransportManager tm)
      {
            this.tm = tm;
            tm.registerMessageHandler(this, 80, 100);
      }

      private Channel getChannel(int id)
      {
            synchronized (channels)
            {
                  for (int i = 0; i < channels.size(); i++)
                  {
                        Channel c = (Channel) channels.elementAt(i);
                        if (c.localID == id)
                              return c;
                  }
            }
            return null;
      }

      private void removeChannel(int id)
      {
            synchronized (channels)
            {
                  for (int i = 0; i < channels.size(); i++)
                  {
                        Channel c = (Channel) channels.elementAt(i);
                        if (c.localID == id)
                        {
                              channels.removeElementAt(i);
                              break;
                        }
                  }
            }
      }

      private int addChannel(Channel c)
      {
            synchronized (channels)
            {
                  channels.addElement(c);
                  return nextLocalChannel++;
            }
      }

      private void waitUntilChannelOpen(Channel c) throws IOException
      {
            synchronized (c)
            {
                  while (c.state == Channel.STATE_OPENING)
                  {
                        try
                        {
                              c.wait();
                        }
                        catch (InterruptedException ignore)
                        {
                    throw new InterruptedIOException();
                        }
                  }

                  if (c.state != Channel.STATE_OPEN)
                  {
                        removeChannel(c.localID);

                        String detail = c.getReasonClosed();

                        if (detail == null)
                              detail = "state: " + c.state;

                        throw new IOException("Could not open channel (" + detail + ")");
                  }
            }
      }

      private final boolean waitForGlobalRequestResult() throws IOException
      {
            synchronized (channels)
            {
                  while ((globalSuccessCounter == 0) && (globalFailedCounter == 0))
                  {
                        if (shutdown)
                        {
                              throw new IOException("The connection is being shutdown");
                        }

                        try
                        {
                              channels.wait();
                        }
                        catch (InterruptedException ignore)
                        {
                    throw new InterruptedIOException();
                        }
                  }

                  if ((globalFailedCounter == 0) && (globalSuccessCounter == 1))
                        return true;

                  if ((globalFailedCounter == 1) && (globalSuccessCounter == 0))
                        return false;

                  throw new IOException("Illegal state. The server sent " + globalSuccessCounter
                              + " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages.");
            }
      }

      private final boolean waitForChannelRequestResult(Channel c) throws IOException
      {
            synchronized (c)
            {
                  while ((c.successCounter == 0) && (c.failedCounter == 0))
                  {
                        if (c.state != Channel.STATE_OPEN)
                        {
                              String detail = c.getReasonClosed();

                              if (detail == null)
                                    detail = "state: " + c.state;

                              throw new IOException("This SSH2 channel is not open (" + detail + ")");
                        }

                        try
                        {
                              c.wait();
                        }
                        catch (InterruptedException ignore)
                        {
                    throw new InterruptedIOException();
                        }
                  }

                  if ((c.failedCounter == 0) && (c.successCounter == 1))
                        return true;

                  if ((c.failedCounter == 1) && (c.successCounter == 0))
                        return false;

                  throw new IOException("Illegal state. The server sent " + c.successCounter
                              + " SSH_MSG_CHANNEL_SUCCESS and " + c.failedCounter + " SSH_MSG_CHANNEL_FAILURE messages.");
            }
      }

      public void registerX11Cookie(String hexFakeCookie, X11ServerData data)
      {
            synchronized (x11_magic_cookies)
            {
                  x11_magic_cookies.put(hexFakeCookie, data);
            }
      }

      public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels)
      {
            if (hexFakeCookie == null)
                  throw new IllegalStateException("hexFakeCookie may not be null");

            synchronized (x11_magic_cookies)
            {
                  x11_magic_cookies.remove(hexFakeCookie);
            }

            if (killChannels == false)
                  return;

            if (log.isEnabled())
                  log.log(50, "Closing all X11 channels for the given fake cookie");

            Vector channel_copy;

            synchronized (channels)
            {
                  channel_copy = (Vector) channels.clone();
            }

            for (int i = 0; i < channel_copy.size(); i++)
            {
                  Channel c = (Channel) channel_copy.elementAt(i);

                  synchronized (c)
                  {
                        if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
                              continue;
                  }

                  try
                  {
                        closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
                  }
                  catch (IOException e)
                  {
                  }
            }
      }

      public X11ServerData checkX11Cookie(String hexFakeCookie)
      {
            synchronized (x11_magic_cookies)
            {
                  if (hexFakeCookie != null)
                        return (X11ServerData) x11_magic_cookies.get(hexFakeCookie);
            }
            return null;
      }

      public void closeAllChannels()
      {
            if (log.isEnabled())
                  log.log(50, "Closing all channels");

            Vector channel_copy;

            synchronized (channels)
            {
                  channel_copy = (Vector) channels.clone();
            }

            for (int i = 0; i < channel_copy.size(); i++)
            {
                  Channel c = (Channel) channel_copy.elementAt(i);
                  try
                  {
                        closeChannel(c, "Closing all channels", true);
                  }
                  catch (IOException e)
                  {
                  }
            }
      }

      public void closeChannel(Channel c, String reason, boolean force) throws IOException
      {
            byte msg[] = new byte[5];

            synchronized (c)
            {
                  if (force)
                  {
                        c.state = Channel.STATE_CLOSED;
                        c.EOF = true;
                  }

                  c.setReasonClosed(reason);

                  msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
                  msg[1] = (byte) (c.remoteID >> 24);
                  msg[2] = (byte) (c.remoteID >> 16);
                  msg[3] = (byte) (c.remoteID >> 8);
                  msg[4] = (byte) (c.remoteID);

                  c.notifyAll();
            }

            synchronized (c.channelSendLock)
            {
                  if (c.closeMessageSent == true)
                        return;
                  tm.sendMessage(msg);
                  c.closeMessageSent = true;
            }

            if (log.isEnabled())
                  log.log(50, "Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
      }

      public void sendEOF(Channel c) throws IOException
      {
            byte[] msg = new byte[5];

            synchronized (c)
            {
                  if (c.state != Channel.STATE_OPEN)
                        return;

                  msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
                  msg[1] = (byte) (c.remoteID >> 24);
                  msg[2] = (byte) (c.remoteID >> 16);
                  msg[3] = (byte) (c.remoteID >> 8);
                  msg[4] = (byte) (c.remoteID);
            }

            synchronized (c.channelSendLock)
            {
                  if (c.closeMessageSent == true)
                        return;
                  tm.sendMessage(msg);
            }

            if (log.isEnabled())
                  log.log(50, "Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
      }

      public void sendOpenConfirmation(Channel c) throws IOException
      {
            PacketChannelOpenConfirmation pcoc = null;

            synchronized (c)
            {
                  if (c.state != Channel.STATE_OPENING)
                        return;

                  c.state = Channel.STATE_OPEN;

                  pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
            }

            synchronized (c.channelSendLock)
            {
                  if (c.closeMessageSent == true)
                        return;
                  tm.sendMessage(pcoc.getPayload());
            }
      }

      public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException
      {
            while (len > 0)
            {
                  int thislen = 0;
                  byte[] msg;

                  synchronized (c)
                  {
                        while (true)
                        {
                              if (c.state == Channel.STATE_CLOSED)
                                    throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")");

                              if (c.state != Channel.STATE_OPEN)
                                    throw new IOException("SSH channel in strange state. (" + c.state + ")");

                              if (c.remoteWindow != 0)
                                    break;

                              try
                              {
                                    c.wait();
                              }
                              catch (InterruptedException ignore)
                              {
                        throw new InterruptedIOException();
                              }
                        }

                        /* len > 0, no sign extension can happen when comparing */

                        thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;

                        int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);

                        /* The worst case scenario =) a true bottleneck */

                        if (estimatedMaxDataLen <= 0)
                        {
                              estimatedMaxDataLen = 1;
                        }

                        if (thislen > estimatedMaxDataLen)
                              thislen = estimatedMaxDataLen;

                        c.remoteWindow -= thislen;

                        msg = new byte[1 + 8 + thislen];

                        msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
                        msg[1] = (byte) (c.remoteID >> 24);
                        msg[2] = (byte) (c.remoteID >> 16);
                        msg[3] = (byte) (c.remoteID >> 8);
                        msg[4] = (byte) (c.remoteID);
                        msg[5] = (byte) (thislen >> 24);
                        msg[6] = (byte) (thislen >> 16);
                        msg[7] = (byte) (thislen >> 8);
                        msg[8] = (byte) (thislen);

                        System.arraycopy(buffer, pos, msg, 9, thislen);
                  }

                  synchronized (c.channelSendLock)
                  {
                        if (c.closeMessageSent == true)
                              throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")");

                        tm.sendMessage(msg);
                  }

                  pos += thislen;
                  len -= thislen;
            }
      }

      public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
                  throws IOException
      {
            RemoteForwardingData rfd = new RemoteForwardingData();

            rfd.bindAddress = bindAddress;
            rfd.bindPort = bindPort;
            rfd.targetAddress = targetAddress;
            rfd.targetPort = targetPort;

            synchronized (remoteForwardings)
            {
                  Integer key = new Integer(bindPort);

                  if (remoteForwardings.get(key) != null)
                  {
                        throw new IOException("There is already a forwarding for remote port " + bindPort);
                  }

                  remoteForwardings.put(key, rfd);
            }

            synchronized (channels)
            {
                  globalSuccessCounter = globalFailedCounter = 0;
            }

            PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
            tm.sendMessage(pgf.getPayload());

            if (log.isEnabled())
                  log.log(50, "Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");

            try
            {
                  if (waitForGlobalRequestResult() == false)
                        throw new IOException("The server denied the request (did you enable port forwarding?)");
            }
            catch (IOException e)
            {
                  synchronized (remoteForwardings)
                  {
                        remoteForwardings.remove(rfd);
                  }
                  throw e;
            }

            return bindPort;
      }

      public void requestCancelGlobalForward(int bindPort) throws IOException
      {
            RemoteForwardingData rfd = null;

            synchronized (remoteForwardings)
            {
                  rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(bindPort));

                  if (rfd == null)
                        throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
            }

            synchronized (channels)
            {
                  globalSuccessCounter = globalFailedCounter = 0;
            }

            PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
                        rfd.bindPort);
            tm.sendMessage(pgcf.getPayload());

            if (log.isEnabled())
                  log.log(50, "Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");

            try
            {
                  if (waitForGlobalRequestResult() == false)
                        throw new IOException("The server denied the request.");
            }
            finally
            {
                  synchronized (remoteForwardings)
                  {
                        /* Only now we are sure that no more forwarded connections will arrive */
                        remoteForwardings.remove(rfd);
                  }
            }

      }

      public void registerThread(IChannelWorkerThread thr) throws IOException
      {
            synchronized (listenerThreads)
            {
                  if (listenerThreadsAllowed == false)
                        throw new IOException("Too late, this connection is closed.");
                  listenerThreads.addElement(thr);
            }
      }

      public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
                  int originator_port) throws IOException
      {
            Channel c = new Channel(this);

            synchronized (c)
            {
                  c.localID = addChannel(c);
                  // end of synchronized block forces writing out to main memory
            }

            PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
                        c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);

            tm.sendMessage(dtc.getPayload());

            waitUntilChannelOpen(c);

            return c;
      }

      public Channel openSessionChannel() throws IOException
      {
            Channel c = new Channel(this);

            synchronized (c)
            {
                  c.localID = addChannel(c);
                  // end of synchronized block forces the writing out to main memory
            }

            if (log.isEnabled())
                  log.log(50, "Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");

            PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
            tm.sendMessage(smo.getPayload());

            waitUntilChannelOpen(c);

            return c;
      }

      public void requestGlobalTrileadPing() throws IOException
      {
            synchronized (channels)
            {
                  globalSuccessCounter = globalFailedCounter = 0;
            }

            PacketGlobalTrileadPing pgtp = new PacketGlobalTrileadPing();

            tm.sendMessage(pgtp.getPayload());

            if (log.isEnabled())
                  log.log(50, "Sending SSH_MSG_GLOBAL_REQUEST 'trilead-ping'.");

            try
            {
                  if (waitForGlobalRequestResult() == true)
                        throw new IOException("Your server is alive - but buggy. "
                                    + "It replied with SSH_MSG_REQUEST_SUCCESS when it actually should not.");

            }
            catch (IOException e)
            {
                  throw (IOException) new IOException("The ping request failed.").initCause(e);
            }
      }

      public void requestChannelTrileadPing(Channel c) throws IOException
      {
            PacketChannelTrileadPing pctp;

            synchronized (c)
            {
                  if (c.state != Channel.STATE_OPEN)
                        throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");

                  pctp = new PacketChannelTrileadPing(c.remoteID);

                  c.successCounter = c.failedCounter = 0;
            }

            synchronized (c.channelSendLock)
            {
                  if (c.closeMessageSent)
                        throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
                  tm.sendMessage(pctp.getPayload());
            }

            try
            {
                  if (waitForChannelRequestResult(c) == true)
                        throw new IOException("Your server is alive - but buggy. "
                                    + "It replied with SSH_MSG_SESSION_SUCCESS when it actually should not.");

            }
            catch (IOException e)
            {
                  throw (IOException) new IOException("The ping request failed.").initCause(e);
            }
      }

      public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
                  int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
      {
            PacketSessionPtyRequest spr;

            synchronized (c)
            {
                  if (c.state != Channel.STATE_OPEN)
                        throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");

                  spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
                              term_width_pixels, term_height_pixels, terminal_modes);

                  c.successCounter = c.failedCounter = 0;
            }

            synchronized (c.channelSendLock)
            {
                  if (c.closeMessageSent)
                        throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
                  tm.sendMessage(spr.getPayload());
            }

            try
            {
                  if (waitForChannelRequestResult(c) == false)
                        throw new IOException("The server denied the request.");
            }
            catch (IOException e)
            {
                  throw (IOException) new IOException("PTY request failed").initCause(e);
            }
      }

      public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
                  String x11AuthenticationCookie, int x11ScreenNumber) throws IOException
      {
            PacketSessionX11Request psr;

            synchronized (c)
            {
                  if (c.state != Channel.STATE_OPEN)
                        throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");

                  psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
                              x11AuthenticationCookie, x11ScreenNumber);

                  c.successCounter = c.failedCounter = 0;
            }

            synchronized (c.channelSendLock)
            {
                  if (c.closeMessageSent)
                        throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
                  tm.sendMessage(psr.getPayload());
            }

            if (log.isEnabled())
                  log.log(50, "Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");

            try
            {
                  if (waitForChannelRequestResult(c) == false)
                        throw new IOException("The server denied the request.");
            }
            catch (IOException e)
            {
                  throw (IOException) new IOException("The X11 request failed.").initCause(e);
            }
      }

      public void requestSubSystem(Channel c, String subSystemName) throws IOException
      {
            PacketSessionSubsystemRequest ssr;

            synchronized (c)
            {
                  if (c.state != Channel.STATE_OPEN)
                        throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");

                  ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);

                  c.successCounter = c.failedCounter = 0;
            }

            synchronized (c.channelSendLock)
            {
                  if (c.closeMessageSent)
                        throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
                  tm.sendMessage(ssr.getPayload());
            }

            try
            {
                  if (waitForChannelRequestResult(c) == false)
                        throw new IOException("The server denied the request.");
            }
            catch (IOException e)
            {
                  throw (IOException) new IOException("The subsystem request failed.").initCause(e);
            }
      }

      public void requestExecCommand(Channel c, String cmd) throws IOException
      {
            PacketSessionExecCommand sm;

            synchronized (c)
            {
                  if (c.state != Channel.STATE_OPEN)
                        throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");

                  sm = new PacketSessionExecCommand(c.remoteID, true, cmd);

                  c.successCounter = c.failedCounter = 0;
            }

            synchronized (c.channelSendLock)
            {
                  if (c.closeMessageSent)
                        throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
                  tm.sendMessage(sm.getPayload());
            }

            if (log.isEnabled())
                  log.log(50, "Executing command (channel " + c.localID + ", '" + cmd + "')");

            try
            {
                  if (waitForChannelRequestResult(c) == false)
                        throw new IOException("The server denied the request.");
            }
            catch (IOException e)
            {
                  throw (IOException) new IOException("The execute request failed.").initCause(e);
            }
      }

      public void requestShell(Channel c) throws IOException
      {
            PacketSessionStartShell sm;

            synchronized (c)
            {
                  if (c.state != Channel.STATE_OPEN)
                        throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");

                  sm = new PacketSessionStartShell(c.remoteID, true);

                  c.successCounter = c.failedCounter = 0;
            }

            synchronized (c.channelSendLock)
            {
                  if (c.closeMessageSent)
                        throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
                  tm.sendMessage(sm.getPayload());
            }

            try
            {
                  if (waitForChannelRequestResult(c) == false)
                        throw new IOException("The server denied the request.");
            }
            catch (IOException e)
            {
                  throw (IOException) new IOException("The shell request failed.").initCause(e);
            }
      }

      public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException
      {
            if (msglen <= 13)
                  throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")");

            int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
            int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
            int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);

            Channel c = getChannel(id);

            if (c == null)
                  throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);

            if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
                  throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");

            if (len != (msglen - 13))
                  throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13)
                              + ", got " + len + ")");

            if (log.isEnabled())
                  log.log(80, "Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");

            synchronized (c)
            {
                  if (c.state == Channel.STATE_CLOSED)
                        return; // ignore

                  if (c.state != Channel.STATE_OPEN)
                        throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
                                    + c.state + ")");

                  if (c.localWindow < len)
                        throw new IOException("Remote sent too much data, does not fit into window.");

                  c.localWindow -= len;

                  System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
                  c.stderrWritepos += len;

                  c.notifyAll();
            }
      }

      /**
       * Wait until for a condition.
       * 
       * @param c
       *            Channel
       * @param timeout
       *            in ms, 0 means no timeout.
       * @param condition_mask
       *            minimum event mask
       * @return all current events
       * 
       */
00877       public int waitForCondition(Channel c, long timeout, int condition_mask) throws InterruptedException {
            long end_time = 0;
            boolean end_time_set = false;

            synchronized (c)
            {
                  while (true)
                  {
                        int current_cond = 0;

                        int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
                        int stderrAvail = c.stderrWritepos - c.stderrReadpos;

                        if (stdoutAvail > 0)
                              current_cond = current_cond | ChannelCondition.STDOUT_DATA;

                        if (stderrAvail > 0)
                              current_cond = current_cond | ChannelCondition.STDERR_DATA;

                        if (c.EOF)
                              current_cond = current_cond | ChannelCondition.EOF;

                        if (c.getExitStatus() != null)
                              current_cond = current_cond | ChannelCondition.EXIT_STATUS;

                        if (c.getExitSignal() != null)
                              current_cond = current_cond | ChannelCondition.EXIT_SIGNAL;

                        if (c.state == Channel.STATE_CLOSED)
                              return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF;

                        if ((current_cond & condition_mask) != 0)
                              return current_cond;

                        if (timeout > 0)
                        {
                              if (!end_time_set)
                              {
                                    end_time = System.currentTimeMillis() + timeout;
                                    end_time_set = true;
                              }
                              else
                              {
                                    timeout = end_time - System.currentTimeMillis();

                                    if (timeout <= 0)
                                          return current_cond | ChannelCondition.TIMEOUT;
                              }
                        }

                if (timeout > 0)
                    c.wait(timeout);
                else
                    c.wait();
                  }
            }
      }

      public int getAvailable(Channel c, boolean extended) throws IOException
      {
            synchronized (c)
            {
                  int avail;

                  if (extended)
                        avail = c.stderrWritepos - c.stderrReadpos;
                  else
                        avail = c.stdoutWritepos - c.stdoutReadpos;

                  return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
            }
      }

      public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException
      {
            int copylen = 0;
            int increment = 0;
            int remoteID = 0;
            int localID = 0;

            synchronized (c)
            {
                  int stdoutAvail = 0;
                  int stderrAvail = 0;

                  while (true)
                  {
                        /*
                         * Data available? We have to return remaining data even if the
                         * channel is already closed.
                         */

                        stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
                        stderrAvail = c.stderrWritepos - c.stderrReadpos;

                        if ((!extended) && (stdoutAvail != 0))
                              break;

                        if ((extended) && (stderrAvail != 0))
                              break;

                        /* Do not wait if more data will never arrive (EOF or CLOSED) */

                        if ((c.EOF) || (c.state != Channel.STATE_OPEN))
                              return -1;

                        try
                        {
                              c.wait();
                        }
                        catch (InterruptedException ignore)
                        {
                    throw new InterruptedIOException();
                        }
                  }

                  /* OK, there is some data. Return it. */

                  if (!extended)
                  {
                        copylen = (stdoutAvail > len) ? len : stdoutAvail;
                        System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
                        c.stdoutReadpos += copylen;

                        if (c.stdoutReadpos != c.stdoutWritepos)

                              System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
                                          - c.stdoutReadpos);

                        c.stdoutWritepos -= c.stdoutReadpos;
                        c.stdoutReadpos = 0;
                  }
                  else
                  {
                        copylen = (stderrAvail > len) ? len : stderrAvail;
                        System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
                        c.stderrReadpos += copylen;

                        if (c.stderrReadpos != c.stderrWritepos)

                              System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
                                          - c.stderrReadpos);

                        c.stderrWritepos -= c.stderrReadpos;
                        c.stderrReadpos = 0;
                  }

                  if (c.state != Channel.STATE_OPEN)
                        return copylen;

                  if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2))
                  {
                        int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos, Channel.CHANNEL_BUFFER_SIZE
                                    - c.stderrWritepos);

                        increment = minFreeSpace - c.localWindow;
                        c.localWindow = minFreeSpace;
                  }

                  remoteID = c.remoteID; /* read while holding the lock */
                  localID = c.localID; /* read while holding the lock */
            }

            /*
             * If a consumer reads stdout and stdin in parallel, we may end up with
             * sending two msgWindowAdjust messages. Luckily, it
             * does not matter in which order they arrive at the server.
             */

            if (increment > 0)
            {
                  if (log.isEnabled())
                        log.log(80, "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");

                  synchronized (c.channelSendLock)
                  {
                        byte[] msg = c.msgWindowAdjust;

                        msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
                        msg[1] = (byte) (remoteID >> 24);
                        msg[2] = (byte) (remoteID >> 16);
                        msg[3] = (byte) (remoteID >> 8);
                        msg[4] = (byte) (remoteID);
                        msg[5] = (byte) (increment >> 24);
                        msg[6] = (byte) (increment >> 16);
                        msg[7] = (byte) (increment >> 8);
                        msg[8] = (byte) (increment);

                        if (c.closeMessageSent == false)
                              tm.sendMessage(msg);
                  }
            }

            return copylen;
      }

      public void msgChannelData(byte[] msg, int msglen) throws IOException
      {
            if (msglen <= 9)
                  throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")");

            int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
            int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);

            Channel c = getChannel(id);

            if (c == null)
                  throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);

            if (len != (msglen - 9))
                  throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got "
                              + len + ")");

            if (log.isEnabled())
                  log.log(80, "Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");

            synchronized (c)
            {
                  if (c.state == Channel.STATE_CLOSED)
                        return; // ignore

                  if (c.state != Channel.STATE_OPEN)
                        throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");

                  if (c.localWindow < len)
                        throw new IOException("Remote sent too much data, does not fit into window.");

                  c.localWindow -= len;

                  System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
                  c.stdoutWritepos += len;

                  c.notifyAll();
            }
      }

      public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException
      {
            if (msglen != 9)
                  throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")");

            int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
            int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);

            Channel c = getChannel(id);

            if (c == null)
                  throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);

            synchronized (c)
            {
                  final long huge = 0xFFFFffffL; /* 2^32 - 1 */

                  c.remoteWindow += (windowChange & huge); /* avoid sign extension */

                  /* TODO - is this a good heuristic? */

                  if ((c.remoteWindow > huge))
                        c.remoteWindow = huge;

                  c.notifyAll();
            }

            if (log.isEnabled())
                  log.log(80, "Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
      }

      public void msgChannelOpen(byte[] msg, int msglen) throws IOException
      {
            TypesReader tr = new TypesReader(msg, 0, msglen);

            tr.readByte(); // skip packet type
            String channelType = tr.readString();
            int remoteID = tr.readUINT32(); /* sender channel */
            int remoteWindow = tr.readUINT32(); /* initial window size */
            int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */

            if ("x11".equals(channelType))
            {
                  synchronized (x11_magic_cookies)
                  {
                        /* If we did not request X11 forwarding, then simply ignore this bogus request. */

                        if (x11_magic_cookies.size() == 0)
                        {
                              PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
                                          Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");

                              tm.sendAsynchronousMessage(pcof.getPayload());

                              if (log.isEnabled())
                                    log.log(20, "Unexpected X11 request, denying it!");

                              return;
                        }
                  }

                  String remoteOriginatorAddress = tr.readString();
                  int remoteOriginatorPort = tr.readUINT32();

                  Channel c = new Channel(this);

                  synchronized (c)
                  {
                        c.remoteID = remoteID;
                        c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
                        c.remoteMaxPacketSize = remoteMaxPacketSize;
                        c.localID = addChannel(c);
                  }

                  /*
                   * The open confirmation message will be sent from another thread
                   */

                  RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
                  rxat.setDaemon(true);
                  rxat.start();

                  return;
            }

            if ("forwarded-tcpip".equals(channelType))
            {
                  String remoteConnectedAddress = tr.readString(); /* address that was connected */
                  int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
                  String remoteOriginatorAddress = tr.readString(); /* originator IP address */
                  int remoteOriginatorPort = tr.readUINT32(); /* originator port */

                  RemoteForwardingData rfd = null;

                  synchronized (remoteForwardings)
                  {
                        rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(remoteConnectedPort));
                  }

                  if (rfd == null)
                  {
                        PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
                                    Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
                                    "No thanks, unknown port in forwarded-tcpip request", "");

                        /* Always try to be polite. */

                        tm.sendAsynchronousMessage(pcof.getPayload());

                        if (log.isEnabled())
                              log.log(20, "Unexpected forwarded-tcpip request, denying it!");

                        return;
                  }

                  Channel c = new Channel(this);

                  synchronized (c)
                  {
                        c.remoteID = remoteID;
                        c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
                        c.remoteMaxPacketSize = remoteMaxPacketSize;
                        c.localID = addChannel(c);
                  }

                  /*
                   * The open confirmation message will be sent from another thread.
                   */

                  RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
                              remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);

                  rat.setDaemon(true);
                  rat.start();

                  return;
            }

            /* Tell the server that we have no idea what it is talking about */

            PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
                        "Unknown channel type", "");

            tm.sendAsynchronousMessage(pcof.getPayload());

            if (log.isEnabled())
                  log.log(20, "The peer tried to open an unsupported channel type (" + channelType + ")");
      }

      public void msgChannelRequest(byte[] msg, int msglen) throws IOException
      {
            TypesReader tr = new TypesReader(msg, 0, msglen);

            tr.readByte(); // skip packet type
            int id = tr.readUINT32();

            Channel c = getChannel(id);

            if (c == null)
                  throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);

            String type = tr.readString("US-ASCII");
            boolean wantReply = tr.readBoolean();

            if (log.isEnabled())
                  log.log(80, "Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");

            if (type.equals("exit-status"))
            {
                  if (wantReply != false)
                        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");

                  int exit_status = tr.readUINT32();

                  if (tr.remain() != 0)
                        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");

                  synchronized (c)
                  {
                        c.exit_status = new Integer(exit_status);
                        c.notifyAll();
                  }

                  if (log.isEnabled())
                        log.log(50, "Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");

                  return;
            }

            if (type.equals("exit-signal"))
            {
                  if (wantReply != false)
                        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");

                  String signame = tr.readString("US-ASCII");
                  tr.readBoolean();
                  tr.readString();
                  tr.readString();

                  if (tr.remain() != 0)
                        throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");

                  synchronized (c)
                  {
                        c.exit_signal = signame;
                        c.notifyAll();
                  }

                  if (log.isEnabled())
                        log.log(50, "Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");

                  return;
            }

            /* We simply ignore unknown channel requests, however, if the server wants a reply,
             * then we signal that we have no idea what it is about.
             */

            if (wantReply)
            {
                  byte[] reply = new byte[5];

                  reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE;
                  reply[1] = (byte) (c.remoteID >> 24);
                  reply[2] = (byte) (c.remoteID >> 16);
                  reply[3] = (byte) (c.remoteID >> 8);
                  reply[4] = (byte) (c.remoteID);

                  tm.sendAsynchronousMessage(reply);
            }

            if (log.isEnabled())
                  log.log(50, "Channel request '" + type + "' is not known, ignoring it");
      }

      public void msgChannelEOF(byte[] msg, int msglen) throws IOException
      {
            if (msglen != 5)
                  throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");

            int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);

            Channel c = getChannel(id);

            if (c == null)
                  throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);

            synchronized (c)
            {
                  c.EOF = true;
                  c.notifyAll();
            }

            if (log.isEnabled())
                  log.log(50, "Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
      }

      public void msgChannelClose(byte[] msg, int msglen) throws IOException
      {
            if (msglen != 5)
                  throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");

            int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);

            Channel c = getChannel(id);

            if (c == null)
                  throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);

            synchronized (c)
            {
                  c.EOF = true;
                  c.state = Channel.STATE_CLOSED;
                  c.setReasonClosed("Close requested by remote");
                  c.closeMessageRecv = true;

                  removeChannel(c.localID);

                  c.notifyAll();
            }

            if (log.isEnabled())
                  log.log(50, "Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
      }

      public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
      {
            if (msglen != 5)
                  throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");

            int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);

            Channel c = getChannel(id);

            if (c == null)
                  throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);

            synchronized (c)
            {
                  c.successCounter++;
                  c.notifyAll();
            }

            if (log.isEnabled())
                  log.log(80, "Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
      }

      public void msgChannelFailure(byte[] msg, int msglen) throws IOException
      {
            if (msglen != 5)
                  throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");

            int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);

            Channel c = getChannel(id);

            if (c == null)
                  throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);

            synchronized (c)
            {
                  c.failedCounter++;
                  c.notifyAll();
            }

            if (log.isEnabled())
                  log.log(50, "Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
      }

      public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
      {
            PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);

            Channel c = getChannel(sm.recipientChannelID);

            if (c == null)
                  throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
                              + sm.recipientChannelID);

            synchronized (c)
            {
                  if (c.state != Channel.STATE_OPENING)
                        throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
                                    + sm.recipientChannelID);

                  c.remoteID = sm.senderChannelID;
                  c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
                  c.remoteMaxPacketSize = sm.maxPacketSize;
                  c.state = Channel.STATE_OPEN;
                  c.notifyAll();
            }

            if (log.isEnabled())
                  log.log(50, "Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
                              + sm.senderChannelID + ")");
      }

      public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
      {
            if (msglen < 5)
                  throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");

            TypesReader tr = new TypesReader(msg, 0, msglen);

            tr.readByte(); // skip packet type
            int id = tr.readUINT32(); /* sender channel */

            Channel c = getChannel(id);

            if (c == null)
                  throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);

            int reasonCode = tr.readUINT32();
            String description = tr.readString("UTF-8");

            String reasonCodeSymbolicName = null;

            switch (reasonCode)
            {
            case 1:
                  reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
                  break;
            case 2:
                  reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
                  break;
            case 3:
                  reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
                  break;
            case 4:
                  reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
                  break;
            default:
                  reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
            }

            StringBuffer descriptionBuffer = new StringBuffer();
            descriptionBuffer.append(description);

            for (int i = 0; i < descriptionBuffer.length(); i++)
            {
                  char cc = descriptionBuffer.charAt(i);

                  if ((cc >= 32) && (cc <= 126))
                        continue;
                  descriptionBuffer.setCharAt(i, '\uFFFD');
            }

            synchronized (c)
            {
                  c.EOF = true;
                  c.state = Channel.STATE_CLOSED;
                  c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
                              + descriptionBuffer.toString() + "')");
                  c.notifyAll();
            }

            if (log.isEnabled())
                  log.log(50, "Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
      }

      public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
      {
            /* Currently we do not support any kind of global request */

            TypesReader tr = new TypesReader(msg, 0, msglen);

            tr.readByte(); // skip packet type
            String requestName = tr.readString();
            boolean wantReply = tr.readBoolean();

            if (wantReply)
            {
                  byte[] reply_failure = new byte[1];
                  reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;

                  tm.sendAsynchronousMessage(reply_failure);
            }

            /* We do not clean up the requestName String - that is OK for debug */

            if (log.isEnabled())
                  log.log(80, "Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
      }

      public void msgGlobalSuccess() throws IOException
      {
            synchronized (channels)
            {
                  globalSuccessCounter++;
                  channels.notifyAll();
            }

            if (log.isEnabled())
                  log.log(80, "Got SSH_MSG_REQUEST_SUCCESS");
      }

      public void msgGlobalFailure() throws IOException
      {
            synchronized (channels)
            {
                  globalFailedCounter++;
                  channels.notifyAll();
            }

            if (log.isEnabled())
                  log.log(80, "Got SSH_MSG_REQUEST_FAILURE");
      }

      public void handleMessage(byte[] msg, int msglen) throws IOException
      {
            if (msg == null)
            {
                  if (log.isEnabled())
                        log.log(50, "HandleMessage: got shutdown");

                  synchronized (listenerThreads)
                  {
                        for (int i = 0; i < listenerThreads.size(); i++)
                        {
                              IChannelWorkerThread lat = (IChannelWorkerThread) listenerThreads.elementAt(i);
                              lat.stopWorking();
                        }
                        listenerThreadsAllowed = false;
                  }

                  synchronized (channels)
                  {
                        shutdown = true;

                        for (int i = 0; i < channels.size(); i++)
                        {
                              Channel c = (Channel) channels.elementAt(i);
                              synchronized (c)
                              {
                                    c.EOF = true;
                                    c.state = Channel.STATE_CLOSED;
                                    c.setReasonClosed("The connection is being shutdown");
                                    c.closeMessageRecv = true; /*
                                                                                                                                                                                           * You never know, perhaps
                                                                                                                                                                                           * we are waiting for a
                                                                                                                                                                                           * pending close message
                                                                                                                                                                                           * from the server...
                                                                                                                                                                                           */
                                    c.notifyAll();
                              }
                        }
                        /* Works with J2ME */
                        channels.setSize(0);
                        channels.trimToSize();
                        channels.notifyAll(); /* Notify global response waiters */
                        return;
                  }
            }

            switch (msg[0])
            {
            case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
                  msgChannelOpenConfirmation(msg, msglen);
                  break;
            case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
                  msgChannelWindowAdjust(msg, msglen);
                  break;
            case Packets.SSH_MSG_CHANNEL_DATA:
                  msgChannelData(msg, msglen);
                  break;
            case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
                  msgChannelExtendedData(msg, msglen);
                  break;
            case Packets.SSH_MSG_CHANNEL_REQUEST:
                  msgChannelRequest(msg, msglen);
                  break;
            case Packets.SSH_MSG_CHANNEL_EOF:
                  msgChannelEOF(msg, msglen);
                  break;
            case Packets.SSH_MSG_CHANNEL_OPEN:
                  msgChannelOpen(msg, msglen);
                  break;
            case Packets.SSH_MSG_CHANNEL_CLOSE:
                  msgChannelClose(msg, msglen);
                  break;
            case Packets.SSH_MSG_CHANNEL_SUCCESS:
                  msgChannelSuccess(msg, msglen);
                  break;
            case Packets.SSH_MSG_CHANNEL_FAILURE:
                  msgChannelFailure(msg, msglen);
                  break;
            case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
                  msgChannelOpenFailure(msg, msglen);
                  break;
            case Packets.SSH_MSG_GLOBAL_REQUEST:
                  msgGlobalRequest(msg, msglen);
                  break;
            case Packets.SSH_MSG_REQUEST_SUCCESS:
                  msgGlobalSuccess();
                  break;
            case Packets.SSH_MSG_REQUEST_FAILURE:
                  msgGlobalFailure();
                  break;
            default:
                  throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index