You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2008/12/04 19:59:36 UTC

svn commit: r723396 [10/12] - in /mina/sshd/trunk: ./ src/ src/docs/ src/main/ src/main/filtered-resources/ src/main/filtered-resources/org/ src/main/filtered-resources/org/apache/ src/main/filtered-resources/org/apache/sshd/ src/main/java/ src/main/ja...

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
+
+public class SignatureDSA extends AbstractSignature {
+
+    public static class Factory implements NamedFactory<Signature> {
+
+        public String getName() {
+            return KeyPairProvider.SSH_DSS;
+        }
+
+        public Signature create() {
+            return new SignatureDSA();
+        }
+
+    }
+
+    public SignatureDSA() {
+        super("SHA1withDSA");
+    }
+
+    public byte[] sign() throws Exception {
+        byte[] sig = signature.sign();
+
+        // sig is in ASN.1
+        // SEQUENCE::={ r INTEGER, s INTEGER }
+        int len = 0;
+        int index = 3;
+        len = sig[index++] & 0xff;
+        byte[] r = new byte[len];
+        System.arraycopy(sig, index, r, 0, r.length);
+        index = index + len + 1;
+        len = sig[index++] & 0xff;
+        byte[] s = new byte[len];
+        System.arraycopy(sig, index, s, 0, s.length);
+
+        byte[] result = new byte[40];
+
+        // result must be 40 bytes, but length of r and s may not be 20 bytes
+
+        System.arraycopy(r,
+                         (r.length > 20) ? 1 : 0,
+                         result,
+                         (r.length > 20) ? 0 : 20 - r.length,
+                         (r.length > 20) ? 20 : r.length);
+        System.arraycopy(s,
+                         (s.length > 20) ? 1 : 0,
+                         result,
+                         (s.length > 20) ? 20 : 40 - s.length,
+                         (s.length > 20) ? 20 : s.length);
+
+        return result;
+    }
+
+    public boolean verify(byte[] sig) throws Exception {
+        sig = extractSig(sig);
+
+        // ASN.1
+        int frst = ((sig[0] & 0x80) != 0 ? 1 : 0);
+        int scnd = ((sig[20] & 0x80) != 0 ? 1 : 0);
+
+        int length = sig.length + 6 + frst + scnd;
+        byte[] tmp = new byte[length];
+        tmp[0] = (byte) 0x30;
+        tmp[1] = (byte) 0x2c;
+        tmp[1] += frst;
+        tmp[1] += scnd;
+        tmp[2] = (byte) 0x02;
+        tmp[3] = (byte) 0x14;
+        tmp[3] += frst;
+        System.arraycopy(sig, 0, tmp, 4 + frst, 20);
+        tmp[4 + tmp[3]] = (byte) 0x02;
+        tmp[5 + tmp[3]] = (byte) 0x14;
+        tmp[5 + tmp[3]] += scnd;
+        System.arraycopy(sig, 20, tmp, 6 + tmp[3] + scnd, 20);
+        sig = tmp;
+
+        return signature.verify(sig);
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
+
+public class SignatureRSA extends AbstractSignature {
+
+    public static class Factory implements NamedFactory<Signature> {
+
+        public String getName() {
+            return KeyPairProvider.SSH_RSA;
+        }
+
+        public Signature create() {
+            return new SignatureRSA();
+        }
+
+    }
+
+    public SignatureRSA() {
+        super("SHA1withRSA");
+    }
+
+    public byte[] sign() throws Exception {
+        return signature.sign();
+    }
+
+    public boolean verify(byte[] sig) throws Exception {
+        sig = extractSig(sig);
+        return signature.verify(sig);
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/Buffer.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/Buffer.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/Buffer.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/Buffer.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.SshConstants;
+import org.apache.mina.common.ByteBuffer;
+
+public final class Buffer {
+
+    public static final int DEFAULT_SIZE = 256;
+
+    private byte[] data;
+    private int rpos;
+    private int wpos;
+
+    public Buffer() {
+        this(DEFAULT_SIZE);
+    }
+
+    public Buffer(int size) {
+        this(new byte[getNextPowerOf2(size)], false);
+    }
+
+    public Buffer(byte[] data) {
+        this(data, true);
+    }
+
+    public Buffer(byte[] data, boolean read) {
+        this.data = data;
+        this.rpos = 0;
+        this.wpos = read ? data.length : 0;
+    }
+
+    @Override
+    public String toString() {
+        return "Buffer [rpos=" + rpos + ", wpos=" + wpos + ", size=" + data.length + "]";
+    }
+
+    /*======================
+      Global methods
+    ======================*/
+
+    public int rpos() {
+        return rpos;
+    }
+
+    public void rpos(int rpos) {
+        this.rpos = rpos;
+    }
+
+    public int wpos() {
+        return wpos;
+    }
+
+    public void wpos(int wpos) {
+        ensureCapacity(wpos - this.wpos);
+        this.wpos = wpos;
+    }
+
+    public int available() {
+        return wpos - rpos;
+    }
+
+    public byte[] array() {
+        return data;
+    }
+
+    public void compact() {
+        if (available() > 0) {
+            System.arraycopy(data, rpos, data, 0, wpos - rpos);
+        }
+        wpos -= rpos;
+        rpos = 0;
+    }
+
+    public byte[] getCompactData() {
+        int l = available();
+        if (l > 0) {
+            byte[] b = new byte[l];
+            System.arraycopy(data, rpos, b, 0, l);
+            return b;
+        } else {
+            return new byte[0];
+        }
+    }
+
+    public void clear() {
+        rpos = 0;
+        wpos = 0;
+    }
+
+    public String printHex() {
+        return BufferUtils.printHex(array(), rpos(), available());
+    }
+
+    /*======================
+       Read methods
+     ======================*/
+
+    public byte getByte() {
+        ensureAvailable(1);
+        return data[rpos++];
+    }
+
+    public int getInt() {
+        ensureAvailable(4);
+        int i = ((data[rpos++] << 24) & 0xff000000)|
+                ((data[rpos++] << 16) & 0x00ff0000)|
+                ((data[rpos++] <<  8) & 0x0000ff00)|
+                ((data[rpos++]      ) & 0x000000ff);
+        return i;
+    }
+
+    public boolean getBoolean() {
+        return getByte() != 0;
+    }
+
+    public String getString() {
+        int len = getInt();
+        if (len < 0 || len > 32768) {
+            throw new IllegalStateException("Bad item length: " + len);
+        }
+        ensureAvailable(len);
+        String s = new String(data, rpos, len);
+        rpos += len;
+        return s;
+    }
+
+    public byte[] getStringAsBytes() {
+        return getBytes();
+    }
+
+    public BigInteger getMPInt() {
+        return new BigInteger(getMPIntAsBytes());
+    }
+
+    public byte[] getMPIntAsBytes() {
+        return getBytes();
+    }
+
+    public byte[] getBytes() {
+        int len = getInt();
+        if (len < 0 || len > 32768) {
+            throw new IllegalStateException("Bad item length: " + len);
+        }
+        byte[] b = new byte[len];
+        getRawBytes(b);
+        return b;
+    }
+
+    public void getRawBytes(byte[] buf) {
+        getRawBytes(buf, 0, buf.length);
+    }
+
+    public void getRawBytes(byte[] buf, int off, int len) {
+        ensureAvailable(len);
+        System.arraycopy(data, rpos, buf, off, len);
+        rpos += len;
+    }
+
+    public PublicKey getPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
+        PublicKey key;
+        String keyAlg = getString();
+        if (KeyPairProvider.SSH_RSA.equals(keyAlg)) {
+            BigInteger e = getMPInt();
+            BigInteger n = getMPInt();
+            KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
+            key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
+        } else if (KeyPairProvider.SSH_DSS.equals(keyAlg)) {
+            BigInteger p = getMPInt();
+            BigInteger q = getMPInt();
+            BigInteger g = getMPInt();
+            BigInteger y = getMPInt();
+            KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
+            key = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+        } else {
+            throw new IllegalStateException("Unsupported algorithm: " + keyAlg);
+        }
+        return key;
+    }
+
+    public SshConstants.Message getCommand() {
+        byte b = getByte();
+        SshConstants.Message cmd = SshConstants.Message.fromByte(b);
+        if (cmd == null) {
+            throw new IllegalStateException("Unknown command code: " + b);
+        }
+        return cmd;
+    }
+
+    private void ensureAvailable(int a) {
+        if (available() < a) {
+            throw new BufferException("Underflow");
+        }
+    }
+
+    /*======================
+       Write methods
+     ======================*/
+
+    public void putByte(byte b) {
+        ensureCapacity(1);
+        data[wpos++] = b;
+    }
+
+    public void putBuffer(Buffer buffer) {
+        int r = buffer.available();
+        ensureCapacity(r);
+        System.arraycopy(buffer.data, buffer.rpos, data, wpos, r);
+        wpos += r;
+    }
+
+    public void putBuffer(ByteBuffer buffer) {
+        int r = buffer.remaining();
+        ensureCapacity(r);
+        buffer.get(data, wpos, r);
+        wpos += r;
+    }
+
+    public void putInt(int i) {
+        ensureCapacity(4);
+        data[wpos++] = (byte) (i >> 24);
+        data[wpos++] = (byte) (i >> 16);
+        data[wpos++] = (byte) (i >>  8);
+        data[wpos++] = (byte) (i      );
+    }
+
+    public void putBoolean(boolean b) {
+        putByte(b ? (byte) 1 : (byte) 0);
+    }
+
+    public void putBytes(byte[] b) {
+        putBytes(b, 0, b.length);
+    }
+
+    public void putBytes(byte[] b, int off, int len) {
+        putInt(len);
+        ensureCapacity(len);
+        System.arraycopy(b, off, data, wpos, len);
+        wpos += len;
+    }
+
+    public void putString(String string) {
+        putString(string.getBytes());
+    }
+
+    public void putString(byte[] str) {
+        putInt(str.length);
+        putRawBytes(str);
+    }
+
+    public void putMPInt(BigInteger bi) {
+        putMPInt(bi.toByteArray());
+    }
+
+    public void putMPInt(byte[] foo) {
+        int i = foo.length;
+        if ((foo[0] & 0x80) != 0) {
+            i++;
+            putInt(i);
+            putByte((byte)0);
+        } else {
+            putInt(i);
+        }
+        putRawBytes(foo);
+    }
+
+    public void putRawBytes(byte[] d) {
+        putRawBytes(d, 0, d.length);
+    }
+
+    public void putRawBytes(byte[] d, int off, int len) {
+        ensureCapacity(len);
+        System.arraycopy(d, off, data, wpos, len);
+        wpos += len;
+    }
+
+    public void putPublicKey(PublicKey key) {
+        if (key instanceof RSAPublicKey) {
+            putString(KeyPairProvider.SSH_RSA);
+            putMPInt(((RSAPublicKey) key).getPublicExponent());
+            putMPInt(((RSAPublicKey) key).getModulus());
+        } else if (key instanceof DSAPublicKey) {
+            putString(KeyPairProvider.SSH_DSS);
+            putMPInt(((DSAPublicKey) key).getParams().getP());
+            putMPInt(((DSAPublicKey) key).getParams().getQ());
+            putMPInt(((DSAPublicKey) key).getParams().getG());
+            putMPInt(((DSAPublicKey) key).getY());
+        } else {
+            throw new IllegalStateException("Unsupported algorithm: " + key.getAlgorithm());
+        }
+    }
+
+    public void putCommand(SshConstants.Message cmd) {
+        putByte(cmd.toByte());
+    }
+
+    private void ensureCapacity(int capacity) {
+        if (data.length - wpos < capacity) {
+            int cw = wpos + capacity;
+            byte[] tmp = new byte[getNextPowerOf2(cw)];
+            System.arraycopy(data, 0, tmp, 0, data.length);
+            data = tmp;
+        }
+    }
+
+    public static class BufferException extends RuntimeException {
+        public BufferException(String message) {
+            super(message);
+        }
+    }
+
+    private static final int getNextPowerOf2(int i) {
+        int j = 1;
+        while (j < i) {
+            j <<= 1;
+        }
+        return j;
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/BufferUtils.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/BufferUtils.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/BufferUtils.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/BufferUtils.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+public class BufferUtils {
+
+    public static String printHex(byte[] array) {
+        return printHex(array, 0, array.length);
+    }
+
+    public static String printHex(byte[] array, int offset, int len) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < len; i++) {
+            byte b = array[offset + i];
+            if (sb.length() > 0) {
+                sb.append(' ');
+            }
+            sb.append(digits[(b >> 4) & 0x0F]);
+            sb.append(digits[b & 0x0F]);
+        }
+        return sb.toString();
+    }
+
+    public static boolean equals(byte[] a1, byte[] a2) {
+        if (a1.length != a2.length) {
+            return false;
+        }
+        return equals(a1, 0, a2, 0, a1.length);
+    }
+
+    public static boolean equals(byte[] a1, int a1Offset, byte[] a2, int a2Offset, int length) {
+        if (a1.length < a1Offset + length || a2.length < a2Offset + length) {
+            return false;
+        }
+        while (length-- > 0) {
+            if (a1[a1Offset++] != a2[a2Offset++]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    final static char[] digits = {
+	    '0' , '1' , '2' , '3' , '4' , '5' ,
+	    '6' , '7' , '8' , '9' , 'a' , 'b' ,
+	    'c' , 'd' , 'e' , 'f'
+    };
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/IoUtils.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/IoUtils.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/IoUtils.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/IoUtils.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+public class IoUtils {
+
+    public static void closeQuietly(Closeable... closeables) {
+        for (Closeable c : closeables) {
+            try {
+                if (c != null) {
+                    c.close();
+                }
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LfToCrLfFilterOutputStream.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LfToCrLfFilterOutputStream.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LfToCrLfFilterOutputStream.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LfToCrLfFilterOutputStream.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class LfToCrLfFilterOutputStream extends FilterOutputStream {
+
+    public LfToCrLfFilterOutputStream(OutputStream out) {
+        super(out);
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+	    byte[] d = new byte[1];
+        d[0] = (byte) b;
+        write(d, 0, 1);
+    }
+
+    @Override
+    public void write(byte b[], int off, int len) throws IOException {
+        int nb = 0;
+        for (int i = off; i < off + len; i++) {
+            if (b[i] == '\n') {
+                nb++;
+            }
+        }
+        if (nb > 0) {
+            byte[] bb = new byte[len + nb];
+            for (int i = 0, j = 0; i < len; i++) {
+                if (b[off + i] == '\n') {
+                    bb[j++] = '\r';
+                }
+                bb[j++] = b[off + i];
+            }
+            b = bb;
+            off = 0;
+            len = len + nb;
+        }
+        out.write(b, off, len);
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.slf4j.Logger;
+
+public class LoggingFilterOutputStream extends FilterOutputStream {
+
+    private final String msg;
+    private final Logger log;
+
+    public LoggingFilterOutputStream(OutputStream out, String msg, Logger log) {
+        super(out);
+        this.msg = msg;
+        this.log = log;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+	    byte[] d = new byte[1];
+        d[0] = (byte) b;
+        write(d, 0, 1);
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (log != null && log.isTraceEnabled()) {
+            log.trace("{} {}", msg, BufferUtils.printHex(b, off, len));
+        }
+        out.write(b, off, len);
+    }
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+public class NoCloseInputStream extends FilterInputStream {
+
+    public NoCloseInputStream(InputStream in) {
+        super(in);
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.io.FilterOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public class NoCloseOutputStream extends FilterOutputStream {
+
+    public NoCloseOutputStream(OutputStream out) {
+        super(out);
+    }
+
+
+    @Override
+    public void close() throws IOException {
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/SecurityUtils.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/SecurityUtils.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/SecurityUtils.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
+import javax.crypto.NoSuchPaddingException;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SecurityUtils {
+
+    public static final String BOUNCY_CASTLE = "BC";
+
+    private static final Logger LOG = LoggerFactory.getLogger(SecurityUtils.class);
+
+    private static String securityProvider = null;
+    private static Boolean registerBouncyCastle;
+    private static boolean registrationDone;
+
+    public static synchronized void setSecurityProvider(String securityProvider) {
+        SecurityUtils.securityProvider = securityProvider;
+        registrationDone = false;
+    }
+
+    public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) {
+        SecurityUtils.registerBouncyCastle = registerBouncyCastle;
+        registrationDone = false;
+    }
+
+    public static synchronized String getSecurityProvider() {
+        register();
+        return securityProvider;
+    }
+
+    public static synchronized boolean isBouncyCastleRegistered() {
+        register();
+        return BOUNCY_CASTLE.equals(securityProvider);
+    }
+
+    private static void register() {
+        if (!registrationDone) {
+            if (securityProvider == null && (registerBouncyCastle == null || registerBouncyCastle)) {
+                // Use an inner class to avoid a strong dependency from SshServer on BouncyCastle
+                try {
+                    new BouncyCastleRegistration().run();
+                } catch (Throwable t) {
+                    if (registerBouncyCastle == null) {
+                        LOG.info("BouncyCastle not registered, using the default JCE provider");
+                    } else {
+                        LOG.error("Failed to register BouncyCastle as the defaut JCE provider");
+                        throw new RuntimeException("Failed to register BouncyCastle as the defaut JCE provider", t);
+                    }
+                }
+            }
+            registrationDone = true;
+        }
+    }
+
+    private static class BouncyCastleRegistration {
+        public void run() throws Exception {
+            if (java.security.Security.getProvider(BOUNCY_CASTLE) == null) {
+                LOG.info("Trying to register BouncyCastle as a JCE provider");
+                java.security.Security.addProvider(new BouncyCastleProvider());
+                MessageDigest.getInstance("MD5", BOUNCY_CASTLE);
+                KeyAgreement.getInstance("DH", BOUNCY_CASTLE);
+                LOG.info("Registration succeeded");
+            } else {
+                LOG.info("BouncyCastle already registered as a JCE provider");
+            }
+            securityProvider = BOUNCY_CASTLE;
+        }
+    }
+
+    public static synchronized KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+        register();
+        if (getSecurityProvider() == null) {
+            return KeyFactory.getInstance(algorithm);
+        } else {
+            return KeyFactory.getInstance(algorithm, getSecurityProvider());
+        }
+    }
+
+    public static synchronized Cipher getCipher(String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
+        register();
+        if (getSecurityProvider() == null) {
+            return Cipher.getInstance(transformation);
+        } else {
+            return Cipher.getInstance(transformation, getSecurityProvider());
+        }
+    }
+
+    public static synchronized MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+        register();
+        if (getSecurityProvider() == null) {
+            return MessageDigest.getInstance(algorithm);
+        } else {
+            return MessageDigest.getInstance(algorithm, getSecurityProvider());
+        }
+    }
+
+    public static synchronized KeyPairGenerator getKeyPairGenerator(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+        register();
+        if (getSecurityProvider() == null) {
+            return KeyPairGenerator.getInstance(algorithm);
+        } else {
+            return KeyPairGenerator.getInstance(algorithm, getSecurityProvider());
+        }
+    }
+
+    public static synchronized KeyAgreement getKeyAgreement(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+        register();
+        if (getSecurityProvider() == null) {
+            return KeyAgreement.getInstance(algorithm);
+        } else {
+            return KeyAgreement.getInstance(algorithm, getSecurityProvider());
+        }
+    }
+
+    public static synchronized Mac getMac(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+        register();
+        if (getSecurityProvider() == null) {
+            return Mac.getInstance(algorithm);
+        } else {
+            return Mac.getInstance(algorithm, getSecurityProvider());
+        }
+    }
+
+    public static synchronized Signature getSignature(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+        register();
+        if (getSecurityProvider() == null) {
+            return Signature.getInstance(algorithm);
+        } else {
+            return Signature.getInstance(algorithm, getSecurityProvider());
+        }
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/CommandFactory.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/CommandFactory.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/CommandFactory.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/CommandFactory.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public interface CommandFactory {
+
+    Command createCommand(String command);
+
+    public interface Command {
+
+        /**
+         * Set the input stream that can be used by the shell to read input.
+         * @param in
+         */
+        void setInputStream(InputStream in);
+
+        /**
+         * Set the output stream that can be used by the shell to write its output.
+         * @param out
+         */
+        void setOutputStream(OutputStream out);
+
+        /**
+         * Set the error stream that can be used by the shell to write its errors.
+         * @param err
+         */
+        void setErrorStream(OutputStream err);
+
+        /**
+         * Set the callback that the shell has to call when it is closed.
+         */
+        void setExitCallback(ExitCallback callback);
+
+        /**
+         * Execute the command and return its status
+         *
+         * @return
+         * @throws java.io.IOException
+         */
+        void start() throws IOException;
+    }
+
+
+    /**
+     * Callback used by the shell to notify the SSH server is has exited
+     */
+    public interface ExitCallback {
+
+        /**
+         * Informs the SSH server that the shell has exited
+         *
+         * @param exitValue the exit value
+         */
+        void onExit(int exitValue);
+
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server;
+
+public interface PasswordAuthenticator {
+
+    /**
+     * Check the validity of a password.
+     * This method should return null if the authentication fails.
+     *
+     * @param username the username
+     * @param password the password
+     * @return a non null identity object or <code>null</code if authentication fail
+     */
+    Object authenticate(String username, String password);
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server;
+
+import java.security.PublicKey;
+
+public interface PublickeyAuthenticator {
+
+    boolean hasKey(String username, PublicKey key);
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerChannel.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerChannel.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerChannel.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerChannel.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.common.Channel;
+import org.apache.sshd.common.channel.Window;
+import org.apache.sshd.server.session.ServerSession;
+
+public interface ServerChannel extends Channel {
+
+    void init(ServerSession session, int id, int recipient, int rwsize, int rmpsize);
+
+    void handleRequest(Buffer buffer) throws IOException;
+
+    Window getLocalWindow();
+
+}
\ No newline at end of file

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerFactoryManager.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerFactoryManager.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerFactoryManager.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server;
+
+import java.util.List;
+
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.NamedFactory;
+
+public interface ServerFactoryManager extends FactoryManager {
+
+    PublickeyAuthenticator getPublickeyAuthenticator();
+
+    List<NamedFactory<UserAuth>> getUserAuthFactories();
+
+    List<NamedFactory<ServerChannel>> getChannelFactories();
+
+    ShellFactory getShellFactory();
+
+    CommandFactory getCommandFactory();
+
+    PasswordAuthenticator getPasswordAuthenticator();
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ *
+ * TODO: remove InvertedShell and create a wrapper instead to keep the api cleaner
+ *
+ */
+public interface ShellFactory {
+
+    /**
+     * Create a shell.
+     *
+     * @return
+     * @throws Exception
+     */
+    Shell createShell();
+
+    /**
+     * Represents a shell that can be used in an interactive mode to send command.
+     * Shell implementations must implement one of the two sub-interfaces {@link InvertedShell}
+     * or {@link DirectShell} which have different ways to configure the input and output streams.
+     */
+    public interface Shell {
+
+        /**
+         * Destroy the shell.
+         * This method can be called by the SSH server to destroy the shell because
+         * the client has disconnected somehow.
+         */
+        void destroy();
+
+    }
+
+    /**
+     * This shell have inverted streams, such as the one obtained when launching a
+     * new {@link Process} from java.
+     */
+    public interface InvertedShell extends Shell {
+
+        /**
+         * Starts the shell and will make the streams available for
+         * the ssh server to retrieve and use.
+         *
+         * @param env
+         * @throws Exception
+         */
+        void start(Map<String,String> env) throws IOException;
+
+        /**
+         * Returns the output stream used to feed the shell.
+         * This method is called after the shell has been started.
+         *
+         * @return
+         */
+        OutputStream getInputStream();
+
+        /**
+         * Return an InputStream representing the output stream of the shell.
+         * @return
+         */
+        InputStream getOutputStream();
+
+        /**
+         * Return an InputStream representing the error stream of the shell.
+         * @return
+         */
+        InputStream getErrorStream();
+
+        /**
+         * Check if the underlying shell is still alive
+         * @return
+         */
+        boolean isAlive();
+
+        /**
+         * Retrieve the exit value of the shell.
+         * This method must only be called when the shell is not alive anymore.
+         *
+         * @return the exit value of the shell
+         */
+        int exitValue();
+
+    }
+
+    /**
+     * This shell have direct streams, meaning those streams will be provided by the ssh server
+     * for the shell to use directy.
+     * This interface is suitable for implementing shells in java, rather than using an external
+     * process.
+     */
+    public interface DirectShell extends Shell {
+
+        /**
+         * Set the input stream that can be used by the shell to read input.
+         * @param in
+         */
+        void setInputStream(InputStream in);
+
+        /**
+         * Set the output stream that can be used by the shell to write its output.
+         * @param out
+         */
+        void setOutputStream(OutputStream out);
+
+        /**
+         * Set the error stream that can be used by the shell to write its errors.
+         * @param err
+         */
+        void setErrorStream(OutputStream err);
+
+        /**
+         * Set the callback that the shell has to call when it is closed.
+         */
+        void setExitCallback(ExitCallback callback);
+
+        /**
+         * Starts the shell.
+         * All streams must have been set before calling this method.
+         *
+         * @param env
+         * @throws Exception
+         */
+        void start(Map<String,String> env) throws IOException;
+
+    }
+
+    /**
+     * Callback used by the shell to notify the SSH server is has exited
+     */
+    public interface ExitCallback {
+
+        /**
+         * Informs the SSH server that the shell has exited
+         *
+         * @param exitValue the exit value
+         */
+        void onExit(int exitValue);
+
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/UserAuth.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/UserAuth.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/UserAuth.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/UserAuth.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server;
+
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.session.ServerSession;
+
+public interface UserAuth {
+
+    /**
+     * Try to authenticate the user.
+     * This methods should return a non null object which is the user identity if
+     * the authentication succeeded.  If the authentication failed, this method should
+     * throw an exception.  If the authentication is still ongoing, a null value should
+     * be returned.
+     *
+     * @param session the current ssh session
+     * @param username the user trying to log in
+     * @param buffer the request buffer containing parameters specific to this request
+     *
+     * @return the identity of the user or null if the authentication fails
+     */
+    Object auth(ServerSession session, String username, Buffer buffer) throws Exception;
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.auth;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.UserAuth;
+
+public class UserAuthNone implements UserAuth {
+
+    public static class Factory implements NamedFactory<UserAuth> {
+        public String getName() {
+            return "none";
+        }
+        public UserAuth create() {
+            return new UserAuthNone();
+        }
+    }
+
+    public Object auth(ServerSession session, String username, Buffer buffer) {
+        return username;
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.auth;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.UserAuth;
+
+public class UserAuthPassword implements UserAuth {
+
+    public static class Factory implements NamedFactory<UserAuth> {
+        public String getName() {
+            return "password";
+        }
+        public UserAuth create() {
+            return new UserAuthPassword();
+        }
+    }
+
+    public Object auth(ServerSession session, String username, Buffer buffer) throws Exception {
+        boolean newPassword = buffer.getBoolean();
+        if (newPassword) {
+            throw new IllegalStateException("Password changes are not supported");
+        }
+        String password = buffer.getString();
+        return checkPassword(session, username, password);
+    }
+
+    private Object checkPassword(ServerSession session, String username, String password) throws Exception {
+        PasswordAuthenticator auth = session.getServerFactoryManager().getPasswordAuthenticator();
+        if (auth != null) {
+            Object identity = auth.authenticate(username, password);
+            if (identity != null) {
+                return identity;
+            } else {
+                throw new Exception("Authentication failed: bad username or password supplied");
+            }
+        }
+        throw new Exception("No PasswordAuthenticator configured");
+    }
+
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.auth;
+
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.PublickeyAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.UserAuth;
+
+public class UserAuthPublicKey implements UserAuth {
+
+    public static class Factory implements NamedFactory<UserAuth> {
+        public String getName() {
+            return "publickey";
+        }
+        public UserAuth create() {
+            return new UserAuthPublicKey();
+        }
+    }
+
+    public Object auth(ServerSession session, String username, Buffer buffer) throws Exception {
+        boolean hasSig = buffer.getBoolean();
+        String alg = buffer.getString();
+
+        int oldLim = buffer.wpos();
+        int oldPos = buffer.rpos();
+        int len = buffer.getInt();
+        buffer.wpos(buffer.rpos() + len);
+        PublicKey key = buffer.getPublicKey();
+        String keyAlg = (key instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS;
+
+        Signature verif = NamedFactory.Utils.create(session.getFactoryManager().getSignatureFactories(), keyAlg);
+        verif.init(key, null);
+        buffer.wpos(oldLim);
+
+        byte[] sig = hasSig ? buffer.getBytes() : null;
+
+        PublickeyAuthenticator authenticator = session.getServerFactoryManager().getPublickeyAuthenticator();
+        if (authenticator == null) {
+            throw new Exception("No PublickeyAuthenticator configured");
+        }
+
+        if (!hasSig) {
+            if (authenticator.hasKey(username, key)) {
+                Buffer buf = session.createBuffer(SshConstants.Message.SSH_MSG_USERAUTH_PK_OK);
+                buf.putString(alg);
+                buf.putRawBytes(buffer.array(), oldPos, 4 + len);
+                session.writePacket(buf);
+                return null;
+            } else {
+                throw new Exception("Unsupported key for user");
+            }
+        } else {
+            if (!authenticator.hasKey(username, key)) {
+                throw new Exception("Unsupported key for user");
+            }
+            Buffer buf = new Buffer();
+            buf.putString(session.getKex().getH());
+            buf.putCommand(SshConstants.Message.SSH_MSG_USERAUTH_REQUEST);
+            buf.putString(username);
+            buf.putString("ssh-connection");
+            buf.putString("publickey");
+            buf.putByte((byte) 1);
+            buf.putString(keyAlg);
+            buffer.rpos(oldPos);
+            buffer.wpos(oldPos + 4 + len);
+            buf.putBuffer(buffer);
+            verif.update(buf.array(), buf.rpos(), buf.available());
+            if (verif.verify(sig)) {
+                return username;
+            } else {
+                throw new Exception("Key verification failed");
+            }
+        }
+    }
+}

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/AbstractServerChannel.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/AbstractServerChannel.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/AbstractServerChannel.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/AbstractServerChannel.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.channel;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.channel.AbstractChannel;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.ServerChannel;
+import org.apache.sshd.server.session.ServerSession;
+
+public abstract class AbstractServerChannel extends AbstractChannel implements ServerChannel {
+
+    protected boolean exitStatusSent;
+
+    public void init(ServerSession session, int id, int recipient, int rwsize, int rmpsize) {
+        this.session = session;
+        this.id = id;
+        this.recipient = recipient;
+        this.remoteWindow.init(rwsize, rmpsize);
+        configureWindow();
+    }
+
+    protected void sendExitStatus(int v) throws IOException {
+        if (!exitStatusSent) {
+            exitStatusSent = true;
+            log.info("Send SSH_MSG_CHANNEL_REQUEST exit-status on channel {}", id);
+            Buffer buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_REQUEST);
+            buffer.putInt(recipient);
+            buffer.putString("exit-status");
+            buffer.putByte((byte) 0);
+            buffer.putInt(v);
+            session.writePacket(buffer);
+        }
+    }
+
+}
\ No newline at end of file

Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java Thu Dec  4 10:59:31 2008
@@ -0,0 +1,494 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.channel.ChannelOutputStream;
+import org.apache.sshd.common.channel.ChannelPipedOutputStream;
+import org.apache.sshd.common.channel.ChannelPipedInputStream;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.common.util.LfToCrLfFilterOutputStream;
+import org.apache.sshd.common.util.LoggingFilterOutputStream;
+import org.apache.sshd.common.util.IoUtils;
+import org.apache.sshd.server.ServerChannel;
+import org.apache.sshd.server.ShellFactory;
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ *
+ */
+public class ChannelSession extends AbstractServerChannel {
+
+    public static class Factory implements NamedFactory<ServerChannel> {
+
+        public String getName() {
+            return "session";
+        }
+
+        public ServerChannel create() {
+            return new ChannelSession();
+        }
+    }
+
+    protected static enum PtyMode {
+        // Chars
+        VINTR(1), VQUIT(2), VERASE(3), VKILL(4), VEOF(5), VEOL(6), VEOL2(7), VSTART(8), VSTOP(9),
+        VSUSP(10), VDSUSP(11), VREPRINT(12), VWERASE(13), VLNEXT(14), VFLUSH(15), VSWTCH(16), VSTATUS(17), VDISCARD(18),
+        // I flags
+        IGNPAR(30), PARMRK(31), INPCK(32), ISTRIP(33), INCLR(34), IGNCR(35), ICRNL(36), IUCLC(37), IXON(38), IXANY(39),
+        IXOFF(40), IMAXBEL(41),
+        // L flags
+        ISIG(50), ICANON(51), XCASE(52), ECHO(53), ECHOE(54), ECHOK(55), ECHONL(56), NOFLSH(57), TOSTOP(58), IEXTEN(59),
+        ECHOCTL(60), ECHOKE(61), PENDIN(62),
+        // O flags
+        OPOST(70), OLCUC(71), ONLCR(72), OCRNL(73), ONOCR(74), ONLRET(75),
+        // C flags
+        CS7(90), CS8(91), PARENB(92), PARODD(93),
+        // Speeed
+        TTY_OP_ISPEED(128), TTY_OP_OSPEED(129);
+
+        private int v;
+        private PtyMode(int v) {
+            this.v = v;
+        }
+
+        public int toInt() {
+            return v;
+        }
+
+        static Map<Integer,PtyMode> commands;
+        static {
+            commands = new HashMap<Integer, PtyMode>();
+            for (PtyMode c : PtyMode.values()) {
+                commands.put(c.toInt(), c);
+            }
+        }
+        public static PtyMode fromInt(int b) {
+            return commands.get(b);
+        }
+    }
+
+    protected static class PtyModeValue {
+        public final PtyMode mode;
+        public final int value;
+
+        public PtyModeValue(PtyMode mode, int value) {
+            this.mode = mode;
+            this.value = value;
+        }
+
+        public String toString() {
+            return mode + "(" + mode.toInt() + ") =" + value;
+        }
+    }
+
+    protected String type;
+    protected PtyModeValue[] ptyModes;
+    protected InputStream in;
+    protected OutputStream out;
+    protected OutputStream err;
+    protected ShellFactory.Shell shell;
+    protected OutputStream shellIn;
+    protected InputStream shellOut;
+    protected InputStream shellErr;
+    protected Map<String,String> env;
+    protected ExecutorService executor;
+    protected boolean usePipedChannelStreams;
+
+    public ChannelSession() {
+        executor = Executors.newSingleThreadExecutor();
+    }
+
+    public void close() throws IOException {
+        super.close();
+        if (shell != null) {
+            shell.destroy();
+        }
+        IoUtils.closeQuietly(in, out, err, shellIn, shellOut, shellErr);
+        executor.shutdown();
+    }
+
+    public void handleRequest(Buffer buffer) throws IOException {
+        log.info("Received SSH_MSG_CHANNEL_REQUEST on channel {}", id);
+        String type = buffer.getString();
+        log.info("Received channel request: {}", type);
+        if (!handleRequest(type, buffer)) {
+            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE);
+            buffer.putInt(id);
+            session.writePacket(buffer);
+        }
+    }
+
+    protected void doWriteData(byte[] data, int off, int len) throws IOException {
+        if (usePipedChannelStreams) {
+            shellIn.write(data, off, len);
+            shellIn.flush();
+        } else {
+            final byte[] buf = new byte[len];
+            System.arraycopy(data, off, buf, 0, len);
+            executor.execute(new Runnable() {
+                public void run() {
+                    try {
+                        if (shellIn != null) {
+                            shellIn.write(buf);
+                            shellIn.flush();
+                        }
+                    } catch (IOException e) {
+                        try {
+                            internalClose();
+                        } catch (IOException e2) {
+                            // Ignore
+                        }
+                    }
+                }
+            });
+            localWindow.consumeAndCheck(len);
+        }
+    }
+
+    protected void doWriteExtendedData(byte[] data, int off, int len) throws IOException {
+        throw new UnsupportedOperationException("Server channel does not support extended data");
+    }
+
+    protected boolean handleRequest(String type, Buffer buffer) throws IOException {
+        if ("env".equals(type)) {
+            return handleEnv(buffer);
+        }
+        if ("pty-req".equals(type)) {
+            return handlePtyReq(buffer);
+        }
+        if ("window-change".equals(type)) {
+            return handleWindowChange(buffer);
+        }
+        if ("signal".equals(type)) {
+            return handleSignal(buffer);
+        }
+        if ("shell".equals(type)) {
+            if (checkType(type)) {
+                return handleShell(buffer);
+            } else {
+                return false;
+            }
+        }
+        if ("exec".equals(type)) {
+            if (checkType(type)) {
+                return handleExec(buffer);
+            } else {
+                return false;
+            }
+        }
+        if ("subsystem".equals(type)) {
+            if (checkType(type)) {
+                return handleSubsystem(buffer);
+            } else {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Only one of "shell", "exec" or "subsystem" command
+     * is permitted for a given channel.
+     *
+     * @param type
+     * @return
+     */
+    private boolean checkType(String type) {
+        if (this.type == null) {
+            this.type = type;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    protected boolean handleEnv(Buffer buffer) throws IOException {
+        boolean wantReply = buffer.getBoolean();
+        String name = buffer.getString();
+        String value = buffer.getString();
+        addEnvVariable(name, value);
+        log.debug("env for channel {}: {} = {}", new Object[] { id, name, value });
+        if (wantReply) {
+            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+            buffer.putInt(id);
+            session.writePacket(buffer);
+        }
+        return true;
+    }
+
+    protected boolean handlePtyReq(Buffer buffer) throws IOException {
+        boolean wantReply = buffer.getBoolean();
+        String term = buffer.getString();
+        int tColumns = buffer.getInt();
+        int tRows = buffer.getInt();
+        int tWidth = buffer.getInt();
+        int tHeight = buffer.getInt();
+        byte[] modes = buffer.getBytes();
+        List<PtyModeValue> modeList = new ArrayList<PtyModeValue>();
+        for (int i = 0; i < modes.length && modes[i] != 0;) {
+            PtyMode mode = PtyMode.fromInt(modes[i++]);
+            int val  = ((modes[i++] << 24) & 0xff000000) |
+                       ((modes[i++] << 16) & 0x00ff0000) |
+                       ((modes[i++] <<  8) & 0x0000ff00) |
+                       ((modes[i++]      ) & 0x000000ff);
+            PtyModeValue m = new PtyModeValue(mode, val);
+            modeList.add(m);
+        }
+        ptyModes = modeList.toArray(new PtyModeValue[0]);
+        if (log.isDebugEnabled()) {
+            StringBuffer strModes = new StringBuffer();
+            for (PtyModeValue m : ptyModes) {
+                if (strModes.length() > 0) {
+                    strModes.append(", ");
+                }
+                strModes.append(m);
+            }
+            log.debug("pty for channel {}: term={}, size=({} - {}), pixels=({}, {}), modes=[{}]", new Object[] { id, term, tColumns, tRows, tWidth, tHeight, strModes.toString() });
+        }
+        addEnvVariable("TERM", term);
+        // TODO: handle pty request correctly
+        if (wantReply) {
+            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+            buffer.putInt(id);
+            session.writePacket(buffer);
+        }
+        return true;
+    }
+
+    protected boolean handleWindowChange(Buffer buffer) throws IOException {
+        boolean wantReply = buffer.getBoolean();
+        int tColumns = buffer.getInt();
+        int tRows = buffer.getInt();
+        int tWidth = buffer.getInt();
+        int tHeight = buffer.getInt();
+        log.debug("window-change for channel {}: ({} - {}), ({}, {})", new Object[] { id, tColumns, tRows, tWidth, tHeight });
+        // TODO: handle window-change request correctly
+        if (wantReply) {
+            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+            buffer.putInt(id);
+            session.writePacket(buffer);
+        }
+        return true;
+    }
+
+    protected boolean handleSignal(Buffer buffer) throws IOException {
+        boolean wantReply = buffer.getBoolean();
+        String name = buffer.getString();
+        log.debug("Signal received on channel {}: {}", id, name);
+        // TODO: handle signal request correctly
+        if (wantReply) {
+            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+            buffer.putInt(id);
+            session.writePacket(buffer);
+        }
+        return true;
+    }
+
+    protected boolean handleShell(Buffer buffer) throws IOException {
+        boolean wantReply = buffer.getBoolean();
+
+        if (((ServerSession) session).getServerFactoryManager().getShellFactory() == null) {
+            return false;
+        }
+        addEnvVariable("USER", ((ServerSession) session).getUsername());
+        shell = ((ServerSession) session).getServerFactoryManager().getShellFactory().createShell();
+        out = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_DATA);
+        err = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_EXTENDED_DATA);
+        // Wrap in logging filters
+        out = new LoggingFilterOutputStream(out, "OUT:", log);
+        err = new LoggingFilterOutputStream(err, "ERR:", log);
+        if (getPtyModeValue(PtyMode.ONLCR) != 0) {
+            out = new LfToCrLfFilterOutputStream(out);
+            err = new LfToCrLfFilterOutputStream(err);
+        }
+        if (shell instanceof ShellFactory.InvertedShell) {
+            ((ShellFactory.InvertedShell) shell).start(env);
+            shellIn = ((ShellFactory.InvertedShell) shell).getInputStream();
+            shellOut = ((ShellFactory.InvertedShell) shell).getOutputStream();
+            shellErr = ((ShellFactory.InvertedShell) shell).getErrorStream();
+            new Thread() {
+                @Override
+                public void run() {
+                    pumpStreams();
+                }
+            }.start();
+            // TODO: set this thread as daemon?
+        } else if (shell instanceof ShellFactory.DirectShell) {
+            in = new ChannelPipedInputStream(localWindow);
+            shellIn = new ChannelPipedOutputStream((ChannelPipedInputStream) in);
+            usePipedChannelStreams = true;
+            ((ShellFactory.DirectShell) shell).setInputStream(in);
+            ((ShellFactory.DirectShell) shell).setOutputStream(out);
+            ((ShellFactory.DirectShell) shell).setErrorStream(err);
+            ((ShellFactory.DirectShell) shell).setExitCallback(new ShellFactory.ExitCallback() {
+                public void onExit(int exitValue) {
+                    try {
+                        closeShell(exitValue);
+                    } catch (IOException e) {
+                        log.info("Error closing shell", e);
+                    }
+                }
+            });
+            ((ShellFactory.DirectShell) shell).start(env);
+        } else {
+            throw new IllegalStateException("Unknown shell type");
+        }
+        shellIn = new LoggingFilterOutputStream(shellIn, "IN: ", log);
+
+        if (wantReply) {
+            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+            buffer.putInt(id);
+            session.writePacket(buffer);
+        }
+        return true;
+    }
+
+    protected int getPtyModeValue(PtyMode mode) {
+        if (ptyModes != null) {
+            for (PtyModeValue m : ptyModes) {
+                if (m.mode == mode) {
+                    return m.value;
+                }
+            }
+        }
+        return 0;
+    }
+
+    protected boolean handleExec(Buffer buffer) throws IOException {
+        boolean wantReply = buffer.getBoolean();
+        String commandLine = buffer.getString();
+
+        if (((ServerSession) session).getServerFactoryManager().getCommandFactory() == null) {
+            return false;
+        }
+
+        CommandFactory.Command command = ((ServerSession) session).getServerFactoryManager().getCommandFactory().createCommand(commandLine);
+        out = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_DATA);
+        err = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_EXTENDED_DATA);
+        // Wrap in logging filters
+        out = new LoggingFilterOutputStream(out, "OUT:", log);
+        err = new LoggingFilterOutputStream(err, "ERR:", log);
+        in = new ChannelPipedInputStream(localWindow);
+        shellIn = new ChannelPipedOutputStream((ChannelPipedInputStream) in);
+        usePipedChannelStreams = true;
+        command.setInputStream(in);
+        command.setOutputStream(out);
+        command.setErrorStream(err);
+        command.setExitCallback(new CommandFactory.ExitCallback() {
+            public void onExit(int exitValue) {
+                try {
+                    closeShell(exitValue);
+                } catch (IOException e) {
+                    log.info("Error closing shell", e);
+                }
+            }
+        });
+        command.start();
+
+        if (wantReply) {
+            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+            buffer.putInt(id);
+            session.writePacket(buffer);
+        }
+
+        return true;
+    }
+
+    protected boolean handleSubsystem(Buffer buffer) throws IOException {
+        boolean wantReply = buffer.getBoolean();
+        // TODO: start subsystem
+        if (wantReply) {
+            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+            buffer.putInt(id);
+            session.writePacket(buffer);
+        }
+        return true;
+    }
+
+    protected synchronized void addEnvVariable(String name, String value) {
+        if (env == null) {
+            env = new HashMap<String,String>();
+        }
+        env.put(name, value);
+    }
+
+    protected void pumpStreams() {
+        try {
+            // Use a single thread to correctly sequence the output and error streams.
+            // If any bytes are available from the output stream, send them first, then
+            // check the error stream, or wait until more data is available.
+            ShellFactory.InvertedShell is = (ShellFactory.InvertedShell) shell;
+            byte[] buffer = new byte[512];
+            for (;;) {
+                if (!is.isAlive()) {
+                    closeShell(is.exitValue());
+                    return;
+                }
+                if (pumpStream(shellOut, out, buffer)) {
+                    continue;
+                }
+                if (pumpStream(shellErr, err, buffer)) {
+                    continue;
+                }
+                // Sleep a bit.  This is not very good, as it consumes CPU, but the
+                // input streams are not selectable for nio, and any other blocking
+                // method would consume at least two threads
+                Thread.sleep(1);
+            }
+        } catch (Exception e) {
+            if (!closed && !closing) {
+                // TODO: send a disconnect if there is a problem?
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private boolean pumpStream(InputStream in, OutputStream out, byte[] buffer) throws IOException {
+        if (in.available() > 0) {
+            int len = in.read(buffer);
+            if (len > 0) {
+                out.write(buffer, 0, len);
+                out.flush();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected void closeShell(int exitValue) throws IOException {
+        sendEof();
+        sendExitStatus(exitValue);
+        // TODO: We should wait for all streams to be consumed before closing the channel
+        internalClose();
+    }
+
+}
\ No newline at end of file