changeset 342:175c7d68f3c4

merge ganymed into mainline
author Carl Byington <carl@five-ten-sg.com>
date Thu, 31 Jul 2014 16:33:38 -0700
parents ce2f4e397703 (current diff) 529883a3a114 (diff)
children df13118e8e79
files src/com/trilead/ssh2/AuthAgentCallback.java src/com/trilead/ssh2/ChannelCondition.java src/com/trilead/ssh2/Connection.java src/com/trilead/ssh2/ConnectionInfo.java src/com/trilead/ssh2/ConnectionMonitor.java src/com/trilead/ssh2/DHGexParameters.java src/com/trilead/ssh2/DebugLogger.java src/com/trilead/ssh2/DynamicPortForwarder.java src/com/trilead/ssh2/HTTPProxyData.java src/com/trilead/ssh2/HTTPProxyException.java src/com/trilead/ssh2/InteractiveCallback.java src/com/trilead/ssh2/KnownHosts.java src/com/trilead/ssh2/LocalPortForwarder.java src/com/trilead/ssh2/LocalStreamForwarder.java src/com/trilead/ssh2/ProxyData.java src/com/trilead/ssh2/SCPClient.java src/com/trilead/ssh2/SFTPException.java src/com/trilead/ssh2/SFTPv3Client.java src/com/trilead/ssh2/SFTPv3DirectoryEntry.java src/com/trilead/ssh2/SFTPv3FileAttributes.java src/com/trilead/ssh2/SFTPv3FileHandle.java src/com/trilead/ssh2/ServerHostKeyVerifier.java src/com/trilead/ssh2/Session.java src/com/trilead/ssh2/StreamGobbler.java src/com/trilead/ssh2/auth/AuthenticationManager.java src/com/trilead/ssh2/channel/AuthAgentForwardThread.java src/com/trilead/ssh2/channel/Channel.java src/com/trilead/ssh2/channel/ChannelInputStream.java src/com/trilead/ssh2/channel/ChannelManager.java src/com/trilead/ssh2/channel/ChannelOutputStream.java src/com/trilead/ssh2/channel/DynamicAcceptThread.java src/com/trilead/ssh2/channel/IChannelWorkerThread.java src/com/trilead/ssh2/channel/LocalAcceptThread.java src/com/trilead/ssh2/channel/RemoteAcceptThread.java src/com/trilead/ssh2/channel/RemoteForwardingData.java src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java src/com/trilead/ssh2/channel/StreamForwarder.java src/com/trilead/ssh2/channel/X11ServerData.java src/com/trilead/ssh2/compression/CompressionFactory.java src/com/trilead/ssh2/compression/ICompressor.java src/com/trilead/ssh2/compression/Zlib.java src/com/trilead/ssh2/compression/ZlibOpenSSH.java src/com/trilead/ssh2/crypto/Base64.java src/com/trilead/ssh2/crypto/CryptoWishList.java src/com/trilead/ssh2/crypto/KeyMaterial.java src/com/trilead/ssh2/crypto/PEMDecoder.java src/com/trilead/ssh2/crypto/PEMStructure.java src/com/trilead/ssh2/crypto/SimpleDERReader.java src/com/trilead/ssh2/crypto/cipher/AES.java src/com/trilead/ssh2/crypto/cipher/BlockCipher.java src/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java src/com/trilead/ssh2/crypto/cipher/BlowFish.java src/com/trilead/ssh2/crypto/cipher/CBCMode.java src/com/trilead/ssh2/crypto/cipher/CTRMode.java src/com/trilead/ssh2/crypto/cipher/CipherInputStream.java src/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java src/com/trilead/ssh2/crypto/cipher/DES.java src/com/trilead/ssh2/crypto/cipher/DESede.java src/com/trilead/ssh2/crypto/cipher/NullCipher.java src/com/trilead/ssh2/crypto/dh/DhExchange.java src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java src/com/trilead/ssh2/crypto/dh/EcDhExchange.java src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java src/com/trilead/ssh2/crypto/digest/MAC.java src/com/trilead/ssh2/log/Logger.java src/com/trilead/ssh2/packets/PacketChannelAuthAgentReq.java src/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java src/com/trilead/ssh2/packets/PacketChannelOpenFailure.java src/com/trilead/ssh2/packets/PacketChannelTrileadPing.java src/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java src/com/trilead/ssh2/packets/PacketDisconnect.java src/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java src/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java src/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java src/com/trilead/ssh2/packets/PacketIgnore.java src/com/trilead/ssh2/packets/PacketKexDHInit.java src/com/trilead/ssh2/packets/PacketKexDHReply.java src/com/trilead/ssh2/packets/PacketKexDhGexGroup.java src/com/trilead/ssh2/packets/PacketKexDhGexInit.java src/com/trilead/ssh2/packets/PacketKexDhGexReply.java src/com/trilead/ssh2/packets/PacketKexDhGexRequest.java src/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java src/com/trilead/ssh2/packets/PacketKexInit.java src/com/trilead/ssh2/packets/PacketNewKeys.java src/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java src/com/trilead/ssh2/packets/PacketOpenSessionChannel.java src/com/trilead/ssh2/packets/PacketServiceAccept.java src/com/trilead/ssh2/packets/PacketServiceRequest.java src/com/trilead/ssh2/packets/PacketSessionExecCommand.java src/com/trilead/ssh2/packets/PacketSessionPtyRequest.java src/com/trilead/ssh2/packets/PacketSessionPtyResize.java src/com/trilead/ssh2/packets/PacketSessionStartShell.java src/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java src/com/trilead/ssh2/packets/PacketSessionX11Request.java src/com/trilead/ssh2/packets/PacketUserauthBanner.java src/com/trilead/ssh2/packets/PacketUserauthFailure.java src/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java src/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java src/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java src/com/trilead/ssh2/packets/PacketUserauthRequestNone.java src/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java src/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java src/com/trilead/ssh2/packets/Packets.java src/com/trilead/ssh2/packets/TypesReader.java src/com/trilead/ssh2/packets/TypesWriter.java src/com/trilead/ssh2/sftp/AttrTextHints.java src/com/trilead/ssh2/sftp/AttribBits.java src/com/trilead/ssh2/sftp/AttribFlags.java src/com/trilead/ssh2/sftp/AttribPermissions.java src/com/trilead/ssh2/sftp/AttribTypes.java src/com/trilead/ssh2/sftp/ErrorCodes.java src/com/trilead/ssh2/sftp/OpenFlags.java src/com/trilead/ssh2/sftp/Packet.java src/com/trilead/ssh2/signature/DSASHA1Verify.java src/com/trilead/ssh2/signature/ECDSASHA2Verify.java src/com/trilead/ssh2/signature/RSASHA1Verify.java src/com/trilead/ssh2/transport/ClientServerHello.java src/com/trilead/ssh2/transport/KexManager.java src/com/trilead/ssh2/transport/KexParameters.java src/com/trilead/ssh2/transport/KexState.java src/com/trilead/ssh2/transport/MessageHandler.java src/com/trilead/ssh2/transport/NegotiateException.java src/com/trilead/ssh2/transport/NegotiatedParameters.java src/com/trilead/ssh2/transport/TransportConnection.java src/com/trilead/ssh2/transport/TransportManager.java src/com/trilead/ssh2/util/TimeoutService.java src/com/trilead/ssh2/util/Tokenizer.java
diffstat 330 files changed, 23230 insertions(+), 19320 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Thu Jul 17 22:09:05 2014 -0700
+++ b/Makefile	Thu Jul 31 16:33:38 2014 -0700
@@ -76,9 +76,9 @@
 		--indent-cases \
 		--indent-namespaces \
 		--break-blocks \
+		--unpad-paren \
 		--pad-oper \
 		--pad-header \
-		--unpad-paren \
 		--delete-empty-lines \
 		--align-pointer=type \
 		--break-closing-brackets \
--- a/TODO	Thu Jul 17 22:09:05 2014 -0700
+++ b/TODO	Thu Jul 31 16:33:38 2014 -0700
@@ -37,6 +37,12 @@
 svn checkout svn://svn.code.sf.net/p/tn5250j/code/branches/new-tabs-jse1.6 tn5250j
 
 ==================================
+
+update com.trilead.ssh2 to ganymed
+svn checkout http://ganymed-ssh-2.googlecode.com/svn/trunk/ ganymed-ssh-2-read-only
+rev 161
+
+==================================
 TODO:
 
 possible merge of irssi?
--- a/assets/help/About.html	Thu Jul 17 22:09:05 2014 -0700
+++ b/assets/help/About.html	Thu Jul 31 16:33:38 2014 -0700
@@ -38,9 +38,32 @@
 </p>
 
 <p>
-Based on the Trilead SSH2 client provided under a BSD-style
-license. Copyright &copy; 2007 Trilead
-AG <a href="http://www.trilead.com">http://www.trilead.com</a>
+Based on the Ganymed SSH2 client provided under a BSD-style
+license. Copyright &copy; 2005 - 2006 Swiss Federal Institute of
+Technology (ETH Zurich), Department of Computer
+Science <a href="http://www.inf.ethz.ch">http://www.inf.ethz.ch</a>,
+Christian Plattner.  Copyright &copy; 2006 - 2013 Christian Plattner.
+<a href="http://code.google.com/p/ganymed-ssh-2/">http://code.google.com/p/ganymed-ssh-2/</a>
+The Java implementations of the AES, Blowfish and 3DES ciphers have been
+taken (and slightly modified) from the cryptography package released by
+"The Legion Of The Bouncy Castle".
+Copyright &copy; 2000 - 2004 The Legion Of The Bouncy Castle
+<a href="http://www.bouncycastle.org">http://www.bouncycastle.org</a>
+The following disclaimer applies:
+</p>
+
+<p>
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
 </p>
 
 <p>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ch.ethz.ssh2.LICENSE.txt	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,87 @@
+Copyright (c) 2006 - 2013 Christian Plattner. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+a.) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+b.) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+c.) Neither the name of Christian Plattner nor the names of its contributors may
+    be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
+This software includes work that was released under the following license:
+
+Copyright (c) 2005 - 2006 Swiss Federal Institute of Technology (ETH Zurich),
+  Department of Computer Science (http://www.inf.ethz.ch),
+  Christian Plattner. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+a.) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+b.) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+c.) Neither the name of ETH Zurich nor the names of its contributors may
+    be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
+The Java implementations of the AES, Blowfish and 3DES ciphers have been
+taken (and slightly modified) from the cryptography package released by
+"The Legion Of The Bouncy Castle".
+
+Their license states the following:
+
+Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+(http://www.bouncycastle.org)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. 
+
--- a/res/layout/act_generatepubkey.xml	Thu Jul 17 22:09:05 2014 -0700
+++ b/res/layout/act_generatepubkey.xml	Thu Jul 31 16:33:38 2014 -0700
@@ -66,8 +66,7 @@
 					android:id="@+id/rsa"
 					android:layout_width="wrap_content"
 					android:layout_height="wrap_content"
-					android:text="RSA"
-					android:paddingRight="30dip" />
+					android:text="RSA" />
 
 				<RadioButton
 					android:id="@+id/dsa"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/AbstractSFTPClient.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,690 @@
+package ch.ethz.ssh2;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketException;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.HashMap;
+import java.util.Map;
+
+import ch.ethz.ssh2.channel.Channel;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+import ch.ethz.ssh2.sftp.AttribFlags;
+import ch.ethz.ssh2.sftp.ErrorCodes;
+import ch.ethz.ssh2.sftp.Packet;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @version $Id: AbstractSFTPClient.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public abstract class AbstractSFTPClient implements SFTPClient {
+
+    private static final Logger log = Logger.getLogger(SFTPv3Client.class);
+
+    private Session sess;
+
+    private InputStream is;
+    private OutputStream os;
+
+    private int next_request_id = 1000;
+
+    private String charset;
+
+    /**
+     * Parallel read requests maximum size.
+     */
+    private static final int DEFAULT_MAX_PARALLELISM = 64;
+
+    /**
+     * Parallel read requests.
+     */
+    private int parallelism = DEFAULT_MAX_PARALLELISM;
+
+    public void setRequestParallelism(int parallelism) {
+        this.parallelism = Math.min(parallelism, DEFAULT_MAX_PARALLELISM);
+    }
+
+    /**
+     * Mapping request ID to request.
+     */
+    private Map<Integer, OutstandingReadRequest> pendingReadQueue
+        = new HashMap<Integer, OutstandingReadRequest>();
+
+    /**
+     * Mapping request ID to request.
+     */
+    private Map<Integer, OutstandingStatusRequest> pendingStatusQueue
+        = new HashMap<Integer, OutstandingStatusRequest>();
+
+    private PacketListener listener;
+
+    protected AbstractSFTPClient(final Connection conn, final int version, final PacketListener listener) throws IOException {
+        this.listener = listener;
+        log.debug("Opening session and starting SFTP subsystem.");
+        sess = conn.openSession();
+        sess.startSubSystem("sftp");
+        is = sess.getStdout();
+        os = new BufferedOutputStream(sess.getStdin(), 2048);
+        init(version);
+    }
+
+    private void init(final int client_version) throws IOException {
+        // Send SSH_FXP_INIT with client version
+        TypesWriter tw = new TypesWriter();
+        tw.writeUINT32(client_version);
+        sendMessage(Packet.SSH_FXP_INIT, 0, tw.getBytes());
+        /* Receive SSH_FXP_VERSION */
+        log.debug("Waiting for SSH_FXP_VERSION...");
+        TypesReader tr = new TypesReader(receiveMessage(34000)); /* Should be enough for any reasonable server */
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+
+        if (t != Packet.SSH_FXP_VERSION) {
+            log.warning(String.format("The server did not send a SSH_FXP_VERSION but %d", t));
+            throw new PacketTypeException(t);
+        }
+
+        final int protocol_version = tr.readUINT32();
+        log.debug("SSH_FXP_VERSION: protocol_version = " + protocol_version);
+
+        if (protocol_version != client_version) {
+            throw new IOException(String.format("Server protocol version %d does not match %d",
+                                                protocol_version, client_version));
+        }
+
+        // Both parties should from then on adhere to particular version of the protocol
+
+        // Read and save extensions (if any) for later use
+        while (tr.remain() != 0) {
+            String name = tr.readString();
+            listener.read(name);
+            byte[] value = tr.readByteString();
+            log.debug(String.format("SSH_FXP_VERSION: extension: %s = '%s'", name, StringEncoder.GetString(value)));
+        }
+    }
+
+    /**
+     * Queries the channel state
+     *
+     * @return True if the underlying session is in open state
+     */
+    public boolean isConnected() {
+        return sess.getState() == Channel.STATE_OPEN;
+    }
+
+    /**
+     * Close this SFTP session. NEVER forget to call this method to free up
+     * resources - even if you got an exception from one of the other methods.
+     * Sometimes these other methods may throw an exception, saying that the
+     * underlying channel is closed (this can happen, e.g., if the other server
+     * sent a close message.) However, as long as you have not called the
+     * <code>close()</code> method, you are likely wasting resources.
+     */
+    public void close() {
+        sess.close();
+    }
+
+    /**
+     * Set the charset used to convert between Java Unicode Strings and byte encodings
+     * used by the server for paths and file names.
+     *
+     * @param charset the name of the charset to be used or <code>null</code> to use UTF-8.
+     * @throws java.io.IOException
+     * @see #getCharset()
+     */
+    public void setCharset(String charset) throws IOException {
+        if (charset == null) {
+            this.charset = null;
+            return;
+        }
+
+        try {
+            Charset.forName(charset);
+        }
+        catch (UnsupportedCharsetException e) {
+            throw new IOException("This charset is not supported", e);
+        }
+
+        this.charset = charset;
+    }
+
+    /**
+     * The currently used charset for filename encoding/decoding.
+     *
+     * @return The name of the charset (<code>null</code> if UTF-8 is used).
+     * @see #setCharset(String)
+     */
+    public String getCharset() {
+        return charset;
+    }
+
+    public abstract SFTPFileHandle openFile(String fileName, int flags, SFTPFileAttributes attr) throws IOException;
+
+    public void mkdir(String dirName, int posixPermissions) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(dirName, this.getCharset());
+        tw.writeUINT32(AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS);
+        tw.writeUINT32(posixPermissions);
+        sendMessage(Packet.SSH_FXP_MKDIR, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+
+    public void rm(String fileName) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(fileName, this.getCharset());
+        sendMessage(Packet.SSH_FXP_REMOVE, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+
+    public void rmdir(String dirName) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(dirName, this.getCharset());
+        sendMessage(Packet.SSH_FXP_RMDIR, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+
+    public void mv(String oldPath, String newPath) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(oldPath, this.getCharset());
+        tw.writeString(newPath, this.getCharset());
+        sendMessage(Packet.SSH_FXP_RENAME, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+
+    public String readLink(String path) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(path, charset);
+        sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes());
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != req_id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t == Packet.SSH_FXP_NAME) {
+            int count = tr.readUINT32();
+
+            if (count != 1) {
+                throw new PacketTypeException(t);
+            }
+
+            return tr.readString(charset);
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    public void setstat(String path, SFTPFileAttributes attr) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(path, charset);
+        tw.writeBytes(attr.toBytes());
+        sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+
+    public void fsetstat(SFTPFileHandle handle, SFTPFileAttributes attr) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(handle.getHandle(), 0, handle.getHandle().length);
+        tw.writeBytes(attr.toBytes());
+        sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+
+    public void createSymlink(String src, String target) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(src, charset);
+        tw.writeString(target, charset);
+        sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+
+    public void createHardlink(String src, String target) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString("hardlink@openssh.com", charset);
+        tw.writeString(target, charset);
+        tw.writeString(src, charset);
+        sendMessage(Packet.SSH_FXP_EXTENDED, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+
+    public String canonicalPath(String path) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(path, charset);
+        sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes());
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != req_id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t == Packet.SSH_FXP_NAME) {
+            int count = tr.readUINT32();
+
+            if (count != 1) {
+                throw new PacketFormatException("The server sent an invalid SSH_FXP_NAME packet.");
+            }
+
+            final String name = tr.readString(charset);
+            listener.read(name);
+            return name;
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    private void sendMessage(int type, int requestId, byte[] msg, int off, int len) throws IOException {
+        if (log.isDebugEnabled()) {
+            log.debug(String.format("Send message of type %d with request id %d", type, requestId));
+        }
+
+        listener.write(Packet.forName(type));
+        int msglen = len + 1;
+
+        if (type != Packet.SSH_FXP_INIT) {
+            msglen += 4;
+        }
+
+        os.write(msglen >> 24);
+        os.write(msglen >> 16);
+        os.write(msglen >> 8);
+        os.write(msglen);
+        os.write(type);
+
+        if (type != Packet.SSH_FXP_INIT) {
+            os.write(requestId >> 24);
+            os.write(requestId >> 16);
+            os.write(requestId >> 8);
+            os.write(requestId);
+        }
+
+        os.write(msg, off, len);
+        os.flush();
+    }
+
+    protected void sendMessage(int type, int requestId, byte[] msg) throws IOException {
+        sendMessage(type, requestId, msg, 0, msg.length);
+    }
+
+    private void readBytes(byte[] buff, int pos, int len) throws IOException {
+        while (len > 0) {
+            int count = is.read(buff, pos, len);
+
+            if (count < 0) {
+                throw new SocketException("Unexpected end of stream.");
+            }
+
+            len -= count;
+            pos += count;
+        }
+    }
+
+    /**
+     * Read a message and guarantee that the <b>contents</b> is not larger than
+     * <code>maxlen</code> bytes.
+     * <p/>
+     * Note: receiveMessage(34000) actually means that the message may be up to 34004
+     * bytes (the length attribute preceding the contents is 4 bytes).
+     *
+     * @param maxlen
+     * @return the message contents
+     * @throws IOException
+     */
+    protected byte[] receiveMessage(int maxlen) throws IOException {
+        byte[] msglen = new byte[4];
+        readBytes(msglen, 0, 4);
+        int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff));
+
+        if ((len > maxlen) || (len <= 0)) {
+            throw new PacketFormatException(String.format("Illegal SFTP packet length %d", len));
+        }
+
+        byte[] msg = new byte[len];
+        readBytes(msg, 0, len);
+        return msg;
+    }
+
+    protected int generateNextRequestID() {
+        synchronized (this) {
+            return next_request_id++;
+        }
+    }
+
+    protected void closeHandle(byte[] handle) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(handle, 0, handle.length);
+        sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+
+    private void readStatus() throws IOException {
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        // Search the pending queue
+        OutstandingStatusRequest status = pendingStatusQueue.remove(tr.readUINT32());
+
+        if (null == status) {
+            throw new RequestMismatchException();
+        }
+
+        // Evaluate the answer
+        if (t == Packet.SSH_FXP_STATUS) {
+            // In any case, stop sending more packets
+            int code = tr.readUINT32();
+
+            if (log.isDebugEnabled()) {
+                String[] desc = ErrorCodes.getDescription(code);
+                log.debug("Got SSH_FXP_STATUS (" + status.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")");
+            }
+
+            if (code == ErrorCodes.SSH_FX_OK) {
+                return;
+            }
+
+            String msg = tr.readString();
+            listener.read(msg);
+            throw new SFTPException(msg, code);
+        }
+
+        throw new PacketTypeException(t);
+    }
+
+    private void readPendingReadStatus() throws IOException {
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        // Search the pending queue
+        OutstandingReadRequest status = pendingReadQueue.remove(tr.readUINT32());
+
+        if (null == status) {
+            throw new RequestMismatchException();
+        }
+
+        // Evaluate the answer
+        if (t == Packet.SSH_FXP_STATUS) {
+            // In any case, stop sending more packets
+            int code = tr.readUINT32();
+
+            if (log.isDebugEnabled()) {
+                String[] desc = ErrorCodes.getDescription(code);
+                log.debug("Got SSH_FXP_STATUS (" + status.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")");
+            }
+
+            if (code == ErrorCodes.SSH_FX_OK) {
+                return;
+            }
+
+            if (code == ErrorCodes.SSH_FX_EOF) {
+                return;
+            }
+
+            String msg = tr.readString();
+            listener.read(msg);
+            throw new SFTPException(msg, code);
+        }
+
+        throw new PacketTypeException(t);
+    }
+
+    protected void expectStatusOKMessage(int id) throws IOException {
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+
+        if (errorCode == ErrorCodes.SSH_FX_OK) {
+            return;
+        }
+
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    public void closeFile(SFTPFileHandle handle) throws IOException {
+        while (!pendingReadQueue.isEmpty()) {
+            this.readPendingReadStatus();
+        }
+
+        while (!pendingStatusQueue.isEmpty()) {
+            this.readStatus();
+        }
+
+        closeHandle(handle.getHandle());
+    }
+
+    public int read(SFTPFileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException {
+        boolean errorOccured = false;
+        int remaining = len * parallelism;
+        //int clientOffset = dstoff;
+        long serverOffset = fileOffset;
+
+        for (OutstandingReadRequest r : pendingReadQueue.values()) {
+            // Server offset should take pending requests into account.
+            serverOffset += r.len;
+        }
+
+        while (true) {
+            // Stop if there was an error and no outstanding request
+            if ((pendingReadQueue.size() == 0) && errorOccured) {
+                break;
+            }
+
+            // Send as many requests as we are allowed to
+            while (pendingReadQueue.size() < parallelism) {
+                if (errorOccured) {
+                    break;
+                }
+
+                // Send the next read request
+                OutstandingReadRequest req = new OutstandingReadRequest();
+                req.req_id = generateNextRequestID();
+                req.serverOffset = serverOffset;
+                req.len = (remaining > len) ? len : remaining;
+                req.buffer = dst;
+                req.dstOffset = dstoff;
+                serverOffset += req.len;
+                //clientOffset += req.len;
+                remaining -= req.len;
+                sendReadRequest(req.req_id, handle, req.serverOffset, req.len);
+                pendingReadQueue.put(req.req_id, req);
+            }
+
+            if (pendingReadQueue.size() == 0) {
+                break;
+            }
+
+            // Receive a single answer
+            byte[] resp = receiveMessage(34000);
+            TypesReader tr = new TypesReader(resp);
+            int t = tr.readByte();
+            listener.read(Packet.forName(t));
+            // Search the pending queue
+            OutstandingReadRequest req = pendingReadQueue.remove(tr.readUINT32());
+
+            if (null == req) {
+                throw new RequestMismatchException();
+            }
+
+            // Evaluate the answer
+            if (t == Packet.SSH_FXP_STATUS) {
+                /* In any case, stop sending more packets */
+                int code = tr.readUINT32();
+                String msg = tr.readString();
+                listener.read(msg);
+
+                if (log.isDebugEnabled()) {
+                    String[] desc = ErrorCodes.getDescription(code);
+                    log.debug("Got SSH_FXP_STATUS (" + req.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")");
+                }
+
+                // Flag to read all pending requests but don't send any more.
+                errorOccured = true;
+
+                if (pendingReadQueue.isEmpty()) {
+                    if (ErrorCodes.SSH_FX_EOF == code) {
+                        return -1;
+                    }
+
+                    throw new SFTPException(msg, code);
+                }
+            }
+            else if (t == Packet.SSH_FXP_DATA) {
+                // OK, collect data
+                int readLen = tr.readUINT32();
+
+                if ((readLen < 0) || (readLen > req.len)) {
+                    throw new PacketFormatException("The server sent an invalid length field in a SSH_FXP_DATA packet.");
+                }
+
+                if (log.isDebugEnabled()) {
+                    log.debug("Got SSH_FXP_DATA (" + req.req_id + ") " + req.serverOffset + "/" + readLen
+                              + " (requested: " + req.len + ")");
+                }
+
+                // Read bytes into buffer
+                tr.readBytes(req.buffer, req.dstOffset, readLen);
+
+                if (readLen < req.len) {
+                    /* Send this request packet again to request the remaing data in this slot. */
+                    req.req_id = generateNextRequestID();
+                    req.serverOffset += readLen;
+                    req.len -= readLen;
+                    log.debug("Requesting again: " + req.serverOffset + "/" + req.len);
+                    sendReadRequest(req.req_id, handle, req.serverOffset, req.len);
+                    pendingReadQueue.put(req.req_id, req);
+                }
+
+                return readLen;
+            }
+            else {
+                throw new PacketTypeException(t);
+            }
+        }
+
+        // Should never reach here.
+        throw new SFTPException("No EOF reached", -1);
+    }
+
+    private void sendReadRequest(int id, SFTPFileHandle handle, long offset, int len) throws IOException {
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(handle.getHandle(), 0, handle.getHandle().length);
+        tw.writeUINT64(offset);
+        tw.writeUINT32(len);
+        sendMessage(Packet.SSH_FXP_READ, id, tw.getBytes());
+    }
+
+    public void write(SFTPFileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException {
+        while (len > 0) {
+            int writeRequestLen = len;
+
+            if (writeRequestLen > 32768) {
+                writeRequestLen = 32768;
+            }
+
+            // Send the next write request
+            OutstandingStatusRequest req = new OutstandingStatusRequest();
+            req.req_id = generateNextRequestID();
+            TypesWriter tw = new TypesWriter();
+            tw.writeString(handle.getHandle(), 0, handle.getHandle().length);
+            tw.writeUINT64(fileOffset);
+            tw.writeString(src, srcoff, writeRequestLen);
+            sendMessage(Packet.SSH_FXP_WRITE, req.req_id, tw.getBytes());
+            pendingStatusQueue.put(req.req_id, req);
+
+            // Only read next status if parallelism reached
+            while (pendingStatusQueue.size() >= parallelism) {
+                this.readStatus();
+            }
+
+            fileOffset += writeRequestLen;
+            srcoff += writeRequestLen;
+            len -= writeRequestLen;
+        }
+    }
+
+
+    /**
+     * A read  is divided into multiple requests sent sequentially before
+     * reading any status from the server
+     */
+    private static class OutstandingReadRequest {
+        int req_id;
+        /**
+         * Read offset to request on server starting at the file offset for the first request.
+         */
+        long serverOffset;
+        /**
+         * Length of requested data
+         */
+        int len;
+        /**
+         * Offset in destination buffer
+         */
+        int dstOffset;
+        /**
+         * Temporary buffer
+         */
+        byte[] buffer;
+    }
+
+    /**
+     * A read  is divided into multiple requests sent sequentially before
+     * reading any status from the server
+     */
+    private static final class OutstandingStatusRequest {
+        int req_id;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/AuthAgentCallback.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,64 @@
+package ch.ethz.ssh2;
+
+import java.security.KeyPair;
+import java.util.Map;
+
+/**
+ * AuthAgentCallback.
+ *
+ * @author Kenny Root
+ * @version $Id$
+ */
+public interface AuthAgentCallback {
+
+    /**
+     * @return array of blobs containing the OpenSSH-format encoded public keys
+     */
+    Map<String, byte[]> retrieveIdentities();
+
+    /**
+     * @param pair A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code> or <code>ECPrivateKey</code>
+     *            containing a DSA or RSA or EC private key of
+     *            the user in standard java key format.
+     * @param comment comment associated with this key
+     * @param confirmUse whether to prompt before using this key
+     * @param lifetime lifetime in seconds for key to be remembered
+     * @return success or failure
+     */
+    boolean addIdentity(KeyPair pair, String comment, boolean confirmUse, int lifetime);
+
+    /**
+     * @param publicKey byte blob containing the OpenSSH-format encoded public key
+     * @return success or failure
+     */
+    boolean removeIdentity(byte[] publicKey);
+
+    /**
+     * @return success or failure
+     */
+    boolean removeAllIdentities();
+
+    /**
+     * @param publicKey byte blob containing the OpenSSH-format encoded public key
+     * @return A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code>
+     *         containing a DSA or RSA or EC private key of
+     *         the user in standard java key format.
+     */
+    KeyPair getKeyPair(byte[] publicKey);
+
+    /**
+     * @return
+     */
+    boolean isAgentLocked();
+
+    /**
+     * @param lockPassphrase
+     */
+    boolean setAgentLock(String lockPassphrase);
+
+    /**
+     * @param unlockPassphrase
+     * @return
+     */
+    boolean requestAgentUnlock(String unlockPassphrase);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/AuthenticationResult.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,21 @@
+
+package ch.ethz.ssh2;
+
+public enum AuthenticationResult {
+
+    /**
+     *
+     */
+    SUCCESS,
+    /**
+     * The authentication request to which this is a response was successful, however, more
+     * authentication requests are needed (multi-method authentication sequence).
+     *
+     * @see ServerAuthenticationCallback#getRemainingAuthMethods(ServerConnection)
+     */
+    PARTIAL_SUCCESS,
+    /**
+     * The server rejected the authentication request.
+     */
+    FAILURE
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ChannelCondition.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * Contains constants that can be used to specify what conditions to wait for on
+ * a SSH-2 channel (e.g., represented by a {@link Session}).
+ *
+ * @see Session#waitForCondition(int, long)
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public abstract interface ChannelCondition {
+    /**
+     * A timeout has occurred, none of your requested conditions is fulfilled.
+     * However, other conditions may be true - therefore, NEVER use the "=="
+     * operator to test for this (or any other) condition. Always use
+     * something like <code>((cond &amp; ChannelCondition.CLOSED) != 0)</code>.
+     */
+    public static final int TIMEOUT = 1;
+
+    /**
+     * The underlying SSH-2 channel, however not necessarily the whole connection,
+     * has been closed. This implies <code>EOF</code>. Note that there may still
+     * be unread stdout or stderr data in the local window, i.e, <code>STDOUT_DATA</code>
+     * or/and <code>STDERR_DATA</code> may be set at the same time.
+     */
+    public static final int CLOSED = 2;
+
+    /**
+     * There is stdout data available that is ready to be consumed.
+     */
+    public static final int STDOUT_DATA = 4;
+
+    /**
+     * There is stderr data available that is ready to be consumed.
+     */
+    public static final int STDERR_DATA = 8;
+
+    /**
+     * EOF on has been reached, no more _new_ stdout or stderr data will arrive
+     * from the remote server. However, there may be unread stdout or stderr
+     * data, i.e, <code>STDOUT_DATA</code> or/and <code>STDERR_DATA</code>
+     * may be set at the same time.
+     */
+    public static final int EOF = 16;
+
+    /**
+     * The exit status of the remote process is available.
+     * Some servers never send the exist status, or occasionally "forget" to do so.
+     */
+    public static final int EXIT_STATUS = 32;
+
+    /**
+     * The exit signal of the remote process is available.
+     */
+    public static final int EXIT_SIGNAL = 64;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/Connection.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,1444 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import ch.ethz.ssh2.auth.AgentProxy;
+import ch.ethz.ssh2.auth.AuthenticationManager;
+import ch.ethz.ssh2.channel.ChannelManager;
+import ch.ethz.ssh2.compression.CompressionFactory;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.packets.PacketIgnore;
+import ch.ethz.ssh2.transport.ClientTransportManager;
+import ch.ethz.ssh2.transport.HTTPProxyClientTransportManager;
+import ch.ethz.ssh2.transport.KexManager;
+import ch.ethz.ssh2.util.TimeoutService;
+import ch.ethz.ssh2.util.TimeoutService.TimeoutToken;
+
+/**
+ * A <code>Connection</code> is used to establish an encrypted TCP/IP
+ * connection to a SSH-2 server.
+ * <p/>
+ * Typically, one
+ * <ol>
+ * <li>creates a {@link #Connection(String) Connection} object.</li>
+ * <li>calls the {@link #connect() connect()} method.</li>
+ * <li>calls some of the authentication methods (e.g., {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
+ * <li>calls one or several times the {@link #openSession() openSession()} method.</li>
+ * <li>finally, one must close the connection and release resources with the {@link #close() close()} method.</li>
+ * </ol>
+ *
+ * @author Christian Plattner
+ * @version $Id: Connection.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+
+public class Connection {
+    /**
+     * The identifier presented to the SSH-2 server. This is the same
+     * as the "softwareversion" defined in RFC 4253.
+     * <p/>
+     * <b>NOTE: As per the RFC, the "softwareversion" string MUST consist of printable
+     * US-ASCII characters, with the exception of whitespace characters and the minus sign (-).</b>
+     */
+    private String softwareversion
+        = String.format("Ganymed_%s", Version.getSpecification());
+
+    /* Will be used to generate all random data needed for the current connection.
+     * Note: SecureRandom.nextBytes() is thread safe.
+     */
+
+    private SecureRandom generator;
+
+    /**
+     * Unless you know what you are doing, you will never need this.
+     *
+     * @return The list of supported cipher algorithms by this implementation.
+     */
+
+    public static synchronized String[] getAvailableCiphers() {
+        return BlockCipherFactory.getDefaultCipherList();
+    }
+
+    /**
+     * Unless you know what you are doing, you will never need this.
+     *
+     * @return The list of supported MAC algorthims by this implementation.
+     */
+
+    public static synchronized String[] getAvailableMACs() {
+        return MAC.getMacList();
+    }
+
+    /**
+     * Unless you know what you are doing, you will never need this.
+     *
+     * @return The list of supported server host key algorthims by this implementation.
+     */
+
+    public static synchronized String[] getAvailableServerHostKeyAlgorithms() {
+        return KexManager.getDefaultServerHostkeyAlgorithmList();
+    }
+
+    private AuthenticationManager am;
+
+    private boolean authenticated;
+    private ChannelManager cm;
+
+    private CryptoWishList cryptoWishList
+        = new CryptoWishList();
+
+    private DHGexParameters dhgexpara
+        = new DHGexParameters();
+
+    private final String hostname;
+
+    private final int port;
+
+    private ClientTransportManager tm;
+
+    private boolean tcpNoDelay = false;
+
+    private HTTPProxyData proxy;
+
+    private List<ConnectionMonitor> connectionMonitors
+        = new ArrayList<ConnectionMonitor>();
+
+    /**
+     * Prepares a fresh <code>Connection</code> object which can then be used
+     * to establish a connection to the specified SSH-2 server.
+     * <p/>
+     * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
+     *
+     * @param hostname the hostname of the SSH-2 server.
+     */
+    public Connection(String hostname) {
+        this(hostname, 22);
+    }
+
+    /**
+     * Prepares a fresh <code>Connection</code> object which can then be used
+     * to establish a connection to the specified SSH-2 server.
+     *
+     * @param hostname the host where we later want to connect to.
+     * @param port     port on the server, normally 22.
+     */
+    public Connection(String hostname, int port) {
+        this.hostname = hostname;
+        this.port = port;
+    }
+
+    /**
+     * Prepares a fresh <code>Connection</code> object which can then be used
+     * to establish a connection to the specified SSH-2 server.
+     *
+     * @param hostname        the host where we later want to connect to.
+     * @param port            port on the server, normally 22.
+     * @param softwareversion Allows you to set a custom "softwareversion" string as defined in RFC 4253.
+     *                        <b>NOTE: As per the RFC, the "softwareversion" string MUST consist of printable
+     *                        US-ASCII characters, with the exception of whitespace characters and the minus sign (-).</b>
+     */
+    public Connection(String hostname, int port, String softwareversion) {
+        this.hostname = hostname;
+        this.port = port;
+        this.softwareversion = softwareversion;
+    }
+
+    public Connection(String hostname, int port, final HTTPProxyData proxy) {
+        this.hostname = hostname;
+        this.port = port;
+        this.proxy = proxy;
+    }
+
+    public Connection(String hostname, int port, String softwareversion, final HTTPProxyData proxy) {
+        this.hostname = hostname;
+        this.port = port;
+        this.softwareversion = softwareversion;
+        this.proxy = proxy;
+    }
+
+    /**
+     * After a successful connect, one has to authenticate oneself. This method
+     * is based on DSA (it uses DSA to sign a challenge sent by the server).
+     * <p/>
+     * If the authentication phase is complete, <code>true</code> will be
+     * returned. If the server does not accept the request (or if further
+     * authentication steps are needed), <code>false</code> is returned and
+     * one can retry either by using this or any other authentication method
+     * (use the <code>getRemainingAuthMethods</code> method to get a list of
+     * the remaining possible methods).
+     *
+     * @param user     A <code>String</code> holding the username.
+     * @param pem      A <code>String</code> containing the DSA private key of the
+     *                 user in OpenSSH key format (PEM, you can't miss the
+     *                 "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
+     *                 linefeeds.
+     * @param password If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
+     *                 must specify the password. Otherwise, this argument will be
+     *                 ignored and can be set to <code>null</code>.
+     * @return whether the connection is now authenticated.
+     * @throws IOException
+     * @deprecated You should use one of the {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
+     * methods, this method is just a wrapper for it and will
+     * disappear in future builds.
+     */
+    @Deprecated
+
+    public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException {
+        if (tm == null) {
+            throw new IllegalStateException("Connection is not established!");
+        }
+
+        if (authenticated) {
+            throw new IllegalStateException("Connection is already authenticated!");
+        }
+
+        if (am == null) {
+            am = new AuthenticationManager(tm);
+        }
+
+        if (cm == null) {
+            cm = new ChannelManager(tm);
+        }
+
+        if (user == null) {
+            throw new IllegalArgumentException("user argument is null");
+        }
+
+        if (pem == null) {
+            throw new IllegalArgumentException("pem argument is null");
+        }
+
+        authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());
+        return authenticated;
+    }
+
+    /**
+     * A wrapper that calls {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
+     * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod list.
+     *
+     * @param user A <code>String</code> holding the username.
+     * @param cb   An <code>InteractiveCallback</code> which will be used to
+     *             determine the responses to the questions asked by the server.
+     * @return whether the connection is now authenticated.
+     * @throws IOException
+     */
+
+    public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
+    throws IOException {
+        return authenticateWithKeyboardInteractive(user, null, cb);
+    }
+
+    /**
+     * After a successful connect, one has to authenticate oneself. This method
+     * is based on "keyboard-interactive", specified in
+     * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
+     * callback object which will be feeded with challenges generated by the
+     * server. Answers are then sent back to the server. It is possible that the
+     * callback will be called several times during the invocation of this
+     * method (e.g., if the server replies to the callback's answer(s) with
+     * another challenge...)
+     * <p/>
+     * If the authentication phase is complete, <code>true</code> will be
+     * returned. If the server does not accept the request (or if further
+     * authentication steps are needed), <code>false</code> is returned and
+     * one can retry either by using this or any other authentication method
+     * (use the <code>getRemainingAuthMethods</code> method to get a list of
+     * the remaining possible methods).
+     * <p/>
+     * Note: some SSH servers advertise "keyboard-interactive", however, any
+     * interactive request will be denied (without having sent any challenge to
+     * the client).
+     *
+     * @param user       A <code>String</code> holding the username.
+     * @param submethods An array of submethod names, see
+     *                   draft-ietf-secsh-auth-kbdinteract-XX. May be <code>null</code>
+     *                   to indicate an empty list.
+     * @param cb         An <code>InteractiveCallback</code> which will be used to
+     *                   determine the responses to the questions asked by the server.
+     * @return whether the connection is now authenticated.
+     * @throws IOException
+     */
+
+    public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
+            InteractiveCallback cb) throws IOException {
+        if (cb == null) {
+            throw new IllegalArgumentException("Callback may not ne NULL!");
+        }
+
+        if (tm == null) {
+            throw new IllegalStateException("Connection is not established!");
+        }
+
+        if (authenticated) {
+            throw new IllegalStateException("Connection is already authenticated!");
+        }
+
+        if (am == null) {
+            am = new AuthenticationManager(tm);
+        }
+
+        if (cm == null) {
+            cm = new ChannelManager(tm);
+        }
+
+        if (user == null) {
+            throw new IllegalArgumentException("user argument is null");
+        }
+
+        authenticated = am.authenticateInteractive(user, submethods, cb);
+        return authenticated;
+    }
+
+    public synchronized boolean authenticateWithAgent(String user, AgentProxy proxy) throws IOException {
+        if (tm == null) {
+            throw new IllegalStateException("Connection is not established!");
+        }
+
+        if (authenticated) {
+            throw new IllegalStateException("Connection is already authenticated!");
+        }
+
+        if (am == null) {
+            am = new AuthenticationManager(tm);
+        }
+
+        if (cm == null) {
+            cm = new ChannelManager(tm);
+        }
+
+        if (user == null) {
+            throw new IllegalArgumentException("user argument is null");
+        }
+
+        authenticated = am.authenticatePublicKey(user, proxy);
+        return authenticated;
+    }
+
+    /**
+     * After a successful connect, one has to authenticate oneself. This method
+     * sends username and password to the server.
+     * <p/>
+     * If the authentication phase is complete, <code>true</code> will be
+     * returned. If the server does not accept the request (or if further
+     * authentication steps are needed), <code>false</code> is returned and
+     * one can retry either by using this or any other authentication method
+     * (use the <code>getRemainingAuthMethods</code> method to get a list of
+     * the remaining possible methods).
+     * <p/>
+     * Note: if this method fails, then please double-check that it is actually
+     * offered by the server (use {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
+     * <p/>
+     * Often, password authentication is disabled, but users are not aware of it.
+     * Many servers only offer "publickey" and "keyboard-interactive". However,
+     * even though "keyboard-interactive" *feels* like password authentication
+     * (e.g., when using the putty or openssh clients) it is *not* the same mechanism.
+     *
+     * @param user
+     * @param password
+     * @return if the connection is now authenticated.
+     * @throws IOException
+     */
+
+    public synchronized boolean authenticateWithPassword(String user, String password) throws IOException {
+        if (tm == null) {
+            throw new IllegalStateException("Connection is not established!");
+        }
+
+        if (authenticated) {
+            throw new IllegalStateException("Connection is already authenticated!");
+        }
+
+        if (am == null) {
+            am = new AuthenticationManager(tm);
+        }
+
+        if (cm == null) {
+            cm = new ChannelManager(tm);
+        }
+
+        if (user == null) {
+            throw new IllegalArgumentException("user argument is null");
+        }
+
+        if (password == null) {
+            throw new IllegalArgumentException("password argument is null");
+        }
+
+        authenticated = am.authenticatePassword(user, password);
+        return authenticated;
+    }
+
+    /**
+     * After a successful connect, one has to authenticate oneself.
+     * This method can be used to explicitly use the special "none"
+     * authentication method (where only a username has to be specified).
+     * <p/>
+     * Note 1: The "none" method may always be tried by clients, however as by
+     * the specs, the server will not explicitly announce it. In other words,
+     * the "none" token will never show up in the list returned by
+     * {@link #getRemainingAuthMethods(String)}.
+     * <p/>
+     * Note 2: no matter which one of the authenticateWithXXX() methods
+     * you call, the library will always issue exactly one initial "none"
+     * authentication request to retrieve the initially allowed list of
+     * authentication methods by the server. Please read RFC 4252 for the
+     * details.
+     * <p/>
+     * If the authentication phase is complete, <code>true</code> will be
+     * returned. If further authentication steps are needed, <code>false</code>
+     * is returned and one can retry by any other authentication method
+     * (use the <code>getRemainingAuthMethods</code> method to get a list of
+     * the remaining possible methods).
+     *
+     * @param user
+     * @return if the connection is now authenticated.
+     * @throws IOException
+     */
+
+    public synchronized boolean authenticateWithNone(String user) throws IOException {
+        if (tm == null) {
+            throw new IllegalStateException("Connection is not established!");
+        }
+
+        if (authenticated) {
+            throw new IllegalStateException("Connection is already authenticated!");
+        }
+
+        if (am == null) {
+            am = new AuthenticationManager(tm);
+        }
+
+        if (cm == null) {
+            cm = new ChannelManager(tm);
+        }
+
+        if (user == null) {
+            throw new IllegalArgumentException("user argument is null");
+        }
+
+        /* Trigger the sending of the PacketUserauthRequestNone packet */
+        /* (if not already done)                                       */
+        authenticated = am.authenticateNone(user);
+        return authenticated;
+    }
+
+    /**
+     * After a successful connect, one has to authenticate oneself.
+     * The authentication method "publickey" works by signing a challenge
+     * sent by the server. The signature is either DSA or RSA based - it
+     * just depends on the type of private key you specify, either a DSA
+     * or RSA private key in PEM format. And yes, this is may seem to be a
+     * little confusing, the method is called "publickey" in the SSH-2 protocol
+     * specification, however since we need to generate a signature, you
+     * actually have to supply a private key =).
+     * <p/>
+     * The private key contained in the PEM file may also be encrypted ("Proc-Type: 4,ENCRYPTED").
+     * The library supports DES-CBC and DES-EDE3-CBC encryption, as well
+     * as the more exotic PEM encrpytions AES-128-CBC, AES-192-CBC and AES-256-CBC.
+     * <p/>
+     * If the authentication phase is complete, <code>true</code> will be
+     * returned. If the server does not accept the request (or if further
+     * authentication steps are needed), <code>false</code> is returned and
+     * one can retry either by using this or any other authentication method
+     * (use the <code>getRemainingAuthMethods</code> method to get a list of
+     * the remaining possible methods).
+     * <p/>
+     * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
+     * it is not in the expected format. You have to convert it to the OpenSSH
+     * key format by using the "puttygen" tool (can be downloaded from the Putty
+     * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
+     * functionality to get a proper PEM file.
+     *
+     * @param user          A <code>String</code> holding the username.
+     * @param pemPrivateKey A <code>char[]</code> containing a DSA or RSA private key of the
+     *                      user in OpenSSH key format (PEM, you can't miss the
+     *                      "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
+     *                      tag). The char array may contain linebreaks/linefeeds.
+     * @param password      If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED") then
+     *                      you must specify a password. Otherwise, this argument will be ignored
+     *                      and can be set to <code>null</code>.
+     * @return whether the connection is now authenticated.
+     * @throws IOException
+     */
+
+    public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
+    throws IOException {
+        if (tm == null) {
+            throw new IllegalStateException("Connection is not established!");
+        }
+
+        if (authenticated) {
+            throw new IllegalStateException("Connection is already authenticated!");
+        }
+
+        if (am == null) {
+            am = new AuthenticationManager(tm);
+        }
+
+        if (cm == null) {
+            cm = new ChannelManager(tm);
+        }
+
+        if (user == null) {
+            throw new IllegalArgumentException("user argument is null");
+        }
+
+        if (pemPrivateKey == null) {
+            throw new IllegalArgumentException("pemPrivateKey argument is null");
+        }
+
+        authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());
+        return authenticated;
+    }
+
+    /**
+     * After a successful connect, one has to authenticate oneself. The
+     * authentication method "publickey" works by signing a challenge sent by
+     * the server. The signature is either DSA or RSA based - it just depends on
+     * the type of private key you specify, either a DSA or RSA private key in
+     * PEM format. And yes, this is may seem to be a little confusing, the
+     * method is called "publickey" in the SSH-2 protocol specification, however
+     * since we need to generate a signature, you actually have to supply a
+     * private key =).
+     * <p>
+     * If the authentication phase is complete, <code>true</code> will be
+     * returned. If the server does not accept the request (or if further
+     * authentication steps are needed), <code>false</code> is returned and
+     * one can retry either by using this or any other authentication method
+     * (use the <code>getRemainingAuthMethods</code> method to get a list of
+     * the remaining possible methods).
+     *
+     * @param user
+     *            A <code>String</code> holding the username.
+     * @param pair
+     *            A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code>
+     *            containing a DSA or RSA private key of
+     *            the user in Trilead object format.
+     *
+     * @return whether the connection is now authenticated.
+     * @throws IOException
+     */
+
+    public synchronized boolean authenticateWithPublicKey(String user, KeyPair pair)
+    throws IOException {
+        if (tm == null)
+            throw new IllegalStateException("Connection is not established!");
+
+        if (authenticated)
+            throw new IllegalStateException("Connection is already authenticated!");
+
+        if (am == null)
+            am = new AuthenticationManager(tm);
+
+        if (cm == null)
+            cm = new ChannelManager(tm);
+
+        if (user == null)
+            throw new IllegalArgumentException("user argument is null");
+
+        if (pair == null)
+            throw new IllegalArgumentException("Key pair argument is null");
+
+        authenticated = am.authenticatePublicKey(user, pair, getOrCreateSecureRND());
+        return authenticated;
+    }
+
+    /**
+     * A convenience wrapper function which reads in a private key (PEM format, either DSA or RSA)
+     * and then calls <code>authenticateWithPublicKey(String, char[], String)</code>.
+     * <p/>
+     * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
+     * it is not in the expected format. You have to convert it to the OpenSSH
+     * key format by using the "puttygen" tool (can be downloaded from the Putty
+     * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
+     * functionality to get a proper PEM file.
+     *
+     * @param user     A <code>String</code> holding the username.
+     * @param pemFile  A <code>File</code> object pointing to a file containing a DSA or RSA
+     *                 private key of the user in OpenSSH key format (PEM, you can't miss the
+     *                 "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
+     *                 tag).
+     * @param password If the PEM file is encrypted then you must specify the password.
+     *                 Otherwise, this argument will be ignored and can be set to <code>null</code>.
+     * @return whether the connection is now authenticated.
+     * @throws IOException
+     */
+
+    public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
+    throws IOException {
+        if (pemFile == null) {
+            throw new IllegalArgumentException("pemFile argument is null");
+        }
+
+        char[] buff = new char[256];
+        CharArrayWriter cw = new CharArrayWriter();
+        FileReader fr = new FileReader(pemFile);
+
+        while (true) {
+            int len = fr.read(buff);
+
+            if (len < 0) {
+                break;
+            }
+
+            cw.write(buff, 0, len);
+        }
+
+        fr.close();
+        return authenticateWithPublicKey(user, cw.toCharArray(), password);
+    }
+
+    /**
+     * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any time,
+     * but it is best to add connection monitors before invoking
+     * <code>connect()</code> to avoid glitches (e.g., you add a connection monitor after
+     * a successful connect(), but the connection has died in the mean time. Then,
+     * your connection monitor won't be notified.)
+     * <p/>
+     * You can add as many monitors as you like. If a monitor has already been added, then
+     * this method does nothing.
+     *
+     * @param cmon An object implementing the {@link ConnectionMonitor} interface.
+     * @see ConnectionMonitor
+     */
+
+    public synchronized void addConnectionMonitor(ConnectionMonitor cmon) {
+        if (!connectionMonitors.contains(cmon)) {
+            connectionMonitors.add(cmon);
+
+            if (tm != null) {
+                tm.setConnectionMonitors(connectionMonitors);
+            }
+        }
+    }
+
+    /**
+     * Remove a {@link ConnectionMonitor} from this connection.
+     *
+     * @param cmon
+     * @return whether the monitor could be removed
+     */
+
+    public synchronized boolean removeConnectionMonitor(ConnectionMonitor cmon) {
+        boolean existed = connectionMonitors.remove(cmon);
+
+        if (tm != null) {
+            tm.setConnectionMonitors(connectionMonitors);
+        }
+
+        return existed;
+    }
+
+    /**
+     * Controls whether compression is used on the link or not.
+     * <p>
+     * Note: This can only be called before connect()
+     * @param enabled whether to enable compression
+     * @throws IOException
+     */
+
+    public synchronized void setCompression(boolean enabled) throws IOException {
+        if (tm != null)
+            throw new IOException("Connection to " + hostname + " is already in connected state!");
+
+        if (enabled) enableCompression();
+        else         disableCompression();
+    }
+
+    /**
+     * Close the connection to the SSH-2 server. All assigned sessions will be
+     * closed, too. Can be called at any time. Don't forget to call this once
+     * you don't need a connection anymore - otherwise the receiver thread may
+     * run forever.
+     */
+
+    public synchronized void close() {
+        Throwable t = new Throwable("Closed due to user request.");
+        close(t, false);
+    }
+
+    public synchronized void close(Throwable t, boolean hard) {
+        if (cm != null) {
+            cm.closeAllChannels();
+        }
+
+        if (tm != null) {
+            tm.close(t, hard == false);
+            tm = null;
+        }
+
+        am = null;
+        cm = null;
+        authenticated = false;
+    }
+
+    /**
+     * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
+     *
+     * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
+     * @throws IOException
+     */
+
+    public synchronized ConnectionInfo connect() throws IOException {
+        return connect(null, 0, 0);
+    }
+
+    /**
+     * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
+     *
+     * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
+     * @throws IOException
+     */
+
+    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException {
+        return connect(verifier, 0, 0);
+    }
+
+    /**
+     * Connect to the SSH-2 server and, as soon as the server has presented its
+     * host key, use the {@link ServerHostKeyVerifier#verifyServerHostKey(String,
+     * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()}
+     * method of the <code>verifier</code> to ask for permission to proceed.
+     * If <code>verifier</code> is <code>null</code>, then any host key will be
+     * accepted - this is NOT recommended, since it makes man-in-the-middle attackes
+     * VERY easy (somebody could put a proxy SSH server between you and the real server).
+     * <p/>
+     * Note: The verifier will be called before doing any crypto calculations
+     * (i.e., diffie-hellman). Therefore, if you don't like the presented host key then
+     * no CPU cycles are wasted (and the evil server has less information about us).
+     * <p/>
+     * However, it is still possible that the server presented a fake host key: the server
+     * cheated (typically a sign for a man-in-the-middle attack) and is not able to generate
+     * a signature that matches its host key. Don't worry, the library will detect such
+     * a scenario later when checking the signature (the signature cannot be checked before
+     * having completed the diffie-hellman exchange).
+     * <p/>
+     * Note 2: The  {@link ServerHostKeyVerifier#verifyServerHostKey(String,
+     * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method
+     * will *NOT* be called from the current thread, the call is being made from a
+     * background thread (there is a background dispatcher thread for every
+     * established connection).
+     * <p/>
+     * Note 3: This method will block as long as the key exchange of the underlying connection
+     * has not been completed (and you have not specified any timeouts).
+     * <p/>
+     * Note 4: If you want to re-use a connection object that was successfully connected,
+     * then you must call the {@link #close()} method before invoking <code>connect()</code> again.
+     *
+     * @param verifier       An object that implements the
+     *                       {@link ServerHostKeyVerifier} interface. Pass <code>null</code>
+     *                       to accept any server host key - NOT recommended.
+     * @param connectTimeout Connect the underlying TCP socket to the server with the given timeout
+     *                       value (non-negative, in milliseconds). Zero means no timeout.
+     * @param kexTimeout     Timeout for complete connection establishment (non-negative,
+     *                       in milliseconds). Zero means no timeout. The timeout counts from the
+     *                       moment you invoke the connect() method and is cancelled as soon as the
+     *                       first key-exchange round has finished. It is possible that
+     *                       the timeout event will be fired during the invocation of the
+     *                       <code>verifier</code> callback, but it will only have an effect after
+     *                       the <code>verifier</code> returns.
+     * @return A {@link ConnectionInfo} object containing the details of
+     * the established connection.
+     * @throws IOException If any problem occurs, e.g., the server's host key is not
+     *                     accepted by the <code>verifier</code> or there is problem during
+     *                     the initial crypto setup (e.g., the signature sent by the server is wrong).
+     *                     <p/>
+     *                     In case of a timeout (either connectTimeout or kexTimeout)
+     *                     a SocketTimeoutException is thrown.
+     *                     <p/>
+     *                     An exception may also be thrown if the connection was already successfully
+     *                     connected (no matter if the connection broke in the mean time) and you invoke
+     *                     <code>connect()</code> again without having called {@link #close()} first.
+     *                     <p/>
+     *                     If a HTTP proxy is being used and the proxy refuses the connection,
+     *                     then a {@link HTTPProxyException} may be thrown, which
+     *                     contains the details returned by the proxy. If the proxy is buggy and does
+     *                     not return a proper HTTP response, then a normal IOException is thrown instead.
+     */
+
+    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
+    throws IOException {
+        final class TimeoutState {
+            boolean isCancelled = false;
+            boolean timeoutSocketClosed = false;
+        }
+
+        if (tm != null) {
+            throw new IllegalStateException(String.format("Connection to %s is already in connected state", hostname));
+        }
+
+        if (connectTimeout < 0) {
+            throw new IllegalArgumentException("connectTimeout must be non-negative!");
+        }
+
+        if (kexTimeout < 0) {
+            throw new IllegalArgumentException("kexTimeout must be non-negative!");
+        }
+
+        final TimeoutState state = new TimeoutState();
+
+        if (null == proxy) {
+            tm = new ClientTransportManager(new Socket());
+        }
+        else {
+            tm = new HTTPProxyClientTransportManager(new Socket(), proxy);
+        }
+
+        tm.setSoTimeout(connectTimeout);
+        tm.setTcpNoDelay(tcpNoDelay);
+        tm.setConnectionMonitors(connectionMonitors);
+
+        try {
+            TimeoutToken token = null;
+
+            if (kexTimeout > 0) {
+                final Runnable timeoutHandler = new Runnable() {
+                    public void run() {
+                        synchronized (state) {
+                            if (state.isCancelled) {
+                                return;
+                            }
+
+                            state.timeoutSocketClosed = true;
+                            tm.close(new SocketTimeoutException("The connect timeout expired"), false);
+                        }
+                    }
+                };
+                long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
+                token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
+            }
+
+            tm.connect(hostname, port, softwareversion, cryptoWishList, verifier, dhgexpara, connectTimeout,
+                       getOrCreateSecureRND());
+            /* Wait until first KEX has finished */
+            ConnectionInfo ci = tm.getConnectionInfo(1);
+
+            /* Now try to cancel the timeout, if needed */
+
+            if (token != null) {
+                TimeoutService.cancelTimeoutHandler(token);
+
+                /* Were we too late? */
+
+                synchronized (state) {
+                    if (state.timeoutSocketClosed) {
+                        throw new IOException("This exception will be replaced by the one below =)");
+                    }
+
+                    /* Just in case the "cancelTimeoutHandler" invocation came just a little bit
+                     * too late but the handler did not enter the semaphore yet - we can
+                     * still stop it.
+                     */
+                    state.isCancelled = true;
+                }
+            }
+
+            return ci;
+        }
+        catch (SocketTimeoutException e) {
+            throw e;
+        }
+        catch (HTTPProxyException e) {
+            throw e;
+        }
+        catch (IOException e) {
+            // This will also invoke any registered connection monitors
+            close(e, false);
+
+            synchronized (state) {
+                /* Show a clean exception, not something like "the socket is closed!?!" */
+                if (state.timeoutSocketClosed) {
+                    throw new SocketTimeoutException(String.format("The kexTimeout (%d ms) expired.", kexTimeout));
+                }
+            }
+
+            throw e;
+        }
+    }
+
+    /**
+     * Creates a new {@link LocalPortForwarder}.
+     * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
+     * port via the secure tunnel to another host (which may or may not be
+     * identical to the remote SSH-2 server).
+     * <p/>
+     * This method must only be called after one has passed successfully the authentication step.
+     * There is no limit on the number of concurrent forwardings.
+     *
+     * @param local_port      the local port the LocalPortForwarder shall bind to.
+     * @param host_to_connect target address (IP or hostname)
+     * @param port_to_connect target port
+     * @return A {@link LocalPortForwarder} object.
+     * @throws IOException
+     */
+
+    public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
+            int port_to_connect) throws IOException {
+        this.checkConnection();
+        return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
+    }
+
+    /**
+     * Creates a new {@link LocalPortForwarder}.
+     * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
+     * port via the secure tunnel to another host (which may or may not be
+     * identical to the remote SSH-2 server).
+     * <p/>
+     * This method must only be called after one has passed successfully the authentication step.
+     * There is no limit on the number of concurrent forwardings.
+     *
+     * @param addr            specifies the InetSocketAddress where the local socket shall be bound to.
+     * @param host_to_connect target address (IP or hostname)
+     * @param port_to_connect target port
+     * @return A {@link LocalPortForwarder} object.
+     * @throws IOException
+     */
+
+    public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect,
+            int port_to_connect) throws IOException {
+        this.checkConnection();
+        return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect);
+    }
+
+    /**
+     * Creates a new {@link LocalStreamForwarder}.
+     * A <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
+     * that is being forwarded via the secure tunnel into a TCP/IP connection to another host
+     * (which may or may not be identical to the remote SSH-2 server).
+     *
+     * @param host_to_connect
+     * @param port_to_connect
+     * @return A {@link LocalStreamForwarder} object.
+     * @throws IOException
+     */
+
+    public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
+    throws IOException {
+        this.checkConnection();
+        return new LocalStreamForwarder(cm, host_to_connect, port_to_connect);
+    }
+
+    /**
+     * Creates a new {@link DynamicPortForwarder}. A
+     * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
+     * at a local port via the secure tunnel to another host that is chosen via
+     * the SOCKS protocol.
+     * <p>
+     * This method must only be called after one has passed successfully the
+     * authentication step. There is no limit on the number of concurrent
+     * forwardings.
+     *
+     * @param local_port
+     * @return A {@link DynamicPortForwarder} object.
+     * @throws IOException
+     */
+
+    public synchronized DynamicPortForwarder createDynamicPortForwarder(int local_port) throws IOException {
+        if (tm == null)
+            throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
+
+        if (!authenticated)
+            throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
+
+        return new DynamicPortForwarder(cm, local_port);
+    }
+
+    /**
+     * Creates a new {@link DynamicPortForwarder}. A
+     * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
+     * at a local port via the secure tunnel to another host that is chosen via
+     * the SOCKS protocol.
+     * <p>
+     * This method must only be called after one has passed successfully the
+     * authentication step. There is no limit on the number of concurrent
+     * forwardings.
+     *
+     * @param addr
+     *            specifies the InetSocketAddress where the local socket shall
+     *            be bound to.
+     * @return A {@link DynamicPortForwarder} object.
+     * @throws IOException
+     */
+
+    public synchronized DynamicPortForwarder createDynamicPortForwarder(InetSocketAddress addr) throws IOException {
+        if (tm == null)
+            throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
+
+        if (!authenticated)
+            throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
+
+        return new DynamicPortForwarder(cm, addr);
+    }
+
+    /**
+     * Create a very basic {@link SCPClient} that can be used to copy
+     * files from/to the SSH-2 server.
+     * <p/>
+     * Works only after one has passed successfully the authentication step.
+     * There is no limit on the number of concurrent SCP clients.
+     * <p/>
+     * Note: This factory method will probably disappear in the future.
+     *
+     * @return A {@link SCPClient} object.
+     * @throws IOException
+     */
+
+    public synchronized SCPClient createSCPClient() throws IOException {
+        this.checkConnection();
+        return new SCPClient(this);
+    }
+
+    /**
+     * Force an asynchronous key re-exchange (the call does not block). The
+     * latest values set for MAC, Cipher and DH group exchange parameters will
+     * be used. If a key exchange is currently in progress, then this method has
+     * the only effect that the so far specified parameters will be used for the
+     * next (server driven) key exchange.
+     * <p/>
+     * Note: This implementation will never start a key exchange (other than the initial one)
+     * unless you or the SSH-2 server ask for it.
+     *
+     * @throws IOException In case of any failure behind the scenes.
+     */
+
+    public synchronized void forceKeyExchange() throws IOException {
+        this.checkConnection();
+        tm.forceKeyExchange(cryptoWishList, dhgexpara, null, null, null);
+    }
+
+    /**
+     * Returns the hostname that was passed to the constructor.
+     *
+     * @return the hostname
+     */
+
+    public synchronized String getHostname() {
+        return hostname;
+    }
+
+    /**
+     * Returns the port that was passed to the constructor.
+     *
+     * @return the TCP port
+     */
+
+    public synchronized int getPort() {
+        return port;
+    }
+
+    /**
+     * Returns a {@link ConnectionInfo} object containing the details of
+     * the connection. Can be called as soon as the connection has been
+     * established (successfully connected).
+     *
+     * @return A {@link ConnectionInfo} object.
+     * @throws IOException In case of any failure behind the scenes.
+     */
+
+    public synchronized ConnectionInfo getConnectionInfo() throws IOException {
+        this.checkConnection();
+        return tm.getConnectionInfo(1);
+    }
+
+    /**
+     * After a successful connect, one has to authenticate oneself. This method
+     * can be used to tell which authentication methods are supported by the
+     * server at a certain stage of the authentication process (for the given
+     * username).
+     * <p/>
+     * Note 1: the username will only be used if no authentication step was done
+     * so far (it will be used to ask the server for a list of possible
+     * authentication methods by sending the initial "none" request). Otherwise,
+     * this method ignores the user name and returns a cached method list
+     * (which is based on the information contained in the last negative server response).
+     * <p/>
+     * Note 2: the server may return method names that are not supported by this
+     * implementation.
+     * <p/>
+     * After a successful authentication, this method must not be called
+     * anymore.
+     *
+     * @param user A <code>String</code> holding the username.
+     * @return a (possibly emtpy) array holding authentication method names.
+     * @throws IOException
+     */
+
+    public synchronized String[] getRemainingAuthMethods(String user) throws IOException {
+        if (user == null) {
+            throw new IllegalArgumentException("user argument may not be NULL!");
+        }
+
+        if (tm == null) {
+            throw new IllegalStateException("Connection is not established!");
+        }
+
+        if (authenticated) {
+            throw new IllegalStateException("Connection is already authenticated!");
+        }
+
+        if (am == null) {
+            am = new AuthenticationManager(tm);
+        }
+
+        if (cm == null) {
+            cm = new ChannelManager(tm);
+        }
+
+        final Set<String> remainingMethods = am.getRemainingMethods(user);
+        return remainingMethods.toArray(new String[remainingMethods.size()]);
+    }
+
+    /**
+     * Determines if the authentication phase is complete. Can be called at any
+     * time.
+     *
+     * @return <code>true</code> if no further authentication steps are
+     * needed.
+     */
+
+    public synchronized boolean isAuthenticationComplete() {
+        return authenticated;
+    }
+
+    /**
+     * Returns true if there was at least one failed authentication request and
+     * the last failed authentication request was marked with "partial success"
+     * by the server. This is only needed in the rare case of SSH-2 server setups
+     * that cannot be satisfied with a single successful authentication request
+     * (i.e., multiple authentication steps are needed.)
+     * <p/>
+     * If you are interested in the details, then have a look at RFC4252.
+     *
+     * @return if the there was a failed authentication step and the last one
+     * was marked as a "partial success".
+     */
+
+    public synchronized boolean isAuthenticationPartialSuccess() {
+        if (am == null) {
+            return false;
+        }
+
+        return am.getPartialSuccess();
+    }
+
+    /**
+     * Checks if a specified authentication method is available. This method is
+     * actually just a wrapper for {@link #getRemainingAuthMethods(String)
+     * getRemainingAuthMethods()}.
+     *
+     * @param user   A <code>String</code> holding the username.
+     * @param method An authentication method name (e.g., "publickey", "password",
+     *               "keyboard-interactive") as specified by the SSH-2 standard.
+     * @return if the specified authentication method is currently available.
+     * @throws IOException
+     */
+
+    public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException {
+        String methods[] = getRemainingAuthMethods(user);
+
+        for (final String m : methods) {
+            if (m.compareTo(method) == 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private SecureRandom getOrCreateSecureRND() {
+        if (generator == null) {
+            generator = new SecureRandom();
+        }
+
+        return generator;
+    }
+
+    /**
+     * Open a new {@link Session} on this connection. Works only after one has passed
+     * successfully the authentication step. There is no limit on the number of
+     * concurrent sessions.
+     *
+     * @return A {@link Session} object.
+     * @throws IOException
+     */
+
+    public synchronized Session openSession() throws IOException {
+        this.checkConnection();
+        return new Session(cm, getOrCreateSecureRND());
+    }
+
+    /**
+     * Send an SSH_MSG_IGNORE packet. This method will generate a random data attribute
+     * (length between 0 (invlusive) and 16 (exclusive) bytes, contents are random bytes).
+     * <p/>
+     * This method must only be called once the connection is established.
+     *
+     * @throws IOException
+     */
+
+    public synchronized void sendIgnorePacket() throws IOException {
+        SecureRandom rnd = getOrCreateSecureRND();
+        byte[] data = new byte[rnd.nextInt(16)];
+        rnd.nextBytes(data);
+        sendIgnorePacket(data);
+    }
+
+    /**
+     * Send an SSH_MSG_IGNORE packet with the given data attribute.
+     * <p/>
+     * This method must only be called once the connection is established.
+     *
+     * @throws IOException
+     */
+
+    public synchronized void sendIgnorePacket(byte[] data) throws IOException {
+        this.checkConnection();
+        PacketIgnore pi = new PacketIgnore(data);
+        tm.sendMessage(pi.getPayload());
+    }
+
+    /**
+     * Controls whether compression is used on the link or not.
+     */
+
+    public synchronized void setCompression(String[] algorithms) {
+        CompressionFactory.checkCompressorList(algorithms);
+        cryptoWishList.c2s_comp_algos = algorithms;
+    }
+
+    public synchronized void enableCompression() {
+        cryptoWishList.c2s_comp_algos = CompressionFactory.getDefaultCompressorList();
+        cryptoWishList.s2c_comp_algos = CompressionFactory.getDefaultCompressorList();
+    }
+
+    public synchronized void disableCompression() {
+        cryptoWishList.c2s_comp_algos = new String[] {"none"};
+        cryptoWishList.s2c_comp_algos = new String[] {"none"};
+    }
+
+    /**
+     * Unless you know what you are doing, you will never need this.
+     */
+
+    public synchronized void setClient2ServerCiphers(final String[] ciphers) {
+        if ((ciphers == null) || (ciphers.length == 0)) {
+            throw new IllegalArgumentException();
+        }
+
+        BlockCipherFactory.checkCipherList(ciphers);
+        cryptoWishList.c2s_enc_algos = ciphers;
+    }
+
+    /**
+     * Unless you know what you are doing, you will never need this.
+     */
+
+    public synchronized void setClient2ServerMACs(final String[] macs) {
+        MAC.checkMacList(macs);
+        cryptoWishList.c2s_mac_algos = macs;
+    }
+
+    /**
+     * Sets the parameters for the diffie-hellman group exchange. Unless you
+     * know what you are doing, you will never need this. Default values are
+     * defined in the {@link DHGexParameters} class.
+     *
+     * @param dgp {@link DHGexParameters}, non null.
+     */
+
+    public synchronized void setDHGexParameters(DHGexParameters dgp) {
+        if (dgp == null) {
+            throw new IllegalArgumentException();
+        }
+
+        dhgexpara = dgp;
+    }
+
+    /**
+     * Unless you know what you are doing, you will never need this.
+     */
+
+    public synchronized void setServer2ClientCiphers(final String[] ciphers) {
+        BlockCipherFactory.checkCipherList(ciphers);
+        cryptoWishList.s2c_enc_algos = ciphers;
+    }
+
+    /**
+     * Unless you know what you are doing, you will never need this.
+     */
+
+    public synchronized void setServer2ClientMACs(final String[] macs) {
+        MAC.checkMacList(macs);
+        cryptoWishList.s2c_mac_algos = macs;
+    }
+
+    /**
+     * Define the set of allowed server host key algorithms to be used for
+     * the following key exchange operations.
+     * <p/>
+     * Unless you know what you are doing, you will never need this.
+     *
+     * @param algos An array of allowed server host key algorithms.
+     *              SSH-2 defines <code>ssh-dss</code> and <code>ssh-rsa</code>.
+     *              The entries of the array must be ordered after preference, i.e.,
+     *              the entry at index 0 is the most preferred one. You must specify
+     *              at least one entry.
+     */
+
+    public synchronized void setServerHostKeyAlgorithms(final String[] algos) {
+        KexManager.checkServerHostkeyAlgorithmsList(algos);
+        cryptoWishList.serverHostKeyAlgorithms = algos;
+    }
+
+    /**
+     * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the underlying socket.
+     * <p/>
+     * Can be called at any time. If the connection has not yet been established
+     * then the passed value will be stored and set after the socket has been set up.
+     * The default value that will be used is <code>false</code>.
+     *
+     * @param enable the argument passed to the <code>Socket.setTCPNoDelay()</code> method.
+     * @throws IOException
+     */
+
+    public synchronized void setTCPNoDelay(boolean enable) throws IOException {
+        tcpNoDelay = enable;
+
+        if (tm != null) {
+            tm.setTcpNoDelay(enable);
+        }
+    }
+
+    /**
+     * Used to tell the library that the connection shall be established through
+     * a proxy server. It only makes sense to call this method before calling
+     * the {@link #connect() connect()} method.
+     * <p>
+     * At the moment, only HTTP proxies are supported.
+     * <p>
+     * Note: This method can be called any number of times. The
+     * {@link #connect() connect()} method will use the value set in the last
+     * preceding invocation of this method.
+     *
+     * @see HTTPProxyData
+     *
+     * @param proxy
+     *            Connection information about the proxy. If <code>null</code>,
+     *            then no proxy will be used (non surprisingly, this is also the
+     *            default).
+     */
+
+    public synchronized void setProxyData(HTTPProxyData proxy) {
+        this.proxy = proxy;
+    }
+
+    /**
+     * Request a remote port forwarding.
+     * If successful, then forwarded connections will be redirected to the given target address.
+     * You can cancle a requested remote port forwarding by calling
+     * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
+     * <p/>
+     * A call of this method will block until the peer either agreed or disagreed to your request-
+     * <p/>
+     * Note 1: this method typically fails if you
+     * <ul>
+     * <li>pass a port number for which the used remote user has not enough permissions (i.e., port
+     * &lt; 1024)</li>
+     * <li>or pass a port number that is already in use on the remote server</li>
+     * <li>or if remote port forwarding is disabled on the server.</li>
+     * </ul>
+     * <p/>
+     * Note 2: (from the openssh man page): By default, the listening socket on the server will be
+     * bound to the loopback interface only. This may be overriden by specifying a bind address.
+     * Specifying a remote bind address will only succeed if the server's <b>GatewayPorts</b> option
+     * is enabled (see sshd_config(5)).
+     *
+     * @param bindAddress   address to bind to on the server:
+     *                      <ul>
+     *                      <li>"" means that connections are to be accepted on all protocol families
+     *                      supported by the SSH implementation</li>
+     *                      <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
+     *                      <li>"::" means to listen on all IPv6 addresses</li>
+     *                      <li>"localhost" means to listen on all protocol families supported by the SSH
+     *                      implementation on loopback addresses only, [RFC3330] and RFC3513]</li>
+     *                      <li>"127.0.0.1" and "::1" indicate listening on the loopback interfaces for
+     *                      IPv4 and IPv6 respectively</li>
+     *                      </ul>
+     * @param bindPort      port number to bind on the server (must be &gt; 0)
+     * @param targetAddress the target address (IP or hostname)
+     * @param targetPort    the target port
+     * @throws IOException
+     */
+
+    public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
+            int targetPort) throws IOException {
+        this.checkConnection();
+
+        if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0)) {
+            throw new IllegalArgumentException();
+        }
+
+        cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
+    }
+
+    /**
+     * Cancel an earlier requested remote port forwarding.
+     * Currently active forwardings will not be affected (e.g., disrupted).
+     * Note that further connection forwarding requests may be received until
+     * this method has returned.
+     *
+     * @param bindPort the allocated port number on the server
+     * @throws IOException if the remote side refuses the cancel request or another low
+     *                     level error occurs (e.g., the underlying connection is closed)
+     */
+
+    public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException {
+        this.checkConnection();
+        cm.requestCancelGlobalForward(bindPort);
+    }
+
+    /**
+     * Provide your own instance of SecureRandom. Can be used, e.g., if you
+     * want to seed the used SecureRandom generator manually.
+     * <p/>
+     * The SecureRandom instance is used during key exchanges, public key authentication,
+     * x11 cookie generation and the like.
+     *
+     * @param rnd a SecureRandom instance
+     */
+
+    public synchronized void setSecureRandom(SecureRandom rnd) {
+        if (rnd == null) {
+            throw new IllegalArgumentException();
+        }
+
+        this.generator = rnd;
+    }
+
+    private void checkConnection() throws IllegalStateException {
+        if (tm == null) {
+            throw new IllegalStateException("You need to establish a connection first.");
+        }
+
+        if (!authenticated) {
+            throw new IllegalStateException("The connection is not authenticated.");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ConnectionInfo.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * In most cases you probably do not need the information contained in here.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class ConnectionInfo {
+    /**
+     * The used key exchange (KEX) algorithm in the latest key exchange.
+     */
+    public String keyExchangeAlgorithm;
+
+    /**
+     * The currently used crypto algorithm for packets from to the client to the
+     * server.
+     */
+    public String clientToServerCryptoAlgorithm;
+    /**
+     * The currently used crypto algorithm for packets from to the server to the
+     * client.
+     */
+    public String serverToClientCryptoAlgorithm;
+
+    /**
+     * The currently used MAC algorithm for packets from to the client to the
+     * server.
+     */
+    public String clientToServerMACAlgorithm;
+    /**
+     * The currently used MAC algorithm for packets from to the server to the
+     * client.
+     */
+    public String serverToClientMACAlgorithm;
+
+    /**
+     * The type of the server host key (currently either "ssh-dss" or
+     * "ssh-rsa").
+     */
+    public String serverHostKeyAlgorithm;
+
+    /**
+     * The server host key that was sent during the latest key exchange.
+     */
+    public byte[] serverHostKey;
+
+    /**
+     * Number of kex exchanges performed on this connection so far.
+     */
+    public int keyExchangeCounter = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ConnectionMonitor.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * A <code>ConnectionMonitor</code> is used to get notified when the
+ * underlying socket of a connection is closed.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public interface ConnectionMonitor {
+    /**
+     * This method is called after the connection's underlying
+     * socket has been closed. E.g., due to the {@link Connection#close()} request of the
+     * user, if the peer closed the connection, due to a fatal error during connect()
+     * (also if the socket cannot be established) or if a fatal error occured on
+     * an established connection.
+     * <p>
+     * This is an experimental feature.
+     * <p>
+     * You MUST NOT make any assumption about the thread that invokes this method.
+     * <p>
+     * <b>Please note: if the connection is not connected (e.g., there was no successful
+     * connect() call), then the invocation of {@link Connection#close()} will NOT trigger
+     * this method.</b>
+     *
+     * @see Connection#addConnectionMonitor(ConnectionMonitor)
+     *
+     * @param reason Includes an indication why the socket was closed.
+     */
+    public void connectionLost(Throwable reason);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/DHGexParameters.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * A <code>DHGexParameters</code> object can be used to specify parameters for
+ * the diffie-hellman group exchange.
+ * <p>
+ * Depending on which constructor is used, either the use of a
+ * <code>SSH_MSG_KEX_DH_GEX_REQUEST</code> or <code>SSH_MSG_KEX_DH_GEX_REQUEST_OLD</code>
+ * can be forced.
+ *
+ * @see Connection#setDHGexParameters(DHGexParameters)
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class DHGexParameters {
+    private final int min_group_len;
+    private final int pref_group_len;
+    private final int max_group_len;
+
+    private static final int MIN_ALLOWED = 1024;
+    private static final int MAX_ALLOWED = 8192;
+
+    /**
+     * Same as calling {@link #DHGexParameters(int, int, int) DHGexParameters(1024, 1024, 4096)}.
+     * This is also the default used by the Connection class.
+     *
+     */
+    public DHGexParameters() {
+        this(1024, 1024, 4096);
+    }
+
+    /**
+     * This constructor can be used to force the sending of a
+     * <code>SSH_MSG_KEX_DH_GEX_REQUEST_OLD</code> request.
+     * Internally, the minimum and maximum group lengths will
+     * be set to zero.
+     *
+     * @param pref_group_len has to be &gt= 1024 and &lt;= 8192
+     */
+    public DHGexParameters(int pref_group_len) {
+        if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
+            throw new IllegalArgumentException("pref_group_len out of range!");
+
+        this.pref_group_len = pref_group_len;
+        this.min_group_len = 0;
+        this.max_group_len = 0;
+    }
+
+    /**
+     * This constructor can be used to force the sending of a
+     * <code>SSH_MSG_KEX_DH_GEX_REQUEST</code> request.
+     * <p>
+     * Note: older OpenSSH servers don't understand this request, in which
+     * case you should use the {@link #DHGexParameters(int)} constructor.
+     * <p>
+     * All values have to be &gt= 1024 and &lt;= 8192. Furthermore,
+     * min_group_len &lt;= pref_group_len &lt;= max_group_len.
+     *
+     * @param min_group_len
+     * @param pref_group_len
+     * @param max_group_len
+     */
+    public DHGexParameters(int min_group_len, int pref_group_len, int max_group_len) {
+        if ((min_group_len < MIN_ALLOWED) || (min_group_len > MAX_ALLOWED))
+            throw new IllegalArgumentException("min_group_len out of range!");
+
+        if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
+            throw new IllegalArgumentException("pref_group_len out of range!");
+
+        if ((max_group_len < MIN_ALLOWED) || (max_group_len > MAX_ALLOWED))
+            throw new IllegalArgumentException("max_group_len out of range!");
+
+        if ((pref_group_len < min_group_len) || (pref_group_len > max_group_len))
+            throw new IllegalArgumentException("pref_group_len is incompatible with min and max!");
+
+        if (max_group_len < min_group_len)
+            throw new IllegalArgumentException("max_group_len must not be smaller than min_group_len!");
+
+        this.min_group_len = min_group_len;
+        this.pref_group_len = pref_group_len;
+        this.max_group_len = max_group_len;
+    }
+
+    /**
+     * Get the maximum group length.
+     *
+     * @return the maximum group length, may be <code>zero</code> if
+     *         SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested
+     */
+    public int getMax_group_len() {
+        return max_group_len;
+    }
+
+    /**
+     * Get the minimum group length.
+     *
+     * @return minimum group length, may be <code>zero</code> if
+     *         SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested
+     */
+    public int getMin_group_len() {
+        return min_group_len;
+    }
+
+    /**
+     * Get the preferred group length.
+     *
+     * @return the preferred group length
+     */
+    public int getPref_group_len() {
+        return pref_group_len;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/DynamicPortForwarder.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,63 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed 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 ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import ch.ethz.ssh2.channel.ChannelManager;
+import ch.ethz.ssh2.channel.DynamicAcceptThread;
+
+/**
+ * A <code>DynamicPortForwarder</code> forwards TCP/IP connections to a local
+ * port via the secure tunnel to another host which is selected via the
+ * SOCKS protocol. Checkout {@link Connection#createDynamicPortForwarder(int)}
+ * on how to create one.
+ *
+ * @author Kenny Root
+ * @version $Id: $
+ */
+public class DynamicPortForwarder {
+    ChannelManager cm;
+
+    DynamicAcceptThread dat;
+
+    DynamicPortForwarder(ChannelManager cm, int local_port)
+    throws IOException {
+        this.cm = cm;
+        dat = new DynamicAcceptThread(cm, local_port);
+        dat.setDaemon(true);
+        dat.start();
+    }
+
+    DynamicPortForwarder(ChannelManager cm, InetSocketAddress addr) throws IOException {
+        this.cm = cm;
+        dat = new DynamicAcceptThread(cm, addr);
+        dat.setDaemon(true);
+        dat.start();
+    }
+
+    /**
+     * Stop TCP/IP forwarding of newly arriving connections.
+     *
+     * @throws IOException
+     */
+    public void close() throws IOException {
+        dat.stopWorking();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/HTTPProxyData.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * A <code>HTTPProxyData</code> object is used to specify the needed connection data
+ * to connect through a HTTP proxy.
+ *
+ * @see Connection#setProxyData(ProxyData)
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class HTTPProxyData implements ProxyData {
+    public final String proxyHost;
+    public final int proxyPort;
+    public final String proxyUser;
+    public final String proxyPass;
+    public final String[] requestHeaderLines;
+
+    /**
+     * Same as calling {@link #HTTPProxyData(String, int, String, String) HTTPProxyData(proxyHost, proxyPort, <code>null</code>, <code>null</code>)}
+     *
+     * @param proxyHost Proxy hostname.
+     * @param proxyPort Proxy port.
+     */
+    public HTTPProxyData(String proxyHost, int proxyPort) {
+        this(proxyHost, proxyPort, null, null);
+    }
+
+    /**
+     * Same as calling {@link #HTTPProxyData(String, int, String, String, String[]) HTTPProxyData(proxyHost, proxyPort, <code>null</code>, <code>null</code>, <code>null</code>)}
+     *
+     * @param proxyHost Proxy hostname.
+     * @param proxyPort Proxy port.
+     * @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
+     * @param proxyPass Password for basic authentication (<code>null</code> if no authentication is needed).
+     */
+    public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass) {
+        this(proxyHost, proxyPort, proxyUser, proxyPass, null);
+    }
+
+    /**
+     * Connection data for a HTTP proxy. It is possible to specify a username and password
+     * if the proxy requires basic authentication. Also, additional request header lines can
+     * be specified (e.g., "User-Agent: CERN-LineMode/2.15 libwww/2.17b3").
+     * <p>
+     * Please note: if you want to use basic authentication, then both <code>proxyUser</code>
+     * and <code>proxyPass</code> must be non-null.
+     * <p>
+     * Here is an example:
+     * <p>
+     * <code>
+     * new HTTPProxyData("192.168.1.1", "3128", "proxyuser", "secret", new String[] {"User-Agent: GanymedBasedClient/1.0", "X-My-Proxy-Option: something"});
+     * </code>
+     *
+     * @param proxyHost Proxy hostname.
+     * @param proxyPort Proxy port.
+     * @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
+     * @param proxyPass Password for basic authentication (<code>null</code> if no authentication is needed).
+     * @param requestHeaderLines An array with additional request header lines (without end-of-line markers)
+     *        that have to be sent to the server. May be <code>null</code>.
+     */
+
+    public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass,
+                         String[] requestHeaderLines) {
+        if (proxyHost == null)
+            throw new IllegalArgumentException("proxyHost must be non-null");
+
+        if (proxyPort < 0)
+            throw new IllegalArgumentException("proxyPort must be non-negative");
+
+        this.proxyHost = proxyHost;
+        this.proxyPort = proxyPort;
+        this.proxyUser = proxyUser;
+        this.proxyPass = proxyPass;
+        this.requestHeaderLines = requestHeaderLines;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/HTTPProxyException.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+/**
+ * May be thrown upon connect() if a HTTP proxy is being used.
+ *
+ * @see Connection#connect()
+ * @see Connection#setProxyData(ProxyData)
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class HTTPProxyException extends IOException {
+    private static final long serialVersionUID = 2241537397104426186L;
+
+    public final String httpResponse;
+    public final int httpErrorCode;
+
+    public HTTPProxyException(String httpResponse, int httpErrorCode) {
+        super("HTTP Proxy Error (" + httpErrorCode + " " + httpResponse + ")");
+        this.httpResponse = httpResponse;
+        this.httpErrorCode = httpErrorCode;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/InteractiveCallback.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * An <code>InteractiveCallback</code> is used to respond to challenges sent
+ * by the server if authentication mode "keyboard-interactive" is selected.
+ *
+ * @see Connection#authenticateWithKeyboardInteractive(String,
+ *      String[], InteractiveCallback)
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public interface InteractiveCallback {
+    /**
+     * This callback interface is used during a "keyboard-interactive"
+     * authentication. Every time the server sends a set of challenges (however,
+     * most often just one challenge at a time), this callback function will be
+     * called to give your application a chance to talk to the user and to
+     * determine the response(s).
+     * <p>
+     * Some copy-paste information from the standard: a command line interface
+     * (CLI) client SHOULD print the name and instruction (if non-empty), adding
+     * newlines. Then for each prompt in turn, the client SHOULD display the
+     * prompt and read the user input. The name and instruction fields MAY be
+     * empty strings, the client MUST be prepared to handle this correctly. The
+     * prompt field(s) MUST NOT be empty strings.
+     * <p>
+     * Please refer to draft-ietf-secsh-auth-kbdinteract-XX.txt for the details.
+     * <p>
+     * Note: clients SHOULD use control character filtering as discussed in
+     * RFC4251 to avoid attacks by including
+     * terminal control characters in the fields to be displayed.
+     *
+     * @param name
+     *            the name String sent by the server.
+     * @param instruction
+     *            the instruction String sent by the server.
+     * @param numPrompts
+     *            number of prompts - may be zero (in this case, you should just
+     *            return a String array of length zero).
+     * @param prompt
+     *            an array (length <code>numPrompts</code>) of Strings
+     * @param echo
+     *            an array (length <code>numPrompts</code>) of booleans. For
+     *            each prompt, the corresponding echo field indicates whether or
+     *            not the user input should be echoed as characters are typed.
+     * @return an array of reponses - the array size must match the parameter
+     *         <code>numPrompts</code>.
+     */
+    public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo)
+    throws Exception;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/KnownHosts.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,813 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+import java.io.BufferedReader;
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.DigestException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import ch.ethz.ssh2.crypto.Base64;
+import ch.ethz.ssh2.crypto.digest.Digest;
+import ch.ethz.ssh2.crypto.digest.HMAC;
+import ch.ethz.ssh2.crypto.digest.MD5;
+import ch.ethz.ssh2.crypto.digest.SHA1;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * The <code>KnownHosts</code> class is a handy tool to verify received server hostkeys
+ * based on the information in <code>known_hosts</code> files (the ones used by OpenSSH).
+ * <p/>
+ * It offers basically an in-memory database for known_hosts entries, as well as some
+ * helper functions. Entries from a <code>known_hosts</code> file can be loaded at construction time.
+ * It is also possible to add more keys later (e.g., one can parse different
+ * <code>known_hosts<code> files).
+ * <p/>
+ * It is a thread safe implementation, therefore, you need only to instantiate one
+ * <code>KnownHosts</code> for your whole application.
+ *
+ * @author Christian Plattner
+ * @version $Id: KnownHosts.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $
+ */
+
+public class KnownHosts {
+    public static final int HOSTKEY_IS_OK = 0;
+    public static final int HOSTKEY_IS_NEW = 1;
+    public static final int HOSTKEY_HAS_CHANGED = 2;
+
+    private class KnownHostsEntry {
+        String[] patterns;
+        PublicKey key;
+
+        KnownHostsEntry(String[] patterns, PublicKey key) {
+            this.patterns = patterns;
+            this.key = key;
+        }
+    }
+
+    private final LinkedList<KnownHostsEntry> publicKeys = new LinkedList<KnownHosts.KnownHostsEntry>();
+
+    public KnownHosts() {
+    }
+
+    public KnownHosts(char[] knownHostsData) throws IOException {
+        initialize(knownHostsData);
+    }
+
+    public KnownHosts(String knownHosts) throws IOException {
+        initialize(new File(knownHosts));
+    }
+
+    public KnownHosts(File knownHosts) throws IOException {
+        initialize(knownHosts);
+    }
+
+    /**
+     * Adds a single public key entry to the database. Note: this will NOT add the public key
+     * to any physical file (e.g., "~/.ssh/known_hosts") - use <code>addHostkeyToFile()</code> for that purpose.
+     * This method is designed to be used in a {@link ServerHostKeyVerifier}.
+     *
+     * @param hostnames              a list of hostname patterns - at least one most be specified. Check out the
+     *                               OpenSSH sshd man page for a description of the pattern matching algorithm.
+     * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
+     * @param serverHostKey          as passed to the {@link ServerHostKeyVerifier}.
+     * @throws IOException
+     */
+    public void addHostkey(String hostnames[], String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException {
+        if (hostnames == null) {
+            throw new IllegalArgumentException("hostnames may not be null");
+        }
+
+        if ("ssh-rsa".equals(serverHostKeyAlgorithm)) {
+            RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
+
+            synchronized (publicKeys) {
+                publicKeys.add(new KnownHostsEntry(hostnames, rpk));
+            }
+        }
+        else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
+            DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
+
+            synchronized (publicKeys) {
+                publicKeys.add(new KnownHostsEntry(hostnames, dpk));
+            }
+        }
+        else if (serverHostKeyAlgorithm.startsWith("ecdsa-sha2-")) {
+            ECPublicKey epk = ECDSASHA2Verify.decodeSSHECDSAPublicKey(serverHostKey);
+
+            synchronized (publicKeys) {
+                publicKeys.add(new KnownHostsEntry(hostnames, epk));
+            }
+        }
+        else {
+            throw new IOException(String.format("Unknown host key type %s", serverHostKeyAlgorithm));
+        }
+    }
+
+    /**
+     * Parses the given known_hosts data and adds entries to the database.
+     *
+     * @param knownHostsData
+     * @throws IOException
+     */
+    public void addHostkeys(char[] knownHostsData) throws IOException {
+        initialize(knownHostsData);
+    }
+
+    /**
+     * Parses the given known_hosts file and adds entries to the database.
+     *
+     * @param knownHosts
+     * @throws IOException
+     */
+    public void addHostkeys(File knownHosts) throws IOException {
+        initialize(knownHosts);
+    }
+
+    /**
+     * Generate the hashed representation of the given hostname. Useful for adding entries
+     * with hashed hostnames to a known_hosts file. (see -H option of OpenSSH key-gen).
+     *
+     * @param hostname
+     * @return the hashed representation, e.g., "|1|cDhrv7zwEUV3k71CEPHnhHZezhA=|Xo+2y6rUXo2OIWRAYhBOIijbJMA="
+     */
+    public static String createHashedHostname(String hostname) throws IOException {
+        SHA1 sha1 = new SHA1();
+        byte[] salt = new byte[sha1.getDigestLength()];
+        new SecureRandom().nextBytes(salt);
+        byte[] hash;
+
+        try {
+            hash = hmacSha1Hash(salt, hostname);
+        }
+        catch (IOException e) {
+            throw new IOException(e);
+        }
+
+        String base64_salt = new String(Base64.encode(salt));
+        String base64_hash = new String(Base64.encode(hash));
+        return String.format("|1|%s|%s", base64_salt, base64_hash);
+    }
+
+    private static byte[] hmacSha1Hash(byte[] salt, String hostname) throws IOException {
+        SHA1 sha1 = new SHA1();
+
+        if (salt.length != sha1.getDigestLength()) {
+            throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")");
+        }
+
+        try {
+            HMAC hmac = new HMAC(sha1, salt, salt.length);
+            hmac.update(StringEncoder.GetBytes(hostname));
+            byte[] dig = new byte[hmac.getDigestLength()];
+            hmac.digest(dig);
+            return dig;
+        }
+        catch (DigestException e) {
+            throw new IOException(e);
+        }
+    }
+
+    private boolean checkHashed(String entry, String hostname) {
+        if (entry.startsWith("|1|") == false) {
+            return false;
+        }
+
+        int delim_idx = entry.indexOf('|', 3);
+
+        if (delim_idx == -1) {
+            return false;
+        }
+
+        String salt_base64 = entry.substring(3, delim_idx);
+        String hash_base64 = entry.substring(delim_idx + 1);
+        byte[] salt;
+        byte[] hash;
+
+        try {
+            salt = Base64.decode(salt_base64.toCharArray());
+            hash = Base64.decode(hash_base64.toCharArray());
+        }
+        catch (IOException e) {
+            return false;
+        }
+
+        SHA1 sha1 = new SHA1();
+
+        if (salt.length != sha1.getDigestLength()) {
+            return false;
+        }
+
+        byte[] dig = new byte[0];
+
+        try {
+            dig = hmacSha1Hash(salt, hostname);
+        }
+        catch (IOException e) {
+            return false;
+        }
+
+        for (int i = 0; i < dig.length; i++) {
+            if (dig[i] != hash[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private int checkKey(String remoteHostname, PublicKey remoteKey) {
+        int result = HOSTKEY_IS_NEW;
+
+        synchronized (publicKeys) {
+            for (KnownHostsEntry ke : publicKeys) {
+                if (hostnameMatches(ke.patterns, remoteHostname) == false) {
+                    continue;
+                }
+
+                boolean res = matchKeys(ke.key, remoteKey);
+
+                if (res == true) {
+                    return HOSTKEY_IS_OK;
+                }
+
+                result = HOSTKEY_HAS_CHANGED;
+            }
+        }
+
+        return result;
+    }
+
+    private List<Object> getAllKeys(String hostname) {
+        List<Object> keys = new ArrayList<Object>();
+
+        synchronized (publicKeys) {
+            for (KnownHostsEntry ke : publicKeys) {
+                if (hostnameMatches(ke.patterns, hostname) == false) {
+                    continue;
+                }
+
+                keys.add(ke.key);
+            }
+        }
+
+        return keys;
+    }
+
+    /**
+     * Try to find the preferred order of hostkey algorithms for the given hostname.
+     * Based on the type of hostkey that is present in the internal database
+     * (i.e., either <code>ssh-rsa</code> or <code>ssh-dss</code>)
+     * an ordered list of hostkey algorithms is returned which can be passed
+     * to <code>Connection.setServerHostKeyAlgorithms</code>.
+     *
+     * @param hostname
+     * @return <code>null</code> if no key for the given hostname is present or
+     * there are keys of multiple types present for the given hostname. Otherwise,
+     * an array with hostkey algorithms is returned (i.e., an array of length 2).
+     */
+    public String[] getPreferredServerHostkeyAlgorithmOrder(String hostname) {
+        String[] algos = recommendHostkeyAlgorithms(hostname);
+
+        if (algos != null) {
+            return algos;
+        }
+
+        InetAddress[] ipAdresses;
+
+        try {
+            ipAdresses = InetAddress.getAllByName(hostname);
+        }
+        catch (UnknownHostException e) {
+            return null;
+        }
+
+        for (int i = 0; i < ipAdresses.length; i++) {
+            algos = recommendHostkeyAlgorithms(ipAdresses[i].getHostAddress());
+
+            if (algos != null) {
+                return algos;
+            }
+        }
+
+        return null;
+    }
+
+    private boolean hostnameMatches(String[] hostpatterns, String hostname) {
+        boolean isMatch = false;
+        boolean negate;
+        hostname = hostname.toLowerCase();
+
+        for (int k = 0; k < hostpatterns.length; k++) {
+            if (hostpatterns[k] == null) {
+                continue;
+            }
+
+            String pattern;
+
+            /* In contrast to OpenSSH we also allow negated hash entries (as well as hashed
+                            * entries in lines with multiple entries).
+                            */
+
+            if ((hostpatterns[k].length() > 0) && (hostpatterns[k].charAt(0) == '!')) {
+                pattern = hostpatterns[k].substring(1);
+                negate = true;
+            }
+            else {
+                pattern = hostpatterns[k];
+                negate = false;
+            }
+
+            /* Optimize, no need to check this entry */
+
+            if ((isMatch) && (negate == false)) {
+                continue;
+            }
+
+            /* Now compare */
+
+            if (pattern.charAt(0) == '|') {
+                if (checkHashed(pattern, hostname)) {
+                    if (negate) {
+                        return false;
+                    }
+
+                    isMatch = true;
+                }
+            }
+            else {
+                pattern = pattern.toLowerCase();
+
+                if ((pattern.indexOf('?') != -1) || (pattern.indexOf('*') != -1)) {
+                    if (pseudoRegex(pattern.toCharArray(), 0, hostname.toCharArray(), 0)) {
+                        if (negate) {
+                            return false;
+                        }
+
+                        isMatch = true;
+                    }
+                }
+                else if (pattern.compareTo(hostname) == 0) {
+                    if (negate) {
+                        return false;
+                    }
+
+                    isMatch = true;
+                }
+            }
+        }
+
+        return isMatch;
+    }
+
+    private void initialize(char[] knownHostsData) throws IOException {
+        BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
+
+        while (true) {
+            String line = br.readLine();
+
+            if (line == null) {
+                break;
+            }
+
+            line = line.trim();
+
+            if (line.startsWith("#")) {
+                continue;
+            }
+
+            String[] arr = line.split(" ");
+
+            if (arr.length >= 3) {
+                if ((arr[1].compareTo("ssh-rsa") == 0) ||
+                        (arr[1].compareTo("ssh-dss") == 0) ||
+                        (arr[1].startsWith("ecdsa-sha2-"))) {
+                    String[] hostnames = arr[0].split(",");
+                    byte[] msg = Base64.decode(arr[2].toCharArray());
+
+                    try {
+                        addHostkey(hostnames, arr[1], msg);
+                    }
+                    catch (IOException e) {
+                        continue;
+                    }
+                }
+            }
+        }
+    }
+
+    private void initialize(File knownHosts) throws IOException {
+        char[] buff = new char[512];
+        CharArrayWriter cw = new CharArrayWriter();
+        knownHosts.createNewFile();
+        FileReader fr = new FileReader(knownHosts);
+
+        while (true) {
+            int len = fr.read(buff);
+
+            if (len < 0) {
+                break;
+            }
+
+            cw.write(buff, 0, len);
+        }
+
+        fr.close();
+        initialize(cw.toCharArray());
+    }
+
+    private final boolean matchKeys(PublicKey key1, PublicKey key2) {
+        return key1.equals(key2);
+    }
+
+    private boolean pseudoRegex(char[] pattern, int i, char[] match, int j) {
+        /* This matching logic is equivalent to the one present in OpenSSH 4.1 */
+        while (true) {
+            /* Are we at the end of the pattern? */
+            if (pattern.length == i) {
+                return (match.length == j);
+            }
+
+            if (pattern[i] == '*') {
+                i++;
+
+                if (pattern.length == i) {
+                    return true;
+                }
+
+                if ((pattern[i] != '*') && (pattern[i] != '?')) {
+                    while (true) {
+                        if ((pattern[i] == match[j]) && pseudoRegex(pattern, i + 1, match, j + 1)) {
+                            return true;
+                        }
+
+                        j++;
+
+                        if (match.length == j) {
+                            return false;
+                        }
+                    }
+                }
+
+                while (true) {
+                    if (pseudoRegex(pattern, i, match, j)) {
+                        return true;
+                    }
+
+                    j++;
+
+                    if (match.length == j) {
+                        return false;
+                    }
+                }
+            }
+
+            if (match.length == j) {
+                return false;
+            }
+
+            if ((pattern[i] != '?') && (pattern[i] != match[j])) {
+                return false;
+            }
+
+            i++;
+            j++;
+        }
+    }
+
+    private String[] recommendHostkeyAlgorithms(String hostname) {
+        String preferredAlgo = null;
+        List<Object> keys = getAllKeys(hostname);
+
+        for (Object key : keys) {
+            String thisAlgo;
+
+            if (key instanceof RSAPublicKey) {
+                thisAlgo = "ssh-rsa";
+            }
+            else if (key instanceof DSAPublicKey) {
+                thisAlgo = "ssh-dss";
+            }
+            else if (key instanceof ECPublicKey) {
+                ECPublicKey ecPub = (ECPublicKey) key;
+                String keyType = ECDSASHA2Verify.getCurveName(ecPub.getParams().getCurve().getField().getFieldSize());
+                thisAlgo = ECDSASHA2Verify.ECDSA_SHA2_PREFIX + keyType;
+            }
+            else {
+                continue;
+            }
+
+            if (preferredAlgo != null) {
+                /* If we find different key types, then return null */
+                if (preferredAlgo.compareTo(thisAlgo) != 0) {
+                    return null;
+                }
+            }
+            else {
+                preferredAlgo = thisAlgo;
+            }
+        }
+
+        /* If we did not find anything that we know of, return null */
+
+        if (preferredAlgo == null) {
+            return null;
+        }
+
+        /* Now put the preferred algo to the start of the array.
+                   * You may ask yourself why we do it that way - basically, we could just
+                   * return only the preferred algorithm: since we have a saved key of that
+                   * type (sent earlier from the remote host), then that should work out.
+                   * However, imagine that the server is (for whatever reasons) not offering
+                   * that type of hostkey anymore (e.g., "ssh-rsa" was disabled and
+                   * now "ssh-dss" is being used). If we then do not let the server send us
+                   * a fresh key of the new type, then we shoot ourself into the foot:
+                   * the connection cannot be established and hence the user cannot decide
+                   * if he/she wants to accept the new key.
+                   */
+
+        if (preferredAlgo.equals("ssh-rsa")) {
+            return new String[] {"ssh-rsa", "ssh-dss", "ecdsa-sha2-nistp256"};
+        }
+
+        return new String[] {"ssh-dss", "ssh-rsa", "ecdsa-sha2-nistp256"};
+    }
+
+    /**
+     * Checks the internal hostkey database for the given hostkey.
+     * If no matching key can be found, then the hostname is resolved to an IP address
+     * and the search is repeated using that IP address.
+     *
+     * @param hostname               the server's hostname, will be matched with all hostname patterns
+     * @param serverHostKeyAlgorithm type of hostkey, either <code>ssh-rsa</code> or <code>ssh-dss</code>
+     * @param serverHostKey          the key blob
+     * @return <ul>
+     * <li><code>HOSTKEY_IS_OK</code>: the given hostkey matches an entry for the given hostname</li>
+     * <li><code>HOSTKEY_IS_NEW</code>: no entries found for this hostname and this type of hostkey</li>
+     * <li><code>HOSTKEY_HAS_CHANGED</code>: hostname is known, but with another key of the same type
+     * (man-in-the-middle attack?)</li>
+     * </ul>
+     * @throws IOException if the supplied key blob cannot be parsed or does not match the given hostkey type.
+     */
+    public int verifyHostkey(String hostname, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException {
+        PublicKey remoteKey;
+
+        if ("ssh-rsa".equals(serverHostKeyAlgorithm)) {
+            remoteKey = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
+        }
+        else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
+            remoteKey = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
+        }
+        else if (serverHostKeyAlgorithm.startsWith("ecdsa-sha2-")) {
+            remoteKey = ECDSASHA2Verify.decodeSSHECDSAPublicKey(serverHostKey);
+        }
+        else {
+            throw new IllegalArgumentException("Unknown hostkey type " + serverHostKeyAlgorithm);
+        }
+
+        int result = checkKey(hostname, remoteKey);
+
+        if (result == HOSTKEY_IS_OK) {
+            return result;
+        }
+
+        InetAddress[] ipAdresses;
+
+        try {
+            ipAdresses = InetAddress.getAllByName(hostname);
+        }
+        catch (UnknownHostException e) {
+            return result;
+        }
+
+        for (int i = 0; i < ipAdresses.length; i++) {
+            int newresult = checkKey(ipAdresses[i].getHostAddress(), remoteKey);
+
+            if (newresult == HOSTKEY_IS_OK) {
+                return newresult;
+            }
+
+            if (newresult == HOSTKEY_HAS_CHANGED) {
+                result = HOSTKEY_HAS_CHANGED;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Adds a single public key entry to the a known_hosts file.
+     * This method is designed to be used in a {@link ServerHostKeyVerifier}.
+     *
+     * @param knownHosts             the file where the publickey entry will be appended.
+     * @param hostnames              a list of hostname patterns - at least one most be specified. Check out the
+     *                               OpenSSH sshd man page for a description of the pattern matching algorithm.
+     * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
+     * @param serverHostKey          as passed to the {@link ServerHostKeyVerifier}.
+     * @throws IOException
+     */
+    public static void addHostkeyToFile(File knownHosts, String[] hostnames, String serverHostKeyAlgorithm,
+                                        byte[] serverHostKey) throws IOException {
+        if ((hostnames == null) || (hostnames.length == 0)) {
+            throw new IllegalArgumentException("Need at least one hostname specification");
+        }
+
+        if ((serverHostKeyAlgorithm == null) || (serverHostKey == null)) {
+            throw new IllegalArgumentException();
+        }
+
+        CharArrayWriter writer = new CharArrayWriter();
+
+        for (int i = 0; i < hostnames.length; i++) {
+            if (i != 0) {
+                writer.write(',');
+            }
+
+            writer.write(hostnames[i]);
+        }
+
+        writer.write(' ');
+        writer.write(serverHostKeyAlgorithm);
+        writer.write(' ');
+        writer.write(Base64.encode(serverHostKey));
+        writer.write("\n");
+        char[] entry = writer.toCharArray();
+        RandomAccessFile raf = new RandomAccessFile(knownHosts, "rw");
+        long len = raf.length();
+
+        if (len > 0) {
+            raf.seek(len - 1);
+            int last = raf.read();
+
+            if (last != '\n') {
+                raf.write('\n');
+            }
+        }
+
+        raf.write(StringEncoder.GetBytes(new String(entry)));
+        raf.close();
+    }
+
+    /**
+     * Generates a "raw" fingerprint of a hostkey.
+     *
+     * @param type    either "md5" or "sha1"
+     * @param keyType either "ssh-rsa" or "ssh-dss" or "ecdsa-sha2..."
+     * @param hostkey the hostkey
+     * @return the raw fingerprint
+     */
+    static private byte[] rawFingerPrint(String type, String keyType, byte[] hostkey) throws IOException {
+        Digest dig;
+
+        if ("md5".equals(type)) {
+            dig = new MD5();
+        }
+        else if ("sha1".equals(type)) {
+            dig = new SHA1();
+        }
+        else {
+            throw new IllegalArgumentException("Unknown hash type " + type);
+        }
+
+        if ("ssh-rsa".equals(keyType)) {
+        }
+        else if ("ssh-dss".equals(keyType)) {
+        }
+        else if (keyType.startsWith("ecdsa-sha2-")) {
+        }
+        else {
+            throw new IllegalArgumentException("Unknown key type " + keyType);
+        }
+
+        if (hostkey == null) {
+            throw new IllegalArgumentException("hostkey is null");
+        }
+
+        dig.update(hostkey);
+        byte[] res = new byte[dig.getDigestLength()];
+
+        try {
+            dig.digest(res);
+        }
+        catch (DigestException e) {
+            throw new IOException(e);
+        }
+
+        return res;
+    }
+
+    /**
+     * Convert a raw fingerprint to hex representation (XX:YY:ZZ...).
+     *
+     * @param fingerprint raw fingerprint
+     * @return the hex representation
+     */
+    static private String rawToHexFingerprint(byte[] fingerprint) {
+        final char[] alpha = "0123456789abcdef".toCharArray();
+        StringBuilder sb = new StringBuilder();
+
+        for (int i = 0; i < fingerprint.length; i++) {
+            if (i != 0) {
+                sb.append(':');
+            }
+
+            int b = fingerprint[i] & 0xff;
+            sb.append(alpha[b >> 4]);
+            sb.append(alpha[b & 15]);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Convert a raw fingerprint to bubblebabble representation.
+     *
+     * @param raw raw fingerprint
+     * @return the bubblebabble representation
+     */
+    static private String rawToBubblebabbleFingerprint(byte[] raw) {
+        final char[] v = "aeiouy".toCharArray();
+        final char[] c = "bcdfghklmnprstvzx".toCharArray();
+        StringBuilder sb = new StringBuilder();
+        int seed = 1;
+        int rounds = (raw.length / 2) + 1;
+        sb.append('x');
+
+        for (int i = 0; i < rounds; i++) {
+            if (((i + 1) < rounds) || ((raw.length) % 2 != 0)) {
+                sb.append(v[(((raw[2 * i] >> 6) & 3) + seed) % 6]);
+                sb.append(c[(raw[2 * i] >> 2) & 15]);
+                sb.append(v[((raw[2 * i] & 3) + (seed / 6)) % 6]);
+
+                if ((i + 1) < rounds) {
+                    sb.append(c[(((raw[(2 * i) + 1])) >> 4) & 15]);
+                    sb.append('-');
+                    sb.append(c[(((raw[(2 * i) + 1]))) & 15]);
+                    // As long as seed >= 0, seed will be >= 0 afterwards
+                    seed = ((seed * 5) + (((raw[2 * i] & 0xff) * 7) + (raw[(2 * i) + 1] & 0xff))) % 36;
+                }
+            }
+            else {
+                sb.append(v[seed % 6]); // seed >= 0, therefore index positive
+                sb.append('x');
+                sb.append(v[seed / 6]);
+            }
+        }
+
+        sb.append('x');
+        return sb.toString();
+    }
+
+    /**
+     * Convert a ssh2 key-blob into a human readable hex fingerprint.
+     * Generated fingerprints are identical to those generated by OpenSSH.
+     * <p/>
+     * Example fingerprint: d0:cb:76:19:99:5a:03:fc:73:10:70:93:f2:44:63:47.
+     *
+     * @param keytype   either "ssh-rsa" or "ssh-dss" or "ecdsa-sha2..."
+     * @param publickey key blob
+     * @return Hex fingerprint
+     */
+    public static String createHexFingerprint(String keytype, byte[] publickey) throws IOException {
+        byte[] raw = rawFingerPrint("md5", keytype, publickey);
+        return rawToHexFingerprint(raw);
+    }
+
+    /**
+     * Convert a ssh2 key-blob into a human readable bubblebabble fingerprint.
+     * The used bubblebabble algorithm (taken from OpenSSH) generates fingerprints
+     * that are easier to remember for humans.
+     * <p/>
+     * Example fingerprint: xofoc-bubuz-cazin-zufyl-pivuk-biduk-tacib-pybur-gonar-hotat-lyxux.
+     *
+     * @param keytype   either "ssh-rsa" or "ssh-dss"
+     * @param publickey key data
+     * @return Bubblebabble fingerprint
+     */
+    public static String createBubblebabbleFingerprint(String keytype, byte[] publickey) throws IOException {
+        byte[] raw = rawFingerPrint("sha1", keytype, publickey);
+        return rawToBubblebabbleFingerprint(raw);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/LocalPortForwarder.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+
+import ch.ethz.ssh2.channel.ChannelManager;
+import ch.ethz.ssh2.channel.LocalAcceptThread;
+
+/**
+ * A <code>LocalPortForwarder</code> forwards TCP/IP connections to a local
+ * port via the secure tunnel to another host (which may or may not be identical
+ * to the remote SSH-2 server). Checkout {@link Connection#createLocalPortForwarder(int, String, int)}
+ * on how to create one.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class LocalPortForwarder {
+    final ChannelManager cm;
+
+    final String host_to_connect;
+
+    final int port_to_connect;
+
+    final LocalAcceptThread lat;
+
+    LocalPortForwarder(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
+    throws IOException {
+        this.cm = cm;
+        this.host_to_connect = host_to_connect;
+        this.port_to_connect = port_to_connect;
+        lat = new LocalAcceptThread(cm, local_port, host_to_connect, port_to_connect);
+        lat.setDaemon(true);
+        lat.start();
+    }
+
+    LocalPortForwarder(ChannelManager cm, InetSocketAddress addr, String host_to_connect, int port_to_connect)
+    throws IOException {
+        this.cm = cm;
+        this.host_to_connect = host_to_connect;
+        this.port_to_connect = port_to_connect;
+        lat = new LocalAcceptThread(cm, addr, host_to_connect, port_to_connect);
+        lat.setDaemon(true);
+        lat.start();
+    }
+
+    /**
+     * Return the local socket address of the {@link ServerSocket} used to accept connections.
+     * @return
+     */
+    public InetSocketAddress getLocalSocketAddress() {
+        return (InetSocketAddress) lat.getServerSocket().getLocalSocketAddress();
+    }
+
+    /**
+     * Stop TCP/IP forwarding of newly arriving connections.
+     *
+     * @throws IOException
+     */
+    public void close() throws IOException {
+        lat.stopWorking();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/LocalStreamForwarder.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+
+import ch.ethz.ssh2.channel.Channel;
+import ch.ethz.ssh2.channel.ChannelManager;
+
+/**
+ * A <code>LocalStreamForwarder</code> forwards an Input- and Outputstream
+ * pair via the secure tunnel to another host (which may or may not be identical
+ * to the remote SSH-2 server).
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class LocalStreamForwarder {
+    private ChannelManager cm;
+
+    private Channel cn;
+
+    LocalStreamForwarder(ChannelManager cm, String host_to_connect, int port_to_connect) throws IOException {
+        this.cm = cm;
+        cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect,
+                                       InetAddress.getLocalHost().getHostAddress(), 0);
+    }
+
+    /**
+     * @return An <code>InputStream</code> object.
+     * @throws IOException
+     */
+    public InputStream getInputStream() throws IOException {
+        return cn.getStdoutStream();
+    }
+
+    /**
+     * Get the OutputStream. Please be aware that the implementation MAY use an
+     * internal buffer. To make sure that the buffered data is sent over the
+     * tunnel, you have to call the <code>flush</code> method of the
+     * <code>OutputStream</code>. To signal EOF, please use the
+     * <code>close</code> method of the <code>OutputStream</code>.
+     *
+     * @return An <code>OutputStream</code> object.
+     * @throws IOException
+     */
+    public OutputStream getOutputStream() throws IOException {
+        return cn.getStdinStream();
+    }
+
+    /**
+     * Close the underlying SSH forwarding channel and free up resources.
+     * You can also use this method to force the shutdown of the underlying
+     * forwarding channel. Pending output (OutputStream not flushed) will NOT
+     * be sent. Pending input (InputStream) can still be read. If the shutdown
+     * operation is already in progress (initiated from either side), then this
+     * call is a no-op.
+     *
+     * @throws IOException
+     */
+    public void close() throws IOException {
+        cm.closeChannel(cn, "Closed due to user request.", true);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/PacketFormatException.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,13 @@
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+/**
+ * @version $Id: PacketFormatException.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class PacketFormatException extends IOException {
+
+    public PacketFormatException(String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/PacketListener.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011 David Kocher. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * @version $Id: PacketListener.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public interface PacketListener {
+    void read(String packet);
+
+    void write(String packet);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/PacketTypeException.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,24 @@
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+/**
+ * @version $Id: PacketTypeException.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class PacketTypeException extends IOException {
+
+    public PacketTypeException() {
+    }
+
+    public PacketTypeException(final String message) {
+        super(message);
+    }
+
+    public PacketTypeException(final int packet) {
+        super(String.format("The SFTP server sent an unexpected packet type (%d)", packet));
+    }
+
+    public PacketTypeException(final int packet, final String message) {
+        super(String.format("The SFTP server sent an invalid packet type (%d). %s", packet, message));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ProxyData.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * An abstract marker interface implemented by all proxy data implementations.
+ *
+ * @see HTTPProxyData
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public abstract interface ProxyData {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/PtySettings.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * PTY settings for a SSH session. Zero dimension parameters are ignored. The character/row dimensions
+ * override the pixel dimensions (when nonzero). Pixel dimensions refer to
+ * the drawable area of the window. The dimension parameters are only
+ * informational. The encoding of terminal modes (parameter
+ * <code>terminal_modes</code>) is described in RFC4254.
+ *
+ * @author Christian
+ */
+public class PtySettings {
+    /**
+     * TERM environment variable value (e.g., vt100)
+     */
+    public String term;
+
+    /**
+     * Terminal width, characters (e.g., 80)
+     */
+    public int term_width_characters;
+
+    /**
+     * Terminal height, rows (e.g., 24)
+     */
+    public int term_height_characters;
+
+    /**
+     * Terminal width, pixels (e.g., 640)
+     */
+    public int term_width_pixels;
+
+    /**
+     * Terminal height, pixels (e.g., 480)
+     */
+    public int term_height_pixels;
+
+    /**
+     * Encoded terminal modes
+     */
+    public byte[] terminal_modes;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/RequestMismatchException.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,17 @@
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+/**
+ * @version $Id: RequestMismatchException.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class RequestMismatchException extends IOException {
+
+    public RequestMismatchException() {
+        super("The server sent an invalid id field.");
+    }
+
+    public RequestMismatchException(final String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SCPClient.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,742 @@
+package ch.ethz.ssh2;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+
+/**
+ * A very basic <code>SCPClient</code> that can be used to copy files from/to
+ * the SSH-2 server. On the server side, the "scp" program must be in the PATH.
+ * <p/>
+ * This scp client is thread safe - you can download (and upload) different sets
+ * of files concurrently without any troubles. The <code>SCPClient</code> is
+ * actually mapping every request to a distinct {@link ch.ethz.ssh2.Session}.
+ *
+ * @author Christian Plattner, plattner@inf.ethz.ch
+ * @version $Id: SCPClient.java 85 2014-04-07 14:05:09Z dkocher@sudo.ch $
+ */
+
+public class SCPClient {
+    Connection conn;
+
+    String charsetName = null;
+
+    /**
+     * Set the charset used to convert between Java Unicode Strings and byte encodings
+     * used by the server for paths and file names.
+     *
+     * @param charset the name of the charset to be used or <code>null</code> to use the platform's
+     * default encoding.
+     * @throws IOException
+     * @see #getCharset()
+     */
+    public void setCharset(String charset) throws IOException {
+        if (charset == null) {
+            charsetName = charset;
+            return;
+        }
+
+        try {
+            Charset.forName(charset);
+        }
+        catch (UnsupportedCharsetException e) {
+            throw new IOException("This charset is not supported", e);
+        }
+
+        charsetName = charset;
+    }
+
+    /**
+     * The currently used charset for filename encoding/decoding.
+     *
+     * @return The name of the charset (<code>null</code> if the platform's default charset is being used)
+     * @see #setCharset(String)
+     */
+    public String getCharset() {
+        return charsetName;
+    }
+
+    public class LenNamePair {
+        public long length;
+        String filename;
+    }
+
+    public SCPClient(Connection conn) {
+        if (conn == null)
+            throw new IllegalArgumentException("Cannot accept null argument!");
+
+        this.conn = conn;
+    }
+
+    protected void readResponse(InputStream is) throws IOException {
+        int c = is.read();
+
+        if (c == 0)
+            return;
+
+        if (c == -1)
+            throw new IOException("Remote scp terminated unexpectedly.");
+
+        if ((c != 1) && (c != 2))
+            throw new IOException("Remote scp sent illegal error code.");
+
+        if (c == 2)
+            throw new IOException("Remote scp terminated with error.");
+
+        String err = receiveLine(is);
+        throw new IOException("Remote scp terminated with error (" + err + ").");
+    }
+
+    protected String receiveLine(InputStream is) throws IOException {
+        StringBuilder sb = new StringBuilder(30);
+
+        while (true) {
+            /* This is a random limit - if your path names are longer, then adjust it */
+            if (sb.length() > 8192)
+                throw new IOException("Remote scp sent a too long line");
+
+            int c = is.read();
+
+            if (c < 0)
+                throw new IOException("Remote scp terminated unexpectedly.");
+
+            if (c == '\n')
+                break;
+
+            sb.append((char) c);
+        }
+
+        return sb.toString();
+    }
+
+    protected LenNamePair parseCLine(String line) throws IOException {
+        /* Minimum line: "xxxx y z" ---> 8 chars */
+        if (line.length() < 8)
+            throw new IOException("Malformed C line sent by remote SCP binary, line too short.");
+
+        if ((line.charAt(4) != ' ') || (line.charAt(5) == ' '))
+            throw new IOException("Malformed C line sent by remote SCP binary.");
+
+        int length_name_sep = line.indexOf(' ', 5);
+
+        if (length_name_sep == -1)
+            throw new IOException("Malformed C line sent by remote SCP binary.");
+
+        String length_substring = line.substring(5, length_name_sep);
+        String name_substring = line.substring(length_name_sep + 1);
+
+        if ((length_substring.length() <= 0) || (name_substring.length() <= 0))
+            throw new IOException("Malformed C line sent by remote SCP binary.");
+
+        if ((6 + length_substring.length() + name_substring.length()) != line.length())
+            throw new IOException("Malformed C line sent by remote SCP binary.");
+
+        final long len;
+
+        try {
+            len = Long.parseLong(length_substring);
+        }
+        catch (NumberFormatException e) {
+            throw new IOException("Malformed C line sent by remote SCP binary, cannot parse file length.");
+        }
+
+        if (len < 0)
+            throw new IOException("Malformed C line sent by remote SCP binary, illegal file length.");
+
+        LenNamePair lnp = new LenNamePair();
+        lnp.length = len;
+        lnp.filename = name_substring;
+        return lnp;
+    }
+
+    /**
+     * The session for opened for this SCP transfer must be closed using
+     * SCPOutputStream#close
+     *
+     * @param remoteFile
+     * @param length The size of the file to send
+     * @param remoteTargetDirectory
+     * @param mode
+     * @return
+     * @throws IOException
+     */
+    public SCPOutputStream put(final String remoteFile, long length, String remoteTargetDirectory, String mode)
+    throws IOException {
+        Session sess = null;
+
+        if (null == remoteFile)
+            throw new IllegalArgumentException("Null argument.");
+
+        if (null == remoteTargetDirectory)
+            remoteTargetDirectory = "";
+
+        if (null == mode)
+            mode = "0600";
+
+        if (mode.length() != 4)
+            throw new IllegalArgumentException("Invalid mode.");
+
+        for (int i = 0; i < mode.length(); i++)
+            if (Character.isDigit(mode.charAt(i)) == false)
+                throw new IllegalArgumentException("Invalid mode.");
+
+        remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
+        String cmd = "scp -t -d \"" + remoteTargetDirectory + "\"";
+        sess = conn.openSession();
+        sess.execCommand(cmd, charsetName);
+        return new SCPOutputStream(this, sess, remoteFile, length, mode);
+    }
+
+    /**
+     * The session for opened for this SCP transfer must be closed using
+     * SCPInputStream#close
+     *
+     * @param remoteFile
+     * @return
+     * @throws IOException
+     */
+    public SCPInputStream get(final String remoteFile) throws IOException {
+        Session sess = null;
+
+        if (null == remoteFile)
+            throw new IllegalArgumentException("Null argument.");
+
+        if (remoteFile.length() == 0)
+            throw new IllegalArgumentException("Cannot accept empty filename.");
+
+        String cmd = "scp -f";
+        cmd += (" \"" + remoteFile + "\"");
+        sess = conn.openSession();
+        sess.execCommand(cmd, charsetName);
+        return new SCPInputStream(this, sess);
+    }
+    private void sendBytes(Session sess, byte[] data, String fileName, String mode) throws IOException {
+        OutputStream os = sess.getStdin();
+        InputStream is = new BufferedInputStream(sess.getStdout(), 512);
+        readResponse(is);
+        String cline = "C" + mode + " " + data.length + " " + fileName + "\n";
+        os.write(cline.getBytes("ISO-8859-1"));
+        os.flush();
+        readResponse(is);
+        os.write(data, 0, data.length);
+        os.write(0);
+        os.flush();
+        readResponse(is);
+        os.write("E\n".getBytes("ISO-8859-1"));
+        os.flush();
+    }
+
+    private void sendFiles(Session sess, String[] files, String[] remoteFiles, String mode) throws IOException {
+        byte[] buffer = new byte[8192];
+        OutputStream os = new BufferedOutputStream(sess.getStdin(), 40000);
+        InputStream is = new BufferedInputStream(sess.getStdout(), 512);
+        readResponse(is);
+
+        for (int i = 0; i < files.length; i++) {
+            File f = new File(files[i]);
+            long remain = f.length();
+            String remoteName;
+
+            if ((remoteFiles != null) && (remoteFiles.length > i) && (remoteFiles[i] != null))
+                remoteName = remoteFiles[i];
+            else
+                remoteName = f.getName();
+
+            String cline = "C" + mode + " " + remain + " " + remoteName + "\n";
+            os.write(cline.getBytes("ISO-8859-1"));
+            os.flush();
+            readResponse(is);
+            FileInputStream fis = null;
+
+            try {
+                fis = new FileInputStream(f);
+
+                while (remain > 0) {
+                    int trans;
+
+                    if (remain > buffer.length)
+                        trans = buffer.length;
+                    else
+                        trans = (int) remain;
+
+                    if (fis.read(buffer, 0, trans) != trans)
+                        throw new IOException("Cannot read enough from local file " + files[i]);
+
+                    os.write(buffer, 0, trans);
+                    remain -= trans;
+                }
+            }
+            finally {
+                if (fis != null)
+                    fis.close();
+            }
+
+            os.write(0);
+            os.flush();
+            readResponse(is);
+        }
+
+        os.write("E\n".getBytes("ISO-8859-1"));
+        os.flush();
+    }
+
+    private void receiveFiles(Session sess, OutputStream[] targets) throws IOException {
+        byte[] buffer = new byte[8192];
+        OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
+        InputStream is = new BufferedInputStream(sess.getStdout(), 40000);
+        os.write(0x0);
+        os.flush();
+
+        for (int i = 0; i < targets.length; i++) {
+            LenNamePair lnp = null;
+
+            while (true) {
+                int c = is.read();
+
+                if (c < 0)
+                    throw new IOException("Remote scp terminated unexpectedly.");
+
+                String line = receiveLine(is);
+
+                if (c == 'T') {
+                    /* Ignore modification times */
+                    continue;
+                }
+
+                if ((c == 1) || (c == 2))
+                    throw new IOException("Remote SCP error: " + line);
+
+                if (c == 'C') {
+                    lnp = parseCLine(line);
+                    break;
+                }
+
+                throw new IOException("Remote SCP error: " + ((char) c) + line);
+            }
+
+            os.write(0x0);
+            os.flush();
+            long remain = lnp.length;
+
+            while (remain > 0) {
+                int trans;
+
+                if (remain > buffer.length)
+                    trans = buffer.length;
+                else
+                    trans = (int) remain;
+
+                int this_time_received = is.read(buffer, 0, trans);
+
+                if (this_time_received < 0) {
+                    throw new IOException("Remote scp terminated connection unexpectedly");
+                }
+
+                targets[i].write(buffer, 0, this_time_received);
+                remain -= this_time_received;
+            }
+
+            readResponse(is);
+            os.write(0x0);
+            os.flush();
+        }
+    }
+
+    private void receiveFiles(Session sess, String[] files, String target) throws IOException {
+        byte[] buffer = new byte[8192];
+        OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
+        InputStream is = new BufferedInputStream(sess.getStdout(), 40000);
+        os.write(0x0);
+        os.flush();
+
+        for (int i = 0; i < files.length; i++) {
+            LenNamePair lnp = null;
+
+            while (true) {
+                int c = is.read();
+
+                if (c < 0)
+                    throw new IOException("Remote scp terminated unexpectedly.");
+
+                String line = receiveLine(is);
+
+                if (c == 'T') {
+                    /* Ignore modification times */
+                    continue;
+                }
+
+                if ((c == 1) || (c == 2))
+                    throw new IOException("Remote SCP error: " + line);
+
+                if (c == 'C') {
+                    lnp = parseCLine(line);
+                    break;
+                }
+
+                throw new IOException("Remote SCP error: " + ((char) c) + line);
+            }
+
+            os.write(0x0);
+            os.flush();
+            File f = new File(target + File.separatorChar + lnp.filename);
+            FileOutputStream fop = null;
+
+            try {
+                fop = new FileOutputStream(f);
+                long remain = lnp.length;
+
+                while (remain > 0) {
+                    int trans;
+
+                    if (remain > buffer.length)
+                        trans = buffer.length;
+                    else
+                        trans = (int) remain;
+
+                    int this_time_received = is.read(buffer, 0, trans);
+
+                    if (this_time_received < 0) {
+                        throw new IOException("Remote scp terminated connection unexpectedly");
+                    }
+
+                    fop.write(buffer, 0, this_time_received);
+                    remain -= this_time_received;
+                }
+            }
+            finally {
+                if (fop != null)
+                    fop.close();
+            }
+
+            readResponse(is);
+            os.write(0x0);
+            os.flush();
+        }
+    }
+
+    /**
+     * Copy a local file to a remote directory, uses mode 0600 when creating the
+     * file on the remote side.
+     *
+     * @param localFile
+     *            Path and name of local file.
+     * @param remoteTargetDirectory
+     *            Remote target directory. Use an empty string to specify the
+     *            default directory.
+     *
+     * @throws IOException
+     */
+    public void put(String localFile, String remoteTargetDirectory) throws IOException {
+        put(new String[] { localFile }, remoteTargetDirectory, "0600");
+    }
+
+    /**
+     * Copy a set of local files to a remote directory, uses mode 0600 when
+     * creating files on the remote side.
+     *
+     * @param localFiles
+     *            Paths and names of local file names.
+     * @param remoteTargetDirectory
+     *            Remote target directory. Use an empty string to specify the
+     *            default directory.
+     *
+     * @throws IOException
+     */
+
+    public void put(String[] localFiles, String remoteTargetDirectory) throws IOException {
+        put(localFiles, remoteTargetDirectory, "0600");
+    }
+
+    /**
+     * Copy a local file to a remote directory, uses the specified mode when
+     * creating the file on the remote side.
+     *
+     * @param localFile
+     *            Path and name of local file.
+     * @param remoteTargetDirectory
+     *            Remote target directory. Use an empty string to specify the
+     *            default directory.
+     * @param mode
+     *            a four digit string (e.g., 0644, see "man chmod", "man open")
+     * @throws IOException
+     */
+    public void put(String localFile, String remoteTargetDirectory, String mode) throws IOException {
+        put(new String[] { localFile }, remoteTargetDirectory, mode);
+    }
+
+    /**
+     * Copy a local file to a remote directory, uses the specified mode and
+     * remote filename when creating the file on the remote side.
+     *
+     * @param localFile
+     *            Path and name of local file.
+     * @param remoteFileName
+     *            The name of the file which will be created in the remote
+     *            target directory.
+     * @param remoteTargetDirectory
+     *            Remote target directory. Use an empty string to specify the
+     *            default directory.
+     * @param mode
+     *            a four digit string (e.g., 0644, see "man chmod", "man open")
+     * @throws IOException
+     */
+    public void put(String localFile, String remoteFileName, String remoteTargetDirectory, String mode)
+    throws IOException {
+        put(new String[] { localFile }, new String[] { remoteFileName }, remoteTargetDirectory, mode);
+    }
+
+    /**
+     * Create a remote file and copy the contents of the passed byte array into
+     * it. Uses mode 0600 for creating the remote file.
+     *
+     * @param data
+     *            the data to be copied into the remote file.
+     * @param remoteFileName
+     *            The name of the file which will be created in the remote
+     *            target directory.
+     * @param remoteTargetDirectory
+     *            Remote target directory. Use an empty string to specify the
+     *            default directory.
+     * @throws IOException
+     */
+
+    public void put(byte[] data, String remoteFileName, String remoteTargetDirectory) throws IOException {
+        put(data, remoteFileName, remoteTargetDirectory, "0600");
+    }
+
+    /**
+     * Create a remote file and copy the contents of the passed byte array into
+     * it. The method use the specified mode when creating the file on the
+     * remote side.
+     *
+     * @param data
+     *            the data to be copied into the remote file.
+     * @param remoteFileName
+     *            The name of the file which will be created in the remote
+     *            target directory.
+     * @param remoteTargetDirectory
+     *            Remote target directory. Use an empty string to specify the
+     *            default directory.
+     * @param mode
+     *            a four digit string (e.g., 0644, see "man chmod", "man open")
+     * @throws IOException
+     */
+    public void put(byte[] data, String remoteFileName, String remoteTargetDirectory, String mode) throws IOException {
+        Session sess = null;
+
+        if ((remoteFileName == null) || (remoteTargetDirectory == null) || (mode == null))
+            throw new IllegalArgumentException("Null argument.");
+
+        if (mode.length() != 4)
+            throw new IllegalArgumentException("Invalid mode.");
+
+        for (int i = 0; i < mode.length(); i++)
+            if (Character.isDigit(mode.charAt(i)) == false)
+                throw new IllegalArgumentException("Invalid mode.");
+
+        remoteTargetDirectory = remoteTargetDirectory.trim();
+        remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
+        String cmd = "scp -t -d " + remoteTargetDirectory;
+
+        try {
+            sess = conn.openSession();
+            sess.execCommand(cmd);
+            sendBytes(sess, data, remoteFileName, mode);
+        }
+        catch (IOException e) {
+            throw(IOException) new IOException("Error during SCP transfer.").initCause(e);
+        }
+        finally {
+            if (sess != null)
+                sess.close();
+        }
+    }
+
+    /**
+     * Copy a set of local files to a remote directory, uses the specified mode
+     * when creating the files on the remote side.
+     *
+     * @param localFiles
+     *            Paths and names of the local files.
+     * @param remoteTargetDirectory
+     *            Remote target directory. Use an empty string to specify the
+     *            default directory.
+     * @param mode
+     *            a four digit string (e.g., 0644, see "man chmod", "man open")
+     * @throws IOException
+     */
+    public void put(String[] localFiles, String remoteTargetDirectory, String mode) throws IOException {
+        put(localFiles, null, remoteTargetDirectory, mode);
+    }
+
+    public void put(String[] localFiles, String[] remoteFiles, String remoteTargetDirectory, String mode)
+    throws IOException {
+        Session sess = null;
+
+        /*
+         * remoteFiles may be null, indicating that the local filenames shall be
+         * used
+         */
+
+        if ((localFiles == null) || (remoteTargetDirectory == null) || (mode == null))
+            throw new IllegalArgumentException("Null argument.");
+
+        if (mode.length() != 4)
+            throw new IllegalArgumentException("Invalid mode.");
+
+        for (int i = 0; i < mode.length(); i++)
+            if (Character.isDigit(mode.charAt(i)) == false)
+                throw new IllegalArgumentException("Invalid mode.");
+
+        if (localFiles.length == 0)
+            return;
+
+        remoteTargetDirectory = remoteTargetDirectory.trim();
+        remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
+        String cmd = "scp -t -d " + remoteTargetDirectory;
+
+        for (int i = 0; i < localFiles.length; i++) {
+            if (localFiles[i] == null)
+                throw new IllegalArgumentException("Cannot accept null filename.");
+        }
+
+        try {
+            sess = conn.openSession();
+            sess.execCommand(cmd);
+            sendFiles(sess, localFiles, remoteFiles, mode);
+        }
+        catch (IOException e) {
+            throw(IOException) new IOException("Error during SCP transfer.").initCause(e);
+        }
+        finally {
+            if (sess != null)
+                sess.close();
+        }
+    }
+
+    /**
+     * Download a file from the remote server to a local directory.
+     *
+     * @param remoteFile
+     *            Path and name of the remote file.
+     * @param localTargetDirectory
+     *            Local directory to put the downloaded file.
+     *
+     * @throws IOException
+     */
+    public void get(String remoteFile, String localTargetDirectory) throws IOException {
+        get(new String[] { remoteFile }, localTargetDirectory);
+    }
+
+    /**
+     * Download a file from the remote server and pipe its contents into an
+     * <code>OutputStream</code>. Please note that, to enable flexible usage
+     * of this method, the <code>OutputStream</code> will not be closed nor
+     * flushed.
+     *
+     * @param remoteFile
+     *            Path and name of the remote file.
+     * @param target
+     *            OutputStream where the contents of the file will be sent to.
+     * @throws IOException
+     */
+    public void get(String remoteFile, OutputStream target) throws IOException {
+        get(new String[] { remoteFile }, new OutputStream[] { target });
+    }
+
+    private void get(String remoteFiles[], OutputStream[] targets) throws IOException {
+        Session sess = null;
+
+        if ((remoteFiles == null) || (targets == null))
+            throw new IllegalArgumentException("Null argument.");
+
+        if (remoteFiles.length != targets.length)
+            throw new IllegalArgumentException("Length of arguments does not match.");
+
+        if (remoteFiles.length == 0)
+            return;
+
+        String cmd = "scp -f";
+
+        for (int i = 0; i < remoteFiles.length; i++) {
+            if (remoteFiles[i] == null)
+                throw new IllegalArgumentException("Cannot accept null filename.");
+
+            String tmp = remoteFiles[i].trim();
+
+            if (tmp.length() == 0)
+                throw new IllegalArgumentException("Cannot accept empty filename.");
+
+            cmd += (" " + tmp);
+        }
+
+        try {
+            sess = conn.openSession();
+            sess.execCommand(cmd);
+            receiveFiles(sess, targets);
+        }
+        catch (IOException e) {
+            throw(IOException) new IOException("Error during SCP transfer.").initCause(e);
+        }
+        finally {
+            if (sess != null)
+                sess.close();
+        }
+    }
+
+    /**
+     * Download a set of files from the remote server to a local directory.
+     *
+     * @param remoteFiles
+     *            Paths and names of the remote files.
+     * @param localTargetDirectory
+     *            Local directory to put the downloaded files.
+     *
+     * @throws IOException
+     */
+    public void get(String remoteFiles[], String localTargetDirectory) throws IOException {
+        Session sess = null;
+
+        if ((remoteFiles == null) || (localTargetDirectory == null))
+            throw new IllegalArgumentException("Null argument.");
+
+        if (remoteFiles.length == 0)
+            return;
+
+        String cmd = "scp -f";
+
+        for (int i = 0; i < remoteFiles.length; i++) {
+            if (remoteFiles[i] == null)
+                throw new IllegalArgumentException("Cannot accept null filename.");
+
+            String tmp = remoteFiles[i].trim();
+
+            if (tmp.length() == 0)
+                throw new IllegalArgumentException("Cannot accept empty filename.");
+
+            cmd += (" " + tmp);
+        }
+
+        try {
+            sess = conn.openSession();
+            sess.execCommand(cmd);
+            receiveFiles(sess, remoteFiles, localTargetDirectory);
+        }
+        catch (IOException e) {
+            throw(IOException) new IOException("Error during SCP transfer.").initCause(e);
+        }
+        finally {
+            if (sess != null)
+                sess.close();
+        }
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SCPInputStream.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2011 David Kocher. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * @version $Id: SCPInputStream.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class SCPInputStream extends BufferedInputStream {
+    private Session session;
+
+    /**
+     * Bytes remaining to be read from the stream
+     */
+    private long remaining;
+
+    public SCPInputStream(SCPClient client, Session session) throws IOException {
+        super(session.getStdout());
+        this.session = session;
+        OutputStream os = new BufferedOutputStream(session.getStdin(), 512);
+        os.write(0x0);
+        os.flush();
+        final SCPClient.LenNamePair lnp;
+
+        while (true) {
+            int c = session.getStdout().read();
+
+            if (c < 0) {
+                throw new IOException("Remote scp terminated unexpectedly.");
+            }
+
+            String line = client.receiveLine(session.getStdout());
+
+            if (c == 'T') {
+                /* Ignore modification times */
+                continue;
+            }
+
+            if ((c == 1) || (c == 2)) {
+                throw new IOException("Remote SCP error: " + line);
+            }
+
+            if (c == 'C') {
+                lnp = client.parseCLine(line);
+                break;
+            }
+
+            throw new IOException("Remote SCP error: " + ((char) c) + line);
+        }
+
+        os.write(0x0);
+        os.flush();
+        this.remaining = lnp.length;
+    }
+
+    @Override
+    public int read() throws IOException {
+        if (!(remaining > 0)) {
+            return -1;
+        }
+
+        int b = super.read();
+
+        if (b < 0) {
+            throw new IOException("Remote scp terminated connection unexpectedly");
+        }
+
+        remaining -= 1;
+        return b;
+    }
+
+    @Override
+    public int read(byte b[], int off, int len) throws IOException {
+        if (!(remaining > 0)) {
+            return -1;
+        }
+
+        int trans = (int) remaining;
+
+        if (remaining > len) {
+            trans = len;
+        }
+
+        int read = super.read(b, off, trans);
+
+        if (read < 0) {
+            throw new IOException("Remote scp terminated connection unexpectedly");
+        }
+
+        remaining -= read;
+        return read;
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            session.getStdin().write(0x0);
+            session.getStdin().flush();
+        }
+        finally {
+            if (session != null) {
+                session.close();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SCPOutputStream.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2011 David Kocher. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @version $Id: SCPOutputStream.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class SCPOutputStream extends BufferedOutputStream {
+
+    private Session session;
+
+    private SCPClient scp;
+
+    public SCPOutputStream(SCPClient client, Session session, final String remoteFile, long length, String mode) throws IOException {
+        super(session.getStdin(), 40000);
+        this.session = session;
+        this.scp = client;
+        InputStream is = new BufferedInputStream(session.getStdout(), 512);
+        scp.readResponse(is);
+        String cline = "C" + mode + " " + length + " " + remoteFile + "\n";
+        super.write(StringEncoder.GetBytes(cline));
+        this.flush();
+        scp.readResponse(is);
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            this.write(0);
+            this.flush();
+            scp.readResponse(session.getStdout());
+            this.write(StringEncoder.GetBytes("E\n"));
+            this.flush();
+        }
+        finally {
+            if (session != null)
+                session.close();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPClient.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,225 @@
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @version $Id: SFTPClient.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public interface SFTPClient {
+
+    /**
+     * Retrieve the file attributes of an open file.
+     *
+     * @param handle a SFTPv3FileHandle handle.
+     * @return a SFTPv3FileAttributes object.
+     * @throws IOException
+     */
+    SFTPFileAttributes fstat(SFTPFileHandle handle) throws IOException;
+
+    /**
+     * Retrieve the file attributes of a file. This method
+     * follows symbolic links on the server.
+     *
+     * @param path See the {@link SFTPClient comment} for the class for more details.
+     * @return a SFTPv3FileAttributes object.
+     * @throws IOException
+     * @see #lstat(String)
+     */
+    SFTPFileAttributes stat(String path) throws IOException;
+
+    /**
+     * Retrieve the file attributes of a file. This method
+     * does NOT follow symbolic links on the server.
+     *
+     * @param path See the {@link SFTPClient comment} for the class for more details.
+     * @return a SFTPv3FileAttributes object.
+     * @throws IOException
+     * @see #stat(String)
+     */
+    SFTPFileAttributes lstat(String path) throws IOException;
+
+    /**
+     * Read the target of a symbolic link. Note: OpenSSH (as of version 4.4) gets very upset
+     * (SSH_FX_BAD_MESSAGE error) if you want to read the target of a file that is not a
+     * symbolic link. Better check first with {@link #lstat(String)}.
+     *
+     * @param path See the {@link SFTPClient comment} for the class for more details.
+     * @return The target of the link.
+     * @throws IOException
+     */
+    String readLink(String path) throws IOException;
+
+    /**
+     * Modify the attributes of a file. Used for operations such as changing
+     * the ownership, permissions or access times, as well as for truncating a file.
+     *
+     * @param path See the {@link SFTPClient comment} for the class for more details.
+     * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be
+     *             made to the attributes of the file. Empty fields will be ignored.
+     * @throws IOException
+     */
+    void setstat(String path, SFTPFileAttributes attr) throws IOException;
+
+    /**
+     * Modify the attributes of a file. Used for operations such as changing
+     * the ownership, permissions or access times, as well as for truncating a file.
+     *
+     * @param handle a SFTPv3FileHandle handle
+     * @param attr   A SFTPv3FileAttributes object. Specifies the modifications to be
+     *               made to the attributes of the file. Empty fields will be ignored.
+     * @throws IOException
+     */
+    void fsetstat(SFTPFileHandle handle, SFTPFileAttributes attr) throws IOException;
+
+    /**
+     * Create a symbolic link on the server. Creates a link "src" that points
+     * to "target".
+     *
+     * @param src    See the {@link SFTPClient comment} for the class for more details.
+     * @param target See the {@link SFTPClient comment} for the class for more details.
+     * @throws IOException
+     */
+    void createSymlink(String src, String target) throws IOException;
+
+    /**
+     * Create a symbolic link on the server. Creates a link "src" that points
+     * to "target".
+     *
+     * @param src    See the {@link SFTPClient comment} for the class for more details.
+     * @param target See the {@link SFTPClient comment} for the class for more details.
+     * @throws IOException
+     */
+    void createHardlink(String src, String target) throws IOException;
+
+    /**
+     * Have the server canonicalize any given path name to an absolute path.
+     * This is useful for converting path names containing ".." components or
+     * relative pathnames without a leading slash into absolute paths.
+     *
+     * @param path See the {@link SFTPClient comment} for the class for more details.
+     * @return An absolute path.
+     * @throws IOException
+     */
+    String canonicalPath(String path) throws IOException;
+
+    void setCharset(String charset) throws IOException;
+
+    String getCharset();
+
+    SFTPFileHandle openDirectory(String path) throws IOException;
+
+    boolean isConnected();
+
+    void close();
+
+    List<? extends SFTPDirectoryEntry> ls(String dirName) throws IOException;
+
+    /**
+     * Create a new directory.
+     *
+     * @param name             See the {@link SFTPClient comment} for the class for more details.
+     * @param posixPermissions the permissions for this directory, e.g., "0700" (remember that
+     *                         this is octal noation). The server will likely apply a umask.
+     * @throws IOException
+     */
+    void mkdir(String name, int posixPermissions) throws IOException;
+
+    /**
+     * Remove a file.
+     *
+     * @param filename See the {@link SFTPClient comment} for the class for more details.
+     * @throws IOException
+     */
+    void rm(String filename) throws IOException;
+
+    /**
+     * Remove an empty directory.
+     *
+     * @param dirName See the {@link SFTPClient comment} for the class for more details.
+     * @throws IOException
+     */
+    void rmdir(String dirName) throws IOException;
+
+    /**
+     * Move a file or directory.
+     *
+     * @param oldPath See the {@link SFTPClient comment} for the class for more details.
+     * @param newPath See the {@link SFTPClient comment} for the class for more details.
+     * @throws IOException
+     */
+    void mv(String oldPath, String newPath) throws IOException;
+
+    /**
+     * Create a file and open it for reading and writing.
+     * Same as {@link #createFile(String, SFTPFileAttributes) createFile(filename, null)}.
+     *
+     * @param filename See the {@link SFTPClient comment} for the class for more details.
+     * @return a SFTPv3FileHandle handle
+     * @throws IOException
+     */
+    SFTPFileHandle createFile(String filename) throws IOException;
+
+    /**
+     * Create a file and open it for reading and writing.
+     * You can specify the default attributes of the file (the server may or may
+     * not respect your wishes).
+     *
+     * @param filename See the {@link SFTPv3Client comment} for the class for more details.
+     * @param attr     may be <code>null</code> to use server defaults. Probably only
+     *                 the <code>uid</code>, <code>gid</code> and <code>permissions</code>
+     *                 (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle}
+     *                 structure make sense. You need only to set those fields where you want
+     *                 to override the server's defaults.
+     * @return a SFTPv3FileHandle handle
+     * @throws IOException
+     */
+    SFTPFileHandle createFile(String filename, SFTPFileAttributes attr) throws IOException;
+
+    SFTPFileHandle openFile(String filename, int flags) throws IOException;
+
+    /**
+     * Read bytes from a file in a parallel fashion. As many bytes as you want will be read.
+     * <p/>
+     * <ul>
+     * <li>The server will read as many bytes as it can from the file (up to <code>len</code>),
+     * and return them.</li>
+     * <li>If EOF is encountered before reading any data, <code>-1</code> is returned.
+     * <li>If an error occurs, an exception is thrown</li>.
+     * <li>For normal disk files, it is guaranteed that the server will return the specified
+     * number of bytes, or up to end of file. For, e.g., device files this may return
+     * fewer bytes than requested.</li>
+     * </ul>
+     *
+     * @param handle     a SFTPv3FileHandle handle
+     * @param fileOffset offset (in bytes) in the file
+     * @param dst        the destination byte array
+     * @param dstoff     offset in the destination byte array
+     * @param len        how many bytes to read, 0 &lt; len
+     * @return the number of bytes that could be read, may be less than requested if
+     * the end of the file is reached, -1 is returned in case of <code>EOF</code>
+     * @throws IOException
+     */
+    int read(SFTPFileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException;
+
+    /**
+     * Write bytes to a file. If <code>len</code> &gt; 32768, then the write operation will
+     * be split into multiple writes.
+     *
+     * @param handle     a SFTPv3FileHandle handle.
+     * @param fileOffset offset (in bytes) in the file.
+     * @param src        the source byte array.
+     * @param srcoff     offset in the source byte array.
+     * @param len        how many bytes to write.
+     * @throws IOException
+     */
+    void write(SFTPFileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException;
+
+    /**
+     * Close a file.
+     *
+     * @param handle a file handle
+     * @throws IOException
+     */
+    void closeFile(SFTPFileHandle handle) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPDirectoryEntry.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,12 @@
+package ch.ethz.ssh2;
+
+/**
+ * @version $Id: SFTPDirectoryEntry.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public interface SFTPDirectoryEntry {
+
+    public String getFilename();
+
+    public SFTPFileAttributes getAttributes();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPException.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please see the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.sftp.ErrorCodes;
+
+/**
+ * Used in combination with the SFTPv3Client. This exception wraps
+ * error messages sent by the SFTP server.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class SFTPException extends IOException {
+    private static final long serialVersionUID = 578654644222421811L;
+
+    private final String sftpErrorMessage;
+    private final int sftpErrorCode;
+
+    private static String constructMessage(String s, int errorCode) {
+        String[] detail = ErrorCodes.getDescription(errorCode);
+
+        if (detail == null)
+            return s + " (UNKNOWN SFTP ERROR CODE)";
+
+        return s + " (" + detail[0] + ": " + detail[1] + ")";
+    }
+
+    SFTPException(String msg, int errorCode) {
+        super(constructMessage(msg, errorCode));
+        sftpErrorMessage = msg;
+        sftpErrorCode = errorCode;
+    }
+
+    /**
+     * Get the error message sent by the server. Often, this
+     * message does not help a lot (e.g., "failure").
+     *
+     * @return the plain string as sent by the server.
+     */
+    public String getServerErrorMessage() {
+        return sftpErrorMessage;
+    }
+
+    /**
+     * Get the error code sent by the server.
+     *
+     * @return an error code as defined in the SFTP specs.
+     */
+    public int getServerErrorCode() {
+        return sftpErrorCode;
+    }
+
+    /**
+     * Get the symbolic name of the error code as given in the SFTP specs.
+     *
+     * @return e.g., "SSH_FX_INVALID_FILENAME".
+     */
+    public String getServerErrorCodeSymbol() {
+        String[] detail = ErrorCodes.getDescription(sftpErrorCode);
+
+        if (detail == null)
+            return "UNKNOWN SFTP ERROR CODE " + sftpErrorCode;
+
+        return detail[0];
+    }
+
+    /**
+     * Get the description of the error code as given in the SFTP specs.
+     *
+     * @return e.g., "The filename is not valid."
+     */
+    public String getServerErrorCodeVerbose() {
+        String[] detail = ErrorCodes.getDescription(sftpErrorCode);
+
+        if (detail == null)
+            return "The error code " + sftpErrorCode + " is unknown.";
+
+        return detail[1];
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPFileAttributes.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,15 @@
+package ch.ethz.ssh2;
+
+/**
+ * @version $Id: SFTPFileAttributes.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public interface SFTPFileAttributes {
+
+    boolean isDirectory();
+
+    boolean isRegularFile();
+
+    boolean isSymlink();
+
+    byte[] toBytes();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPFileHandle.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,35 @@
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+/**
+ * @version $Id: SFTPFileHandle.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class SFTPFileHandle {
+
+    private final SFTPClient client;
+
+    private final byte[] handle;
+
+    protected SFTPFileHandle(final SFTPClient client, final byte[] handle) {
+        this.client = client;
+        this.handle = handle;
+    }
+
+    /**
+     * Get the SFTPv3Client instance which created this handle.
+     *
+     * @return A SFTPv3Client instance.
+     */
+    public SFTPClient getClient() {
+        return client;
+    }
+
+    public byte[] getHandle() {
+        return handle;
+    }
+
+    public void close() throws IOException {
+        client.closeFile(this);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPInputStream.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011 David Kocher. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @version $Id: SFTPInputStream.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class SFTPInputStream extends InputStream {
+
+    private SFTPFileHandle handle;
+
+    /**
+     * Offset (in bytes) in the file to read
+     */
+    private long readOffset = 0;
+
+    public SFTPInputStream(SFTPFileHandle handle) {
+        this.handle = handle;
+    }
+
+    /**
+     * Reads up to <code>len</code> bytes of data from the input stream into
+     * an array of bytes.  An attempt is made to read as many as
+     * <code>len</code> bytes, but a smaller number may be read, possibly
+     * zero. The number of bytes actually read is returned as an integer.
+     *
+     * @see SFTPClient#read(SFTPFileHandle, long, byte[], int, int)
+     */
+    @Override
+    public int read(byte[] buffer, int offset, int len) throws IOException {
+        int read = handle.getClient().read(handle, readOffset, buffer, offset, len);
+
+        if (read > 0) {
+            readOffset += read;
+        }
+
+        return read;
+    }
+
+    /**
+     * Reads the next byte of data from the input stream. The value byte is
+     * returned as an <code>int</code> in the range <code>0</code> to
+     * <code>255</code>. If no byte is available because the end of the stream
+     * has been reached, the value <code>-1</code> is returned. This method
+     * blocks until input data is available, the end of the stream is detected,
+     * or an exception is thrown.
+     * <p/>
+     * <p> A subclass must provide an implementation of this method.
+     *
+     * @return the next byte of data, or <code>-1</code> if the end of the
+     * stream is reached.
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public int read() throws IOException {
+        byte[] buffer = new byte[1];
+        int b = handle.getClient().read(handle, readOffset, buffer, 0, 1);
+
+        if (b > 0) {
+            readOffset += 1;
+        }
+
+        return b;
+    }
+
+    /**
+     * Skips over and discards <code>n</code> bytes of data from this input
+     * stream.
+     *
+     * @param n the number of bytes to be skipped.
+     * @return the actual number of bytes skipped.
+     */
+    @Override
+    public long skip(long n) {
+        readOffset += n;
+        return n;
+    }
+
+    @Override
+    public void close() throws IOException {
+        handle.getClient().closeFile(handle);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPOutputStream.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2011 David Kocher. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * @version $Id: SFTPOutputStream.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class SFTPOutputStream extends OutputStream {
+
+    private SFTPFileHandle handle;
+
+    /**
+     * Offset (in bytes) in the file to write
+     */
+    private long writeOffset = 0;
+
+    public SFTPOutputStream(SFTPFileHandle handle) {
+        this.handle = handle;
+    }
+
+    /**
+     * Writes <code>len</code> bytes from the specified byte array
+     * starting at offset <code>off</code> to this output stream.
+     * The general contract for <code>write(b, off, len)</code> is that
+     * some of the bytes in the array <code>b</code> are written to the
+     * output stream in order; element <code>b[off]</code> is the first
+     * byte written and <code>b[off+len-1]</code> is the last byte written
+     * by this operation.
+     *
+     * @see SFTPClient#write(SFTPFileHandle, long, byte[], int, int)
+     */
+    @Override
+    public void write(byte[] buffer, int offset, int len) throws IOException {
+        // We can just blindly write the whole buffer at once.
+        // if <code>len</code> &gt; 32768, then the write operation will
+        // be split into multiple writes in SFTPv3Client#write.
+        handle.getClient().write(handle, writeOffset, buffer, offset, len);
+        writeOffset += len;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        byte[] buffer = new byte[1];
+        buffer[0] = (byte) b;
+        handle.getClient().write(handle, writeOffset, buffer, 0, 1);
+        writeOffset += 1;
+    }
+
+    public long skip(long n) {
+        writeOffset += n;
+        return n;
+    }
+
+    @Override
+    public void close() throws IOException {
+        handle.getClient().closeFile(handle);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPv3Client.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+import ch.ethz.ssh2.sftp.ErrorCodes;
+import ch.ethz.ssh2.sftp.Packet;
+
+/**
+ * A <code>SFTPv3Client</code> represents a SFTP (protocol version 3)
+ * client connection tunnelled over a SSH-2 connection. This is a very simple
+ * (synchronous) implementation.
+ * <p/>
+ * Basically, most methods in this class map directly to one of
+ * the packet types described in draft-ietf-secsh-filexfer-02.txt.
+ * <p/>
+ * Note: this is experimental code.
+ * <p/>
+ * Error handling: the methods of this class throw IOExceptions. However, unless
+ * there is catastrophic failure, exceptions of the type {@link SFTPv3Client} will
+ * be thrown (a subclass of IOException). Therefore, you can implement more verbose
+ * behavior by checking if a thrown exception if of this type. If yes, then you
+ * can cast the exception and access detailed information about the failure.
+ * <p/>
+ * Notes about file names, directory names and paths, copy-pasted
+ * from the specs:
+ * <ul>
+ * <li>SFTP v3 represents file names as strings. File names are
+ * assumed to use the slash ('/') character as a directory separator.</li>
+ * <li>File names starting with a slash are "absolute", and are relative to
+ * the root of the file system.  Names starting with any other character
+ * are relative to the user's default directory (home directory).</li>
+ * <li>Servers SHOULD interpret a path name component ".." as referring to
+ * the parent directory, and "." as referring to the current directory.
+ * If the server implementation limits access to certain parts of the
+ * file system, it must be extra careful in parsing file names when
+ * enforcing such restrictions.  There have been numerous reported
+ * security bugs where a ".." in a path name has allowed access outside
+ * the intended area.</li>
+ * <li>An empty path name is valid, and it refers to the user's default
+ * directory (usually the user's home directory).</li>
+ * </ul>
+ * <p/>
+ * If you are still not tired then please go on and read the comment for
+ * {@link #setCharset(String)}.
+ *
+ * @author Christian Plattner, plattner@inf.ethz.ch
+ * @version $Id: SFTPv3Client.java 133 2014-04-14 12:26:29Z dkocher@sudo.ch $
+ */
+public class SFTPv3Client extends AbstractSFTPClient {
+    private static final Logger log = Logger.getLogger(SFTPv3Client.class);
+
+    /**
+     * Open the file for reading.
+     */
+    public static final int SSH_FXF_READ = 0x00000001;
+    /**
+     * Open the file for writing.  If both this and SSH_FXF_READ are
+     * specified, the file is opened for both reading and writing.
+     */
+    public static final int SSH_FXF_WRITE = 0x00000002;
+    /**
+     * Force all writes to append data at the end of the file.
+     */
+    public static final int SSH_FXF_APPEND = 0x00000004;
+    /**
+     * If this flag is specified, then a new file will be created if one
+     * does not alread exist (if O_TRUNC is specified, the new file will
+     * be truncated to zero length if it previously exists).
+     */
+    public static final int SSH_FXF_CREAT = 0x00000008;
+    /**
+     * Forces an existing file with the same name to be truncated to zero
+     * length when creating a file by specifying SSH_FXF_CREAT.
+     * SSH_FXF_CREAT MUST also be specified if this flag is used.
+     */
+    public static final int SSH_FXF_TRUNC = 0x00000010;
+    /**
+     * Causes the request to fail if the named file already exists.
+     */
+    public static final int SSH_FXF_EXCL = 0x00000020;
+
+    private PacketListener listener;
+
+    /**
+     * Create a SFTP v3 client.
+     *
+     * @param conn The underlying SSH-2 connection to be used.
+     * @throws IOException
+     */
+    public SFTPv3Client(Connection conn) throws IOException {
+        this(conn, new PacketListener() {
+            public void read(String packet) {
+                log.debug("Read packet " + packet);
+            }
+            public void write(String packet) {
+                log.debug("Write packet " + packet);
+            }
+        });
+    }
+
+    /**
+     * Create a SFTP v3 client.
+     *
+     * @param conn The underlying SSH-2 connection to be used.
+     * @throws IOException
+     */
+    public SFTPv3Client(Connection conn, PacketListener listener) throws IOException {
+        super(conn, 3, listener);
+        this.listener = listener;
+    }
+
+    public SFTPv3FileAttributes fstat(SFTPFileHandle handle) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(handle.getHandle(), 0, handle.getHandle().length);
+        sendMessage(Packet.SSH_FXP_FSTAT, req_id, tw.getBytes());
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != req_id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t == Packet.SSH_FXP_ATTRS) {
+            return new SFTPv3FileAttributes(tr);
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    private SFTPv3FileAttributes statBoth(String path, int statMethod) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(path, this.getCharset());
+        sendMessage(statMethod, req_id, tw.getBytes());
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != req_id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t == Packet.SSH_FXP_ATTRS) {
+            return new SFTPv3FileAttributes(tr);
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    public SFTPv3FileAttributes stat(String path) throws IOException {
+        return statBoth(path, Packet.SSH_FXP_STAT);
+    }
+
+    public SFTPv3FileAttributes lstat(String path) throws IOException {
+        return statBoth(path, Packet.SSH_FXP_LSTAT);
+    }
+
+
+    private List<SFTPv3DirectoryEntry> scanDirectory(byte[] handle) throws IOException {
+        List<SFTPv3DirectoryEntry> files = new ArrayList<SFTPv3DirectoryEntry>();
+
+        while (true) {
+            int req_id = generateNextRequestID();
+            TypesWriter tw = new TypesWriter();
+            tw.writeString(handle, 0, handle.length);
+            sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes());
+            byte[] resp = receiveMessage(34000);
+            TypesReader tr = new TypesReader(resp);
+            int t = tr.readByte();
+            listener.read(Packet.forName(t));
+            int rep_id = tr.readUINT32();
+
+            if (rep_id != req_id) {
+                throw new RequestMismatchException();
+            }
+
+            if (t == Packet.SSH_FXP_NAME) {
+                int count = tr.readUINT32();
+
+                if (log.isDebugEnabled()) {
+                    log.debug(String.format("Parsing %d name entries", count));
+                }
+
+                while (count > 0) {
+                    SFTPv3DirectoryEntry file = new SFTPv3DirectoryEntry();
+                    file.filename = tr.readString(this.getCharset());
+                    file.longEntry = tr.readString(this.getCharset());
+                    listener.read(file.longEntry);
+                    file.attributes = new SFTPv3FileAttributes(tr);
+
+                    if (log.isDebugEnabled()) {
+                        log.debug(String.format("Adding file %s", file));
+                    }
+
+                    files.add(file);
+                    count--;
+                }
+
+                continue;
+            }
+
+            if (t != Packet.SSH_FXP_STATUS) {
+                throw new PacketTypeException(t);
+            }
+
+            int errorCode = tr.readUINT32();
+
+            if (errorCode == ErrorCodes.SSH_FX_EOF) {
+                return files;
+            }
+
+            String errorMessage = tr.readString();
+            listener.read(errorMessage);
+            throw new SFTPException(errorMessage, errorCode);
+        }
+    }
+
+    public final SFTPv3FileHandle openDirectory(String path) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(path, this.getCharset());
+        sendMessage(Packet.SSH_FXP_OPENDIR, req_id, tw.getBytes());
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != req_id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t == Packet.SSH_FXP_HANDLE) {
+            return new SFTPv3FileHandle(this, tr.readByteString());
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    /**
+     * List the contents of a directory.
+     *
+     * @param dirName See the {@link SFTPv3Client comment} for the class for more details.
+     * @return A Vector containing {@link SFTPv3DirectoryEntry} objects.
+     * @throws IOException
+     */
+    public List<SFTPv3DirectoryEntry> ls(String dirName) throws IOException {
+        SFTPv3FileHandle handle = openDirectory(dirName);
+        List<SFTPv3DirectoryEntry> result = scanDirectory(handle.getHandle());
+        closeFile(handle);
+        return result;
+    }
+
+    /**
+     * Open a file for reading.
+     *
+     * @param filename See the {@link SFTPv3Client comment} for the class for more details.
+     * @return a SFTPv3FileHandle handle
+     * @throws IOException
+     */
+    public SFTPv3FileHandle openFileRO(String filename) throws IOException {
+        return openFile(filename, SSH_FXF_READ, new SFTPv3FileAttributes());
+    }
+
+    /**
+     * Open a file for reading and writing.
+     *
+     * @param filename See the {@link SFTPv3Client comment} for the class for more details.
+     * @return a SFTPv3FileHandle handle
+     * @throws IOException
+     */
+    public SFTPv3FileHandle openFileRW(String filename) throws IOException {
+        return openFile(filename, SSH_FXF_READ | SSH_FXF_WRITE, new SFTPv3FileAttributes());
+    }
+
+    /**
+     * Open a file in append mode. The SFTP v3 draft says nothing but assuming normal POSIX
+     * behavior, all writes will be appendend to the end of the file, no matter which offset
+     * one specifies.
+     * <p/>
+     * A side note for the curious: OpenSSH does an lseek() to the specified writing offset before each write(),
+     * even for writes to files opened in O_APPEND mode. However, bear in mind that when working
+     * in the O_APPEND mode, each write() includes an implicit lseek() to the end of the file
+     * (well, this is what the newsgroups say).
+     *
+     * @param filename See the {@link SFTPv3Client comment} for the class for more details.
+     * @return a SFTPv3FileHandle handle
+     * @throws IOException
+     */
+    public SFTPv3FileHandle openFileRWAppend(String filename) throws IOException {
+        return openFile(filename, SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND, new SFTPv3FileAttributes());
+    }
+
+    /**
+     * Open a file in append mode. The SFTP v3 draft says nothing but assuming normal POSIX
+     * behavior, all writes will be appendend to the end of the file, no matter which offset
+     * one specifies.
+     * <p/>
+     * A side note for the curious: OpenSSH does an lseek() to the specified writing offset before each write(),
+     * even for writes to files opened in O_APPEND mode. However, bear in mind that when working
+     * in the O_APPEND mode, each write() includes an implicit lseek() to the end of the file
+     * (well, this is what the newsgroups say).
+     *
+     * @param filename See the {@link SFTPv3Client comment} for the class for more details.
+     * @return a SFTPv3FileHandle handle
+     * @throws IOException
+     */
+    public SFTPv3FileHandle openFileWAppend(String filename) throws IOException {
+        return openFile(filename, SSH_FXF_WRITE | SSH_FXF_APPEND, new SFTPv3FileAttributes());
+    }
+
+    public SFTPv3FileHandle createFile(String filename) throws IOException {
+        return createFile(filename, new SFTPv3FileAttributes());
+    }
+
+    public SFTPv3FileHandle createFile(String filename, SFTPFileAttributes attr) throws IOException {
+        return openFile(filename, SSH_FXF_CREAT | SSH_FXF_READ | SSH_FXF_WRITE, attr);
+    }
+
+    /**
+     * Create a file (truncate it if it already exists) and open it for writing.
+     * Same as {@link #createFileTruncate(String, SFTPFileAttributes) createFileTruncate(filename, null)}.
+     *
+     * @param filename See the {@link SFTPv3Client comment} for the class for more details.
+     * @return a SFTPv3FileHandle handle
+     * @throws IOException
+     */
+    public SFTPv3FileHandle createFileTruncate(String filename) throws IOException {
+        return createFileTruncate(filename, new SFTPv3FileAttributes());
+    }
+
+    /**
+     * reate a file (truncate it if it already exists) and open it for writing.
+     * You can specify the default attributes of the file (the server may or may
+     * not respect your wishes).
+     *
+     * @param filename See the {@link SFTPv3Client comment} for the class for more details.
+     * @param attr     may be <code>null</code> to use server defaults. Probably only
+     *                 the <code>uid</code>, <code>gid</code> and <code>permissions</code>
+     *                 (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle}
+     *                 structure make sense. You need only to set those fields where you want
+     *                 to override the server's defaults.
+     * @return a SFTPv3FileHandle handle
+     * @throws IOException
+     */
+    public SFTPv3FileHandle createFileTruncate(String filename, SFTPFileAttributes attr) throws IOException {
+        return openFile(filename, SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_WRITE, attr);
+    }
+
+    public SFTPv3FileHandle openFile(String filename, int flags) throws IOException {
+        return openFile(filename, flags, new SFTPv3FileAttributes());
+    }
+
+    @Override
+    public SFTPv3FileHandle openFile(String filename, int flags, SFTPFileAttributes attr) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(filename, this.getCharset());
+        tw.writeUINT32(flags);
+        tw.writeBytes(attr.toBytes());
+        sendMessage(Packet.SSH_FXP_OPEN, req_id, tw.getBytes());
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != req_id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t == Packet.SSH_FXP_HANDLE) {
+            return new SFTPv3FileHandle(this, tr.readByteString());
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    @Override
+    public void createSymlink(String src, String target) throws IOException {
+        int req_id = generateNextRequestID();
+        // Changed semantics of src and target. The bug is known on SFTP servers shipped with all
+        // versions of OpenSSH (Bug #861).
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(target, this.getCharset());
+        tw.writeString(src, this.getCharset());
+        sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPv3DirectoryEntry.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * A <code>SFTPv3DirectoryEntry</code> as returned by {@link SFTPv3Client#ls(String)}.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class SFTPv3DirectoryEntry implements SFTPDirectoryEntry {
+    /**
+     * A relative name within the directory, without any path components.
+     */
+    public String filename;
+
+    /**
+     * An expanded format for the file name, similar to what is returned by
+     * "ls -l" on Un*x systems.
+     * <p/>
+     * The format of this field is unspecified by the SFTP v3 protocol.
+     * It MUST be suitable for use in the output of a directory listing
+     * command (in fact, the recommended operation for a directory listing
+     * command is to simply display this data).  However, clients SHOULD NOT
+     * attempt to parse the longname field for file attributes; they SHOULD
+     * use the attrs field instead.
+     * <p/>
+     * The recommended format for the longname field is as follows:<br>
+     * <code>-rwxr-xr-x   1 mjos     staff      348911 Mar 25 14:29 t-filexfer</code>
+     */
+    public String longEntry;
+
+    /**
+     * The attributes of this entry.
+     */
+    public SFTPv3FileAttributes attributes;
+
+    public String getFilename() {
+        return filename;
+    }
+
+    public SFTPv3FileAttributes getAttributes() {
+        return attributes;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("SFTPv3DirectoryEntry{");
+        sb.append("filename='").append(filename).append('\'');
+        sb.append(", attributes=").append(attributes);
+        sb.append('}');
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPv3FileAttributes.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+import ch.ethz.ssh2.sftp.AttribFlags;
+
+/**
+ * A <code>SFTPv3FileAttributes</code> object represents detail information
+ * about a file on the server. Not all fields may/must be present.
+ *
+ * @author Christian Plattner, plattner@inf.ethz.ch
+ * @version $Id: SFTPv3FileAttributes.java 133 2014-04-14 12:26:29Z dkocher@sudo.ch $
+ */
+public class SFTPv3FileAttributes implements SFTPFileAttributes {
+    /**
+     * The SIZE attribute. <code>NULL</code> if not present.
+     */
+    public Long size = null;
+
+    /**
+     * The UID attribute. <code>NULL</code> if not present.
+     */
+    public Integer uid = null;
+
+    /**
+     * The GID attribute. <code>NULL</code> if not present.
+     */
+    public Integer gid = null;
+
+    /**
+     * The POSIX permissions. <code>NULL</code> if not present.
+     * <p/>
+     * Here is a list:
+     * <p/>
+     * <pre>Note: these numbers are all OCTAL.
+     * <p/>
+     *  S_IFMT     0170000   bitmask for the file type bitfields
+     *  S_IFSOCK   0140000   socket
+     *  S_IFLNK    0120000   symbolic link
+     *  S_IFREG    0100000   regular file
+     *  S_IFBLK    0060000   block device
+     *  S_IFDIR    0040000   directory
+     *  S_IFCHR    0020000   character device
+     *  S_IFIFO    0010000   fifo
+     *  S_ISUID    0004000   set UID bit
+     *  S_ISGID    0002000   set GID bit
+     *  S_ISVTX    0001000   sticky bit
+     * <p/>
+     *  S_IRWXU    00700     mask for file owner permissions
+     *  S_IRUSR    00400     owner has read permission
+     *  S_IWUSR    00200     owner has write permission
+     *  S_IXUSR    00100     owner has execute permission
+     *  S_IRWXG    00070     mask for group permissions
+     *  S_IRGRP    00040     group has read permission
+     *  S_IWGRP    00020     group has write permission
+     *  S_IXGRP    00010     group has execute permission
+     *  S_IRWXO    00007     mask for permissions for others (not in group)
+     *  S_IROTH    00004     others have read permission
+     *  S_IWOTH    00002     others have write permisson
+     *  S_IXOTH    00001     others have execute permission
+     * </pre>
+     */
+    public Integer permissions = null;
+
+    /**
+     * Last access time of the file.
+     * <p/>
+     * The atime attribute. Represented as seconds from Jan 1, 1970 in UTC.
+     * <code>NULL</code> if not present.
+     */
+    public Integer atime = null;
+
+    /**
+     * The mtime attribute. Represented as seconds from Jan 1, 1970 in UTC.
+     * <code>NULL</code> if not present.
+     */
+    public Integer mtime = null;
+
+    /**
+     * Checks if this entry is a directory.
+     *
+     * @return Returns true if permissions are available and they indicate
+     * that this entry represents a directory.
+     */
+    public boolean isDirectory() {
+        if (permissions == null) {
+            return false;
+        }
+
+        return ((permissions & 0040000) == 0040000);
+    }
+
+    /**
+     * Checks if this entry is a regular file.
+     *
+     * @return Returns true if permissions are available and they indicate
+     * that this entry represents a regular file.
+     */
+    public boolean isRegularFile() {
+        if (permissions == null) {
+            return false;
+        }
+
+        return ((permissions & 0100000) == 0100000);
+    }
+
+    /**
+     * Checks if this entry is a a symlink.
+     *
+     * @return Returns true if permissions are available and they indicate
+     * that this entry represents a symlink.
+     */
+    public boolean isSymlink() {
+        if (permissions == null) {
+            return false;
+        }
+
+        return ((permissions & 0120000) == 0120000);
+    }
+
+    /**
+     * Turn the POSIX permissions into a 7 digit octal representation.
+     * Note: the returned value is first masked with <code>0177777</code>.
+     *
+     * @return <code>NULL</code> if permissions are not available.
+     */
+    public String getOctalPermissions() {
+        if (permissions == null) {
+            return null;
+        }
+
+        String res = Integer.toString(permissions.intValue() & 0177777, 8);
+        StringBuilder sb = new StringBuilder();
+        int leadingZeros = 7 - res.length();
+
+        while (leadingZeros > 0) {
+            sb.append('0');
+            leadingZeros--;
+        }
+
+        sb.append(res);
+        return sb.toString();
+    }
+
+    public SFTPv3FileAttributes() {
+        //
+    }
+
+    /**
+     * uint32   valid-attribute-flags
+     * byte     type                   always present
+     * uint64   size                   if flag SIZE
+     * uint64   allocation-size        if flag ALLOCATION_SIZE
+     * string   owner                  if flag OWNERGROUP
+     * string   group                  if flag OWNERGROUP
+     * uint32   permissions            if flag PERMISSIONS
+     * int64    atime                  if flag ACCESSTIME
+     * uint32   atime-nseconds            if flag SUBSECOND_TIMES
+     * int64    createtime             if flag CREATETIME
+     * uint32   createtime-nseconds       if flag SUBSECOND_TIMES
+     * int64    mtime                  if flag MODIFYTIME
+     * uint32   mtime-nseconds            if flag SUBSECOND_TIMES
+     * int64    ctime                  if flag CTIME
+     * uint32   ctime-nseconds            if flag SUBSECOND_TIMES
+     * string   acl                    if flag ACL
+     * uint32   attrib-bits            if flag BITS
+     * uint32   attrib-bits-valid      if flag BITS
+     * byte     text-hint              if flag TEXT_HINT
+     * string   mime-type              if flag MIME_TYPE
+     * uint32   link-count             if flag LINK_COUNT
+     * string   untranslated-name      if flag UNTRANSLATED_NAME
+     * uint32   extended-count         if flag EXTENDED
+     * extension-pair extensions
+     */
+    public SFTPv3FileAttributes(final TypesReader tr) throws IOException {
+        int flags = tr.readUINT32();
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SIZE) != 0) {
+            this.size = tr.readUINT64();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0) {
+            this.uid = tr.readUINT32();
+            this.gid = tr.readUINT32();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+            this.permissions = tr.readUINT32();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0) {
+            this.atime = tr.readUINT32();
+            this.mtime = tr.readUINT32();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_EXTENDED) != 0) {
+            int count = tr.readUINT32();
+
+            // Read it anyway to detect corrupt packets
+            while (count > 0) {
+                tr.readByteString();
+                tr.readByteString();
+                count--;
+            }
+        }
+    }
+
+    /**
+     * The same encoding is used both when returning file
+     * attributes from the server and when sending file attributes to the
+     * server.
+     *
+     * @return Encoded attributes
+     */
+    public byte[] toBytes() {
+        TypesWriter tw = new TypesWriter();
+        int attrFlags = 0;
+
+        if (this.size != null) {
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_SIZE;
+        }
+
+        if ((this.uid != null) && (this.gid != null)) {
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID;
+        }
+
+        if (this.permissions != null) {
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS;
+        }
+
+        if ((this.atime != null) && (this.mtime != null)) {
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME;
+        }
+
+        tw.writeUINT32(attrFlags);
+
+        if (this.size != null) {
+            tw.writeUINT64(this.size);
+        }
+
+        if ((this.uid != null) && (this.gid != null)) {
+            tw.writeUINT32(this.uid);
+            tw.writeUINT32(this.gid);
+        }
+
+        if (this.permissions != null) {
+            tw.writeUINT32(this.permissions);
+        }
+
+        if ((this.atime != null) && (this.mtime != null)) {
+            tw.writeUINT32(this.atime);
+            tw.writeUINT32(this.mtime);
+        }
+
+        return tw.getBytes();
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("SFTPv3FileAttributes{");
+        sb.append("size=").append(size);
+        sb.append(", uid=").append(uid);
+        sb.append(", gid=").append(gid);
+        sb.append(", permissions=").append(permissions);
+        sb.append(", atime=").append(atime);
+        sb.append(", mtime=").append(mtime);
+        sb.append('}');
+        return sb.toString();
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPv3FileHandle.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * A <code>SFTPv3FileHandle</code>.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class SFTPv3FileHandle extends SFTPFileHandle {
+    protected SFTPv3FileHandle(final SFTPv3Client client, final byte[] handle) {
+        super(client, handle);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPv6Client.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,300 @@
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+import ch.ethz.ssh2.sftp.AceMask;
+import ch.ethz.ssh2.sftp.ErrorCodes;
+import ch.ethz.ssh2.sftp.OpenFlags;
+import ch.ethz.ssh2.sftp.Packet;
+
+/**
+ * @version $Id: SFTPv6Client.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class SFTPv6Client extends AbstractSFTPClient {
+    private static final Logger log = Logger.getLogger(SFTPv6Client.class);
+
+    private PacketListener listener;
+
+    public SFTPv6Client(Connection conn) throws IOException {
+        this(conn, new PacketListener() {
+            public void read(String packet) {
+                log.debug("Read packet " + packet);
+            }
+            public void write(String packet) {
+                log.debug("Write packet " + packet);
+            }
+        });
+    }
+
+    public SFTPv6Client(Connection conn, PacketListener listener) throws IOException {
+        super(conn, 6, listener);
+        this.listener = listener;
+    }
+
+    public SFTPv6FileAttributes fstat(SFTPFileHandle handle) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(handle.getHandle(), 0, handle.getHandle().length);
+        sendMessage(Packet.SSH_FXP_FSTAT, req_id, tw.getBytes());
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != req_id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t == Packet.SSH_FXP_ATTRS) {
+            return new SFTPv6FileAttributes(tr);
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    private SFTPv6FileAttributes statBoth(String path, int statMethod) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(path, this.getCharset());
+        sendMessage(statMethod, req_id, tw.getBytes());
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != req_id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t == Packet.SSH_FXP_ATTRS) {
+            return new SFTPv6FileAttributes(tr);
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    public SFTPv6FileAttributes stat(String path) throws IOException {
+        return statBoth(path, Packet.SSH_FXP_STAT);
+    }
+
+    public SFTPv6FileAttributes lstat(String path) throws IOException {
+        return statBoth(path, Packet.SSH_FXP_LSTAT);
+    }
+
+
+    private List<SFTPv6DirectoryEntry> scanDirectory(byte[] handle) throws IOException {
+        List<SFTPv6DirectoryEntry> files = new ArrayList<SFTPv6DirectoryEntry>();
+
+        while (true) {
+            int req_id = generateNextRequestID();
+            TypesWriter tw = new TypesWriter();
+            tw.writeString(handle, 0, handle.length);
+            sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes());
+            byte[] resp = receiveMessage(34000);
+            TypesReader tr = new TypesReader(resp);
+            int t = tr.readByte();
+            listener.read(Packet.forName(t));
+            int rep_id = tr.readUINT32();
+
+            if (rep_id != req_id) {
+                throw new RequestMismatchException();
+            }
+
+            if (t == Packet.SSH_FXP_NAME) {
+                int count = tr.readUINT32();
+
+                if (log.isDebugEnabled()) {
+                    log.debug(String.format("Parsing %d name entries", count));
+                }
+
+                while (count > 0) {
+                    SFTPv6DirectoryEntry file = new SFTPv6DirectoryEntry();
+                    file.filename = tr.readString(this.getCharset());
+                    listener.read(file.filename);
+                    file.attributes = new SFTPv6FileAttributes(tr);
+
+                    if (log.isDebugEnabled()) {
+                        log.debug(String.format("Adding file %s", file));
+                    }
+
+                    files.add(file);
+                    count--;
+                }
+
+                continue;
+            }
+
+            if (t != Packet.SSH_FXP_STATUS) {
+                throw new PacketTypeException(t);
+            }
+
+            int errorCode = tr.readUINT32();
+
+            if (errorCode == ErrorCodes.SSH_FX_EOF) {
+                return files;
+            }
+
+            String errorMessage = tr.readString();
+            listener.read(errorMessage);
+            throw new SFTPException(errorMessage, errorCode);
+        }
+    }
+
+    public final SFTPFileHandle openDirectory(String path) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(path, this.getCharset());
+        sendMessage(Packet.SSH_FXP_OPENDIR, req_id, tw.getBytes());
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != req_id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t == Packet.SSH_FXP_HANDLE) {
+            return new SFTPFileHandle(this, tr.readByteString());
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    /**
+     * List the contents of a directory.
+     *
+     * @param dirName See the {@link SFTPv6Client comment} for the class for more details.
+     * @return A Vector containing {@link SFTPv6DirectoryEntry} objects.
+     * @throws IOException
+     */
+    public List<SFTPv6DirectoryEntry> ls(String dirName) throws IOException {
+        SFTPFileHandle handle = openDirectory(dirName);
+        List<SFTPv6DirectoryEntry> result = scanDirectory(handle.getHandle());
+        closeFile(handle);
+        return result;
+    }
+
+    /**
+     * Create a file and open it for reading and writing.
+     * Same as {@link #createFile(String, SFTPFileAttributes) createFile(fileName, null)}.
+     *
+     * @param filename See the {@link SFTPv6Client comment} for the class for more details.
+     * @return a SFTPFileHandle handle
+     * @throws IOException
+     */
+    public SFTPFileHandle createFile(String filename) throws IOException {
+        return createFile(filename, new SFTPv6FileAttributes());
+    }
+
+    /**
+     * Create a file and open it for reading and writing.
+     * You can specify the default attributes of the file (the server may or may
+     * not respect your wishes).
+     *
+     * @param filename See the {@link SFTPv6Client comment} for the class for more details.
+     * @param attr     may be <code>null</code> to use server defaults. Probably only
+     *                 the <code>uid</code>, <code>gid</code> and <code>permissions</code>
+     *                 (remember the server may apply a umask) entries of the {@link SFTPFileHandle}
+     *                 structure make sense. You need only to set those fields where you want
+     *                 to override the server's defaults.
+     * @return a SFTPFileHandle handle
+     * @throws IOException
+     */
+    public SFTPFileHandle createFile(String filename, SFTPFileAttributes attr) throws IOException {
+        return openFile(filename, OpenFlags.SSH_FXF_CREATE_NEW, attr);
+    }
+
+    public SFTPFileHandle openFile(String filename, int flags) throws IOException {
+        return this.openFile(filename, flags, new SFTPv6FileAttributes());
+    }
+
+    @Override
+    public SFTPFileHandle openFile(String filename, int flags, SFTPFileAttributes attr) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        tw.writeString(filename, this.getCharset());
+        tw.writeUINT32(AceMask.ACE4_READ_DATA | AceMask.ACE4_READ_ATTRIBUTES | AceMask.ACE4_READ_ACL | AceMask.ACE4_READ_NAMED_ATTRS
+                       | AceMask.ACE4_WRITE_DATA | AceMask.ACE4_APPEND_DATA | AceMask.ACE4_WRITE_ATTRIBUTES | AceMask.ACE4_WRITE_ACL | AceMask.ACE4_WRITE_NAMED_ATTRS);
+        tw.writeUINT32(flags);
+        tw.writeBytes(attr.toBytes());
+        sendMessage(Packet.SSH_FXP_OPEN, req_id, tw.getBytes());
+        byte[] resp = receiveMessage(34000);
+        TypesReader tr = new TypesReader(resp);
+        int t = tr.readByte();
+        listener.read(Packet.forName(t));
+        int rep_id = tr.readUINT32();
+
+        if (rep_id != req_id) {
+            throw new RequestMismatchException();
+        }
+
+        if (t == Packet.SSH_FXP_HANDLE) {
+            return new SFTPFileHandle(this, tr.readByteString());
+        }
+
+        if (t != Packet.SSH_FXP_STATUS) {
+            throw new PacketTypeException(t);
+        }
+
+        int errorCode = tr.readUINT32();
+        String errorMessage = tr.readString();
+        listener.read(errorMessage);
+        throw new SFTPException(errorMessage, errorCode);
+    }
+
+    @Override
+    public void createSymlink(String src, String target) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        // new-link-path
+        tw.writeString(src, this.getCharset());
+        // existing-path
+        tw.writeString(target, this.getCharset());
+        tw.writeBoolean(true);
+        sendMessage(Packet.SSH_FXP_LINK, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+
+    @Override
+    public void createHardlink(String src, String target) throws IOException {
+        int req_id = generateNextRequestID();
+        TypesWriter tw = new TypesWriter();
+        // new-link-path
+        tw.writeString(src, this.getCharset());
+        // existing-path
+        tw.writeString(target, this.getCharset());
+        tw.writeBoolean(false);
+        sendMessage(Packet.SSH_FXP_LINK, req_id, tw.getBytes());
+        expectStatusOKMessage(req_id);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPv6DirectoryEntry.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,33 @@
+package ch.ethz.ssh2;
+
+/**
+ * @version $Id: SFTPv6DirectoryEntry.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class SFTPv6DirectoryEntry implements SFTPDirectoryEntry {
+    /**
+     * A relative name within the directory, without any path components.
+     */
+    public String filename;
+
+    /**
+     * The attributes of this entry.
+     */
+    public SFTPv6FileAttributes attributes;
+
+    public String getFilename() {
+        return filename;
+    }
+
+    public SFTPv6FileAttributes getAttributes() {
+        return attributes;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("SFTPv6DirectoryEntry{");
+        sb.append("filename='").append(filename).append('\'');
+        sb.append(", attributes=").append(attributes);
+        sb.append('}');
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SFTPv6FileAttributes.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+import ch.ethz.ssh2.sftp.AttribFlags;
+import ch.ethz.ssh2.sftp.AttribTypes;
+
+/**
+ * A <code>SFTPv3FileAttributes</code> object represents detail information
+ * about a file on the server. Not all fields may/must be present.
+ *
+ * @author Christian Plattner, plattner@inf.ethz.ch
+ * @version $Id: SFTPv6FileAttributes.java 133 2014-04-14 12:26:29Z dkocher@sudo.ch $
+ */
+
+public class SFTPv6FileAttributes implements SFTPFileAttributes {
+
+    /**
+     * The type field is always present
+     *
+     * @see ch.ethz.ssh2.sftp.AttribTypes
+     */
+    private Integer type = null;
+
+    /**
+     * The SIZE attribute. <code>NULL</code> if not present.
+     */
+    public Long size = null;
+
+    /**
+     * The POSIX permissions. <code>NULL</code> if not present.
+     * <p/>
+     * Here is a list:
+     * <p/>
+     * <pre>Note: these numbers are all OCTAL.
+     * <p/>
+     *  S_IFMT     0170000   bitmask for the file type bitfields
+     *  S_IFSOCK   0140000   socket
+     *  S_IFLNK    0120000   symbolic link
+     *  S_IFREG    0100000   regular file
+     *  S_IFBLK    0060000   block device
+     *  S_IFDIR    0040000   directory
+     *  S_IFCHR    0020000   character device
+     *  S_IFIFO    0010000   fifo
+     *  S_ISUID    0004000   set UID bit
+     *  S_ISGID    0002000   set GID bit
+     *  S_ISVTX    0001000   sticky bit
+     * <p/>
+     *  S_IRWXU    00700     mask for file owner permissions
+     *  S_IRUSR    00400     owner has read permission
+     *  S_IWUSR    00200     owner has write permission
+     *  S_IXUSR    00100     owner has execute permission
+     *  S_IRWXG    00070     mask for group permissions
+     *  S_IRGRP    00040     group has read permission
+     *  S_IWGRP    00020     group has write permission
+     *  S_IXGRP    00010     group has execute permission
+     *  S_IRWXO    00007     mask for permissions for others (not in group)
+     *  S_IROTH    00004     others have read permission
+     *  S_IWOTH    00002     others have write permisson
+     *  S_IXOTH    00001     others have execute permission
+     * </pre>
+     */
+    public Integer permissions = null;
+
+    /**
+     * Creation time of the file.
+     * <p/>
+     * The createtime attribute. Represented as seconds from Jan 1, 1970 in UTC.
+     * <code>NULL</code> if not present.
+     */
+    public Long createtime = null;
+
+    /**
+     * Last access time of the file.
+     * <p/>
+     * The atime attribute. Represented as seconds from Jan 1, 1970 in UTC.
+     * <code>NULL</code> if not present.
+     */
+    public Long atime = null;
+
+    /**
+     * The mtime attribute. Represented as seconds from Jan 1, 1970 in UTC.
+     * <code>NULL</code> if not present.
+     */
+    public Long mtime = null;
+
+    /**
+     * Last time the file attributes were changed.  The exact meaning of this field depends on the server.
+     * <p/>
+     * The ctime attribute. Represented as seconds from Jan 1, 1970 in UTC.
+     * <code>NULL</code> if not present.
+     */
+    public Long ctime = null;
+
+    /**
+     * The 'owner' and 'group' fields are represented as UTF-8 strings. user@localhost represents
+     * a user in the context of the server.
+     */
+    public String owner = null;
+
+    /**
+     * The 'owner' and 'group' fields are represented as UTF-8 strings
+     */
+    public String group = null;
+
+    /**
+     * Checks if this entry is a directory.
+     *
+     * @return Returns true if permissions are available and they indicate
+     * that this entry represents a directory.
+     */
+    public boolean isDirectory() {
+        return (type & AttribTypes.SSH_FILEXFER_TYPE_DIRECTORY) == AttribTypes.SSH_FILEXFER_TYPE_DIRECTORY;
+    }
+
+    /**
+     * Checks if this entry is a regular file.
+     *
+     * @return Returns true if permissions are available and they indicate
+     * that this entry represents a regular file.
+     */
+    public boolean isRegularFile() {
+        return (type & AttribTypes.SSH_FILEXFER_TYPE_REGULAR) == AttribTypes.SSH_FILEXFER_TYPE_REGULAR;
+    }
+
+    /**
+     * Checks if this entry is a a symlink.
+     *
+     * @return Returns true if permissions are available and they indicate
+     * that this entry represents a symlink.
+     */
+    public boolean isSymlink() {
+        return (type & AttribTypes.SSH_FILEXFER_TYPE_SYMLINK) == AttribTypes.SSH_FILEXFER_TYPE_SYMLINK;
+    }
+
+    public SFTPv6FileAttributes() {
+        //
+    }
+
+    /**
+     * uint32   valid-attribute-flags
+     * byte     type                   always present
+     * uint64   size                   if flag SIZE
+     * uint64   allocation-size        if flag ALLOCATION_SIZE
+     * string   owner                  if flag OWNERGROUP
+     * string   group                  if flag OWNERGROUP
+     * uint32   permissions            if flag PERMISSIONS
+     * int64    atime                  if flag ACCESSTIME
+     * uint32   atime-nseconds            if flag SUBSECOND_TIMES
+     * int64    createtime             if flag CREATETIME
+     * uint32   createtime-nseconds       if flag SUBSECOND_TIMES
+     * int64    mtime                  if flag MODIFYTIME
+     * uint32   mtime-nseconds            if flag SUBSECOND_TIMES
+     * int64    ctime                  if flag CTIME
+     * uint32   ctime-nseconds            if flag SUBSECOND_TIMES
+     * string   acl                    if flag ACL
+     * uint32   attrib-bits            if flag BITS
+     * uint32   attrib-bits-valid      if flag BITS
+     * byte     text-hint              if flag TEXT_HINT
+     * string   mime-type              if flag MIME_TYPE
+     * uint32   link-count             if flag LINK_COUNT
+     * string   untranslated-name      if flag UNTRANSLATED_NAME
+     * uint32   extended-count         if flag EXTENDED
+     * extension-pair extensions
+     */
+    public SFTPv6FileAttributes(final TypesReader tr) throws IOException {
+        int flags = tr.readUINT32();
+        // The type field is always present
+        this.type = tr.readByte();
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SIZE) != 0) {
+            this.size = tr.readUINT64();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_ALLOCATION_SIZE) != 0) {
+            // Ignore
+            tr.readUINT64();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_OWNERGROUP) != 0) {
+            this.owner = tr.readString();
+            this.group = tr.readString();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+            this.permissions = tr.readUINT32();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_ACCESSTIME) != 0) {
+            this.atime = tr.readUINT64();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+            // Ignore
+            tr.readUINT32();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_CREATETIME) != 0) {
+            this.createtime = tr.readUINT64();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+            // Ignore
+            tr.readUINT32();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_MODIFYTIME) != 0) {
+            this.mtime = tr.readUINT64();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+            // Ignore
+            tr.readUINT32();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_CTIME) != 0) {
+            this.ctime = tr.readUINT64();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SUBSECOND_TIMES) != 0) {
+            // Ignore
+            tr.readUINT32();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_ACL) != 0) {
+            // Ignore
+            tr.readString();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_BITS) != 0) {
+            // Ignore attrib-bits
+            tr.readUINT32();
+            // Ignore attrib-bits-valid
+            tr.readUINT32();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_TEXT_HINT) != 0) {
+            // Ignore
+            tr.readByte();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_MIME_TYPE) != 0) {
+            // Ignore
+            tr.readString();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_LINK_COUNT) != 0) {
+            // Ignore
+            tr.readUINT32();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_UNTRANSLATED_NAME) != 0) {
+            // Ignore
+            tr.readString();
+        }
+
+        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_EXTENDED) != 0) {
+            int count = tr.readUINT32();
+
+            // Read it anyway to detect corrupt packets
+            while (count > 0) {
+                // extension-name
+                tr.readByteString();
+                // extension-data
+                tr.readByteString();
+                count--;
+            }
+        }
+    }
+
+    /**
+     * The same encoding is used both when returning file
+     * attributes from the server and when sending file attributes to the
+     * server.
+     *
+     * @return Encoded attributes
+     */
+    public byte[] toBytes() {
+        TypesWriter tw = new TypesWriter();
+        // The 'valid-attribute-flags' specifies which of the fields are present.  Those fields
+        // for which the corresponding flag is not set are not present
+        int attrFlags = 0;
+
+        if (this.size != null) {
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_SIZE;
+        }
+
+        if ((this.owner != null) && (this.group != null)) {
+            // If either the owner or group field is zero length, the field should
+            // be considered absent, and no change should be made to that specific
+            // field during a modification operation.
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_OWNERGROUP;
+        }
+
+        if (this.permissions != null) {
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS;
+        }
+
+        if (this.atime != null) {
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_ACCESSTIME;
+        }
+
+        if (this.createtime != null) {
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_CREATETIME;
+        }
+
+        if (this.mtime != null) {
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_MODIFYTIME;
+        }
+
+        if (this.ctime != null) {
+            attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_CTIME;
+        }
+
+        tw.writeUINT32(attrFlags);
+
+        // The type field is always present.
+        if (this.size != null) {
+            tw.writeUINT64(this.size);
+        }
+
+        if ((this.owner != null) && (this.group != null)) {
+            tw.writeString(owner);
+            tw.writeString(group);
+        }
+
+        if (this.permissions != null) {
+            tw.writeUINT32(this.permissions);
+        }
+
+        if (this.atime != null) {
+            tw.writeUINT64(this.atime);
+        }
+
+        if (this.createtime != null) {
+            tw.writeUINT64(this.createtime);
+        }
+
+        if (this.mtime != null) {
+            tw.writeUINT64(this.mtime);
+        }
+
+        if (this.ctime != null) {
+            tw.writeUINT64(this.ctime);
+        }
+
+        return tw.getBytes();
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("SFTPv6FileAttributes{");
+        sb.append("type=").append(type);
+        sb.append(", size=").append(size);
+        sb.append(", permissions=").append(permissions);
+        sb.append(", createtime=").append(createtime);
+        sb.append(", atime=").append(atime);
+        sb.append(", mtime=").append(mtime);
+        sb.append(", ctime=").append(ctime);
+        sb.append(", owner='").append(owner).append('\'');
+        sb.append(", group='").append(group).append('\'');
+        sb.append('}');
+        return sb.toString();
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ServerAuthenticationCallback.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2012-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+/**
+ * A callback used during the authentication phase (see RFC 4252) when
+ * implementing a SSH server.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public interface ServerAuthenticationCallback {
+    /**
+     * The method name for host-based authentication.
+     */
+    public final String METHOD_HOSTBASED = "hostbased";
+
+    /**
+     * The method name for public-key authentication.
+     */
+    public final String METHOD_PUBLICKEY = "publickey";
+
+    /**
+     * The method name for password authentication.
+     */
+    public final String METHOD_PASSWORD = "password";
+
+    /**
+     * Called when the client enters authentication.
+     * This gives you the chance to set a custom authentication banner
+     * for this SSH-2 session. This is the first method called in this interface.
+     * It will only called at most once per <code>ServerConnection</code>.
+     *
+     * @param sc The corresponding <code>ServerConnection</code>
+     * @return The authentication banner or <code>NULL</code> in case no banner should be send.
+     */
+    public String initAuthentication(ServerConnection sc);
+
+    /**
+     * Return the authentication methods that are currently available to the client.
+     * Be prepared to return this information at any time during the authentication procedure.
+     * <p/>
+     * The returned name-list of 'method names' (see RFC4252) indicate the authentication methods
+     * that may productively continue the authentication dialog.
+     * </p>
+     * It is RECOMMENDED that servers only include those 'method name'
+     * values in the name-list that are actually useful.  However, it is not
+     * illegal to include 'method name' values that cannot be used to
+     * authenticate the user.
+     * <p/>
+     * Already successfully completed authentications SHOULD NOT be included
+     * in the name-list, unless they should be performed again for some reason.
+     *
+     * @see #METHOD_HOSTBASED
+     * @see #METHOD_PASSWORD
+     * @see #METHOD_PUBLICKEY
+     *
+     * @param sc
+     * @return A list of method names.
+     */
+    public String[] getRemainingAuthMethods(ServerConnection sc);
+
+    /**
+     * Typically, this will be called be the client to get the list of
+     * authentication methods that can continue. You should simply return
+     * {@link AuthenticationResult#FAILURE}.
+     *
+     * @param sc
+     * @param username Name of the user that wants to log in with the "none" method.
+     * @return
+     */
+    public AuthenticationResult authenticateWithNone(ServerConnection sc, String username);
+
+    public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password);
+
+    /**
+     * NOTE: Not implemented yet.
+     *
+     * @param sc
+     * @param username
+     * @param algorithm
+     * @param publickey
+     * @param signature
+     * @return
+     */
+    public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
+            byte[] publickey, byte[] signature);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ServerConnection.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2012-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.Socket;
+import java.util.List;
+import java.util.ArrayList;
+
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.crypto.PEMDecoder;
+import ch.ethz.ssh2.server.ServerConnectionState;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import ch.ethz.ssh2.transport.ServerTransportManager;
+
+/**
+ * A server-side SSH-2 connection.
+ *
+ * @author Christian
+ *
+ */
+public class ServerConnection {
+    /**
+     * The softwareversion presented to the SSH-2 client.
+     */
+    private String softwareversion = String.format("Ganymed_SSHD_%s", Version.getSpecification());
+
+    private final ServerConnectionState state = new ServerConnectionState(this);
+
+    /**
+     * Creates a new <code>ServerConnection</code> that will communicate
+     * with the client over the given <code>Socket</code>.
+     * <p>
+     * Note: you need to call {@link #connect()} or {@link #connect(int)} to
+     * perform the initial handshake and establish the encrypted communication.
+     *
+     * @see #connect(int)
+     *
+     * @param s The socket
+     */
+    public ServerConnection(Socket s) {
+        this(s, null, null, null);
+    }
+
+    public ServerConnection(Socket s, String softwareversion) {
+        this(s, null, null, null);
+        this.softwareversion = softwareversion;
+    }
+
+    /**
+     * Creates a new <code>ServerConnection</code> that will communicate
+     * with the client over the given <code>Socket</code>.
+     * <p>
+     * Note: you need to call {@link #connect()} or {@link #connect(int)} to
+     * perform the initial handshake and establish the encrypted communication.
+     * <p>
+     * Please read the javadoc for the {@link #connect(int)} method.
+     *
+     * @see #connect(int)
+     *
+     * @param s The socket
+     * @param dsa_key The DSA hostkey, may be <code>NULL</code>
+     * @param rsa_key The RSA hostkey, may be <code>NULL</code>
+     * @param ec_key  The EC  hostkey, may be <code>NULL</code>
+     */
+    public ServerConnection(Socket s, KeyPair dsa_key, KeyPair rsa_key, KeyPair ec_key) {
+        state.s = s;
+        state.softwareversion = softwareversion;
+        state.next_dsa_key = dsa_key;
+        state.next_rsa_key = rsa_key;
+        state.next_ec_key  = ec_key;
+        fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
+    }
+
+    /**
+     * Establish the connection and block until the first handshake has completed.
+     * <p>
+     * Note: this is a wrapper that calls <code>connect(0)</code> (i.e., connect with no timeout).
+     * <p>
+     * Please read the javadoc for the {@link #connect(int)} method.
+     *
+     * @see #connect(int)
+     *
+     * @throws IOException
+     */
+
+    public synchronized void connect() throws IOException {
+        connect(0);
+    }
+
+    /**
+     * Establish the connection and block until the first handshake has completed.
+     * <p>
+     * Note 1: at least one DSA, RSA or EC hostkey must be set before calling this method.
+     * <p>
+     * Note 2: You must set the callbacks for authentication ({@link #setAuthenticationCallback(ServerAuthenticationCallback)})
+     * and connection events ({@link #setServerConnectionCallback(ServerConnectionCallback)}).
+     *
+     * @see #setPEMHostKey(char[], String)
+     * @see #setPEMHostKey(File, String)
+     * @see #setRsaHostKey(RSAPrivateKey)
+     * @see #setDsaHostKey(DSAPrivateKey)
+     *
+     * @param timeout_milliseconds Timeout in milliseconds, <code>0</code> means no timeout.
+     * @throws IOException
+     */
+
+    public synchronized void connect(int timeout_milliseconds) throws IOException {
+        synchronized (state) {
+            if (state.cb_conn == null)
+                throw new IllegalStateException("The callback for connection events has not been set.");
+
+            if (state.cb_auth == null)
+                throw new IllegalStateException("The callback for authentication events has not been set.");
+
+            if (state.tm != null)
+                throw new IllegalStateException("The initial handshake has already been started.");
+
+            if ((state.next_dsa_key == null) && (state.next_rsa_key == null) && (state.next_ec_key == null))
+                throw new IllegalStateException("Neither an RSA nor a DSA nor an EC host key has been specified!");
+
+            state.tm = new ServerTransportManager(state.s);
+        }
+
+        state.tm.connect(state);
+        /* Wait until first KEX has finished */
+        state.tm.getConnectionInfo(1);
+    }
+
+    /**
+     * Retrieve the underlying socket.
+     *
+     * @return the socket that has been passed to the constructor.
+     */
+    public Socket getSocket() {
+        return state.s;
+    }
+
+    /**
+     * Force an asynchronous key re-exchange (the call does not block). The
+     * latest values set for MAC, Cipher and DH group exchange parameters will
+     * be used. If a key exchange is currently in progress, then this method has
+     * the only effect that the so far specified parameters will be used for the
+     * next (client driven) key exchange. You may call this method only after
+     * the initial key exchange has been established.
+     * <p>
+     * Note: This implementation will never start automatically a key exchange (other than the initial one)
+     * unless you or the connected SSH-2 client ask for it.
+     *
+     * @throws IOException
+     *             In case of any failure behind the scenes.
+     */
+
+    public synchronized void forceKeyExchange() throws IOException {
+        synchronized (state) {
+            if (state.tm == null)
+                throw new IllegalStateException(
+                    "Cannot force another key exchange, you need to start the key exchange first.");
+
+            state.tm.forceKeyExchange(state.next_cryptoWishList, null, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
+        }
+    }
+
+    /**
+     * Returns a {@link ConnectionInfo} object containing the details of
+     * the connection. May be called as soon as the first key exchange has been
+     * started. The method blocks in case the first key exchange has not been completed.
+     * <p>
+     * Note: upon return of this method, authentication may still be pending.
+     *
+     * @return A {@link ConnectionInfo} object.
+     * @throws IOException
+     *             In case of any failure behind the scenes; e.g., first key exchange was aborted.
+     */
+
+    public synchronized ConnectionInfo getConnectionInfo() throws IOException {
+        synchronized (state) {
+            if (state.tm == null)
+                throw new IllegalStateException(
+                    "Cannot get details of connection, you need to start the key exchange first.");
+        }
+
+        return state.tm.getConnectionInfo(1);
+    }
+
+    /**
+     * Change the current DSA hostkey. Either a DSA or RSA or EC private key must be set for a successful handshake with
+     * the client.
+     * <p>
+     * Note: You can change an existing DSA hostkey after the initial kex exchange (the new value will
+     * be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the
+     * current DSA key, otherwise the next key exchange may fail in case the client supports only DSA hostkeys.
+     *
+     * @param dsa_hostkey
+     */
+
+    public synchronized void setDsaHostKey(KeyPair dsa_hostkey) {
+        synchronized (state) {
+            if ((dsa_hostkey == null) && (state.next_dsa_key != null) && (state.tm != null))
+                throw new IllegalStateException("Cannot remove DSA hostkey after first key exchange.");
+
+            state.next_dsa_key = dsa_hostkey;
+            fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
+        }
+    }
+
+    /**
+     * Change the current RSA hostkey. Either a DSA or RSA or EC private key must be set for a successful handshake with
+     * the client.
+     * <p>
+     * Note: You can change an existing RSA hostkey after the initial kex exchange (the new value will
+     * be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the
+     * current RSA key, otherwise the next key exchange may fail in case the client supports only RSA hostkeys.
+     *
+     * @param rsa_hostkey
+     */
+
+    public synchronized void setRsaHostKey(KeyPair rsa_hostkey) {
+        synchronized (state) {
+            if ((rsa_hostkey == null) && (state.next_rsa_key != null) && (state.tm != null))
+                throw new IllegalStateException("Cannot remove RSA hostkey after first key exchange.");
+
+            state.next_rsa_key = rsa_hostkey;
+            fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
+        }
+    }
+
+    /**
+     * Change the current EC hostkey. Either a DSA or RSA or EC private key must be set for a successful handshake with
+     * the client.
+     * <p>
+     * Note: You can change an existing EC hostkey after the initial kex exchange (the new value will
+     * be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the
+     * current EC key, otherwise the next key exchange may fail in case the client supports only EC hostkeys.
+     *
+     * @param rsa_hostkey
+     */
+
+    public synchronized void setEcHostKey(KeyPair ec_hostkey) {
+        synchronized (state) {
+            if ((ec_hostkey == null) && (state.next_ec_key != null) && (state.tm != null))
+                throw new IllegalStateException("Cannot remove EC hostkey after first key exchange.");
+
+            state.next_ec_key = ec_hostkey;
+            fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
+        }
+    }
+
+    /**
+     * Utility method that loads a PEM based hostkey (either RSA or DSA based) and
+     * calls either <code>setRsaHostKey()</code> or <code>setDsaHostKey()</code>.
+     *
+     * @param pemdata The PEM data
+     * @param password Password, may be null in case the PEM data is not password protected
+     * @throws IOException In case of any error.
+     */
+    public void setPEMHostKey(char[] pemdata, String password) throws IOException {
+        KeyPair pair = PEMDecoder.decode(pemdata, password);
+        PrivateKey key = pair.getPrivate();
+
+        if (key instanceof DSAPrivateKey) setDsaHostKey(pair);
+
+        if (key instanceof RSAPrivateKey) setRsaHostKey(pair);
+
+        if (key instanceof ECPrivateKey) setEcHostKey(pair);
+    }
+
+    /**
+     * Utility method that loads a hostkey from a PEM file (either RSA or DSA based) and
+     * calls either <code>setRsaHostKey()</code> or <code>setDsaHostKey()</code>.
+     *
+     * @param pemFile The PEM file
+     * @param password Password, may be null in case the PEM file is not password protected
+     * @throws IOException
+     */
+    public void setPEMHostKey(File pemFile, String password) throws IOException {
+        if (pemFile == null)
+            throw new IllegalArgumentException("pemfile argument is null");
+
+        char[] buff = new char[256];
+        CharArrayWriter cw = new CharArrayWriter();
+        FileReader fr = new FileReader(pemFile);
+
+        while (true) {
+            int len = fr.read(buff);
+
+            if (len < 0)
+                break;
+
+            cw.write(buff, 0, len);
+        }
+
+        fr.close();
+        setPEMHostKey(cw.toCharArray(), password);
+    }
+
+    private void fixCryptoWishList(CryptoWishList next_cryptoWishList, KeyPair next_dsa_key, KeyPair next_rsa_key, KeyPair next_ec_key) {
+        List<String> algos = new ArrayList<String>();
+
+        if (next_ec_key != null)  algos.add("ecdsa-sha2-nistp521");
+
+        if (next_ec_key != null)  algos.add("ecdsa-sha2-nistp384");
+
+        if (next_ec_key != null)  algos.add("ecdsa-sha2-nistp256");
+
+        if (next_dsa_key != null) algos.add("ssh-dss");
+
+        if (next_rsa_key != null) algos.add("ssh-rsa");
+
+        next_cryptoWishList.serverHostKeyAlgorithms = new String[algos.size()];
+        algos.toArray(next_cryptoWishList.serverHostKeyAlgorithms);
+    }
+
+    /**
+     * Callback interface with methods that will be called upon events
+     * generated by the client (e.g., client opens a new Session which results in a <code>ServerSession</code>).
+     * <p>
+     * Note: This must be set before the first handshake.
+     *
+     * @param cb The callback implementation
+     */
+
+    public synchronized void setServerConnectionCallback(ServerConnectionCallback cb) {
+        synchronized (state) {
+            state.cb_conn = cb;
+        }
+    }
+
+    /**
+     * Callback interface with methods that will be called upon authentication events.
+     * <p>
+     * Note: This must be set before the first handshake.
+     *
+     * @param cb The callback implementation
+     */
+
+    public synchronized void setAuthenticationCallback(ServerAuthenticationCallback cb) {
+        synchronized (state) {
+            state.cb_auth = cb;
+        }
+    }
+
+    /**
+     * Close the connection to the SSH-2 server. All assigned sessions will be
+     * closed, too. Can be called at any time. Don't forget to call this once
+     * you don't need a connection anymore - otherwise the receiver thread may
+     * run forever.
+     */
+    public void close() {
+        synchronized (state) {
+            if (state.cm != null)
+                state.cm.closeAllChannels();
+
+            if (state.tm != null) {
+                state.tm.close(new Throwable("Closed due to user request."), false);
+            }
+        }
+    }
+
+    public void close(IOException t) {
+        synchronized (state) {
+            if (state.cm != null)
+                state.cm.closeAllChannels();
+
+            if (state.tm != null) {
+                state.tm.close(t, false);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ServerConnectionCallback.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2012-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+public interface ServerConnectionCallback {
+    public ServerSessionCallback acceptSession(ServerSession session);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ServerHostKeyVerifier.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * A callback interface used to implement a client specific method of checking
+ * server host keys.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public interface ServerHostKeyVerifier {
+    /**
+     * The actual verifier method, it will be called by the key exchange code
+     * on EVERY key exchange - this can happen several times during the lifetime
+     * of a connection.
+     * <p>
+     * Note: SSH-2 servers are allowed to change their hostkey at ANY time.
+     *
+     * @param hostname the hostname used to create the {@link Connection} object
+     * @param port the remote TCP port
+     * @param serverHostKeyAlgorithm the public key algorithm (<code>ssh-rsa</code> or <code>ssh-dss</code>)
+     * @param serverHostKey the server's public key blob
+     * @return if the client wants to accept the server's host key - if not, the
+     *         connection will be closed.
+     * @throws Exception Will be wrapped with an IOException, extended version of returning false =)
+     */
+    public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey)
+    throws Exception;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ServerSession.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2012-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A ServerSession represents the server-side of a session channel.
+ *
+ * @see Session
+ *
+ * @author Christian
+ *
+ */
+public interface ServerSession {
+    public InputStream getStdout();
+
+    public InputStream getStderr();
+
+    public OutputStream getStdin();
+
+    public void close();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ServerSessionCallback.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+/**
+ * The interface for an object that receives events based on requests that
+ * a client sends on a session channel. Once the session channel has been set up,
+ * a program is started at the server end. The program can be a shell, an application
+ * program, or a subsystem with a host-independent name. Only one of these requests
+ * can succeed per channel.
+ * <p/>
+ * <b>CAUTION: These event methods are being called from the receiver thread. The receiving of
+ * messages will be blocked until your event handler returns. To signal to the client that you
+ * are willing to accept its request, return a {@link Runnable} object which will be executed
+ * in a new thread <i>after</i> the acknowledgment has been sent back to the client.</b>
+ * <p/>
+ * <b>If a method does not allow you to return a {@link Runnable}, then the
+ * SSH protocol does not allow to send a status back to the client (more exactly, the client cannot
+ * request an acknowledgment). In these cases, if you need to invoke
+ * methods on the {@link ServerSession} or plan to execute long-running activity, then please do this from within a new {@link Thread}.</b>
+ * <p/>
+ * If you want to signal a fatal error, then please throw an <code>IOException</code>. Currently, this will
+ * tear down the whole SSH connection.
+ *
+ * @see ServerSession
+ *
+ * @author Christian
+ *
+ */
+public interface ServerSessionCallback {
+    public Runnable requestPtyReq(ServerSession ss, PtySettings pty) throws IOException;
+
+    public Runnable requestEnv(ServerSession ss, String name, String value) throws IOException;
+
+    public Runnable requestShell(ServerSession ss) throws IOException;
+
+    public Runnable requestExec(ServerSession ss, String command) throws IOException;
+
+    public Runnable requestSubsystem(ServerSession ss, String subsystem) throws IOException;
+
+    /**
+     * When the window (terminal) size changes on the client side, it MAY send a message to the other side to inform it of the new dimensions.
+     *
+     * @param ss the corresponding session
+     * @param term_width_columns
+     * @param term_height_rows
+     * @param term_width_pixels
+     */
+    public void requestWindowChange(ServerSession ss, int term_width_columns, int term_height_rows,
+                                    int term_width_pixels, int term_height_pixels) throws IOException;
+
+    /**
+     * A signal can be delivered to the remote process/service. Some systems may not implement signals, in which case they SHOULD ignore this message.
+     *
+     * @param ss the corresponding session
+     * @param signal (a string without the "SIG" prefix)
+     * @return
+     * @throws IOException
+     */
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/Session.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.channel.Channel;
+import ch.ethz.ssh2.channel.ChannelManager;
+import ch.ethz.ssh2.channel.X11ServerData;
+
+/**
+ * A <code>Session</code> is a remote execution of a program. "Program" means
+ * in this context either a shell, an application or a system command. The
+ * program may or may not have a tty. Only one single program can be started on
+ * a session. However, multiple sessions can be active simultaneously.
+ *
+ * @author Christian Plattner
+ * @version $Id: Session.java 96 2014-04-08 15:14:37Z dkocher@sudo.ch $
+ */
+public class Session {
+    private ChannelManager cm;
+    private Channel cn;
+
+    private boolean flag_pty_requested = false;
+    private boolean flag_x11_requested = false;
+    private boolean flag_execution_started = false;
+    private boolean flag_closed = false;
+
+    private String x11FakeCookie = null;
+
+    private final SecureRandom rnd;
+
+    protected Session(ChannelManager cm, SecureRandom rnd) throws IOException {
+        this.cm = cm;
+        this.cn = cm.openSessionChannel();
+        this.rnd = rnd;
+    }
+
+    /**
+     * Basically just a wrapper for lazy people - identical to calling
+     * <code>requestPTY("dumb", 0, 0, 0, 0, null)</code>.
+     *
+     * @throws IOException
+     */
+    public void requestDumbPTY() throws IOException {
+        requestPTY("dumb", 0, 0, 0, 0, null);
+    }
+
+    /**
+     * Basically just another wrapper for lazy people - identical to calling
+     * <code>requestPTY(term, 0, 0, 0, 0, null)</code>.
+     *
+     * @throws IOException
+     */
+    public void requestPTY(String term) throws IOException {
+        requestPTY(term, 0, 0, 0, 0, null);
+    }
+
+    /**
+     * Allocate a pseudo-terminal for this session.
+     * <p/>
+     * This method may only be called before a program or shell is started in
+     * this session.
+     * <p/>
+     * Different aspects can be specified:
+     * <p/>
+     * <ul>
+     * <li>The TERM environment variable value (e.g., vt100)</li>
+     * <li>The terminal's dimensions.</li>
+     * <li>The encoded terminal modes.</li>
+     * </ul>
+     * Zero dimension parameters are ignored. The character/row dimensions
+     * override the pixel dimensions (when nonzero). Pixel dimensions refer to
+     * the drawable area of the window. The dimension parameters are only
+     * informational. The encoding of terminal modes (parameter
+     * <code>terminal_modes</code>) is described in RFC4254.
+     *
+     * @param term The TERM environment variable value (e.g., vt100)
+     * @param term_width_characters terminal width, characters (e.g., 80)
+     * @param term_height_characters terminal height, rows (e.g., 24)
+     * @param term_width_pixels terminal width, pixels (e.g., 640)
+     * @param term_height_pixels terminal height, pixels (e.g., 480)
+     * @param terminal_modes encoded terminal modes (may be <code>null</code>)
+     * @throws IOException
+     */
+    public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels,
+                           int term_height_pixels, byte[] terminal_modes) throws IOException {
+        if (term == null)
+            throw new IllegalArgumentException("TERM cannot be null.");
+
+        if ((terminal_modes != null) && (terminal_modes.length > 0)) {
+            if (terminal_modes[terminal_modes.length - 1] != 0)
+                throw new IOException("Illegal terminal modes description, does not end in zero byte");
+        }
+        else
+            terminal_modes = new byte[] {0};
+
+        synchronized (this) {
+            /* The following is just a nicer error, we would catch it anyway later in the channel code */
+            if (flag_closed)
+                throw new IOException("This session is closed.");
+
+            if (flag_pty_requested)
+                throw new IOException("A PTY was already requested.");
+
+            if (flag_execution_started)
+                throw new IOException(
+                    "Cannot request PTY at this stage anymore, a remote execution has already started.");
+
+            flag_pty_requested = true;
+        }
+
+        cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels,
+                      terminal_modes);
+    }
+
+    /**
+     * Tells the server that the size of the terminal has changed.
+     *
+     * See {@link #requestPTY(String, int, int, int, int, byte[])} for more details about how parameters are interpreted.
+     *
+     * @param term_width_characters
+     *            terminal width, characters (e.g., 80)
+     * @param term_height_characters
+     *            terminal height, rows (e.g., 24)
+     * @param term_width_pixels
+     *            terminal width, pixels (e.g., 640)
+     * @param term_height_pixels
+     *            terminal height, pixels (e.g., 480)
+     * @throws IOException
+     */
+    public void resizePTY(int term_width_characters, int term_height_characters, int term_width_pixels, int term_height_pixels) throws IOException {
+        requestWindowChange(term_width_characters, term_height_characters, term_width_pixels, term_height_pixels);
+    }
+
+    public void requestWindowChange(int term_width_characters, int term_height_characters, int term_width_pixels,
+                                    int term_height_pixels) throws IOException {
+        synchronized (this) {
+            /* The following is just a nicer error, we would catch it anyway later in the channel code */
+            if (flag_closed)
+                throw new IOException("This session is closed.");
+
+            if (!flag_pty_requested)
+                throw new IOException("A PTY was not requested.");
+        }
+
+        cm.requestWindowChange(cn, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels);
+    }
+
+    /**
+     * Request X11 forwarding for the current session.
+     * <p/>
+     * You have to supply the name and port of your X-server.
+     * <p/>
+     * This method may only be called before a program or shell is started in
+     * this session.
+     *
+     * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1)
+     * @param port the port of the real (target) X11 server (e.g., 6010)
+     * @param cookie if non-null, then present this cookie to the real X11 server
+     * @param singleConnection if true, then the server is instructed to only forward one single
+     * connection, no more connections shall be forwarded after first, or after the session
+     * channel has been closed
+     * @throws IOException
+     */
+    public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection)
+    throws IOException {
+        if (hostname == null)
+            throw new IllegalArgumentException("hostname argument may not be null");
+
+        synchronized (this) {
+            /* The following is just a nicer error, we would catch it anyway later in the channel code */
+            if (flag_closed)
+                throw new IOException("This session is closed.");
+
+            if (flag_x11_requested)
+                throw new IOException("X11 forwarding was already requested.");
+
+            if (flag_execution_started)
+                throw new IOException(
+                    "Cannot request X11 forwarding at this stage anymore, a remote execution has already started.");
+
+            flag_x11_requested = true;
+        }
+
+        /* X11ServerData - used to store data about the target X11 server */
+        X11ServerData x11data = new X11ServerData();
+        x11data.hostname = hostname;
+        x11data.port = port;
+        x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */
+        /* Generate fake cookie - this one is used between remote clients and the ganymed proxy */
+        byte[] fakeCookie = new byte[16];
+        String hexEncodedFakeCookie;
+
+        /* Make sure that this fake cookie is unique for this connection */
+
+        while (true) {
+            rnd.nextBytes(fakeCookie);
+            /* Generate also hex representation of fake cookie */
+            StringBuilder tmp = new StringBuilder(32);
+
+            for (int i = 0; i < fakeCookie.length; i++) {
+                String digit2 = Integer.toHexString(fakeCookie[i] & 0xff);
+                tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
+            }
+
+            hexEncodedFakeCookie = tmp.toString();
+
+            /* Well, yes, chances are low, but we want to be on the safe side */
+
+            if (cm.checkX11Cookie(hexEncodedFakeCookie) == null)
+                break;
+        }
+
+        /* Ask for X11 forwarding */
+        cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0);
+
+        /* OK, that went fine, get ready to accept X11 connections... */
+        /* ... but only if the user has not called close() in the meantime =) */
+
+        synchronized (this) {
+            if (flag_closed == false) {
+                this.x11FakeCookie = hexEncodedFakeCookie;
+                cm.registerX11Cookie(hexEncodedFakeCookie, x11data);
+            }
+        }
+
+        /* Now it is safe to start remote X11 programs */
+    }
+
+    /**
+     * Execute a command on the remote machine.
+     *
+     * @param cmd The command to execute on the remote host.
+     * @throws IOException
+     */
+    public void execCommand(String cmd) throws IOException {
+        this.execCommand(cmd, null);
+    }
+
+    /**
+     * Execute a command on the remote machine.
+     *
+     * @param cmd The command to execute on the remote host.
+     * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
+     * @throws IOException
+     */
+    public void execCommand(String cmd, String charsetName) throws IOException {
+        if (cmd == null)
+            throw new IllegalArgumentException("cmd argument may not be null");
+
+        synchronized (this) {
+            /* The following is just a nicer error, we would catch it anyway later in the channel code */
+            if (flag_closed)
+                throw new IOException("This session is closed.");
+
+            if (flag_execution_started)
+                throw new IOException("A remote execution has already started.");
+
+            flag_execution_started = true;
+        }
+
+        cm.requestExecCommand(cn, cmd, charsetName);
+    }
+
+    /**
+     * Start a shell on the remote machine.
+     *
+     * @throws IOException
+     */
+    public void startShell() throws IOException {
+        synchronized (this) {
+            /* The following is just a nicer error, we would catch it anyway later in the channel code */
+            if (flag_closed)
+                throw new IOException("This session is closed.");
+
+            if (flag_execution_started)
+                throw new IOException("A remote execution has already started.");
+
+            flag_execution_started = true;
+        }
+
+        cm.requestShell(cn);
+    }
+
+    /**
+     * Start a subsystem on the remote machine.
+     * Unless you know what you are doing, you will never need this.
+     *
+     * @param name the name of the subsystem.
+     * @throws IOException
+     */
+    public void startSubSystem(String name) throws IOException {
+        if (name == null)
+            throw new IllegalArgumentException("name argument may not be null");
+
+        synchronized (this) {
+            /* The following is just a nicer error, we would catch it anyway later in the channel code */
+            if (flag_closed)
+                throw new IOException("This session is closed.");
+
+            if (flag_execution_started)
+                throw new IOException("A remote execution has already started.");
+
+            flag_execution_started = true;
+        }
+
+        cm.requestSubSystem(cn, name);
+    }
+
+    /**
+     * Request authentication agent forwarding.
+     * @param agent object that implements the callbacks
+     *
+     * @throws IOException in case of any problem or when the session is closed
+     */
+
+    public synchronized void requestAuthAgentForwarding(AuthAgentCallback agent) throws IOException {
+        synchronized (this) {
+            /*
+             * The following is just a nicer error, we would catch it anyway
+             * later in the channel code
+             */
+            if (flag_closed)
+                throw new IOException("This session is closed.");
+        }
+
+        cm.requestChannelAgentForwarding(cn, agent);
+    }
+
+    public int getState() {
+        return cn.getState();
+    }
+
+    public InputStream getStdout() {
+        return cn.getStdoutStream();
+    }
+
+    public InputStream getStderr() {
+        return cn.getStderrStream();
+    }
+
+    public OutputStream getStdin() {
+        return cn.getStdinStream();
+    }
+
+    /**
+     * This method blocks until there is more data available on either the
+     * stdout or stderr InputStream of this <code>Session</code>. Very useful
+     * if you do not want to use two parallel threads for reading from the two
+     * InputStreams. One can also specify a timeout. NOTE: do NOT call this
+     * method if you use concurrent threads that operate on either of the two
+     * InputStreams of this <code>Session</code> (otherwise this method may
+     * block, even though more data is available).
+     *
+     * @param timeout The (non-negative) timeout in <code>ms</code>. <code>0</code> means no
+     * timeout, the call may block forever.
+     * @return <ul>
+     *         <li><code>0</code> if no more data will arrive.</li>
+     *         <li><code>1</code> if more data is available.</li>
+     *         <li><code>-1</code> if a timeout occurred.</li>
+     *         </ul>
+     * @throws IOException
+     * @deprecated This method has been replaced with a much more powerful wait-for-condition
+     *             interface and therefore acts only as a wrapper.
+     */
+    public int waitUntilDataAvailable(long timeout) throws IOException {
+        if (timeout < 0)
+            throw new IllegalArgumentException("timeout must not be negative!");
+
+        int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA
+                                             | ChannelCondition.EOF);
+
+        if ((conditions & ChannelCondition.TIMEOUT) != 0)
+            return -1;
+
+        if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0)
+            return 1;
+
+        /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */
+
+        if ((conditions & ChannelCondition.EOF) != 0)
+            return 0;
+
+        throw new IllegalStateException("Unexpected condition result (" + conditions + ")");
+    }
+
+    /**
+     * This method blocks until certain conditions hold true on the underlying SSH-2 channel.
+     * <p/>
+     * This method returns as soon as one of the following happens:
+     * <ul>
+     * <li>at least of the specified conditions (see {@link ChannelCondition}) holds true</li>
+     * <li>timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions)</a>
+     * <li>the underlying channel was closed (CLOSED will be set in result conditions)</a>
+     * </ul>
+     * <p/>
+     * In any case, the result value contains ALL current conditions, which may be more
+     * than the specified condition set (i.e., never use the "==" operator to test for conditions
+     * in the bitmask, see also comments in {@link ChannelCondition}).
+     * <p/>
+     * Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and
+     * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two
+     * InputStreams of this <code>Session</code> (otherwise this method may
+     * block, even though more data is available in the StreamGobblers).
+     *
+     * @param condition_set a bitmask based on {@link ChannelCondition} values
+     * @param timeout non-negative timeout in ms, <code>0</code> means no timeout
+     * @return all bitmask specifying all current conditions that are true
+     */
+
+    public int waitForCondition(int condition_set, long timeout) throws IOException {
+        if (timeout < 0)
+            throw new IllegalArgumentException("timeout must be non-negative!");
+
+        return cm.waitForCondition(cn, timeout, condition_set);
+    }
+
+    /**
+     * Get the exit code/status from the remote command - if available. Be
+     * careful - not all server implementations return this value. It is
+     * generally a good idea to call this method only when all data from the
+     * remote side has been consumed (see also the <code<WaitForCondition</code> method).
+     *
+     * @return An <code>Integer</code> holding the exit code, or
+     *         <code>null</code> if no exit code is (yet) available.
+     */
+    public Integer getExitStatus() {
+        return cn.getExitStatus();
+    }
+
+    /**
+     * Get the name of the signal by which the process on the remote side was
+     * stopped - if available and applicable. Be careful - not all server
+     * implementations return this value.
+     *
+     * @return An <code>String</code> holding the name of the signal, or
+     *         <code>null</code> if the process exited normally or is still
+     *         running (or if the server forgot to send this information).
+     */
+    public String getExitSignal() {
+        return cn.getExitSignal();
+    }
+
+    /**
+     * Close this session. NEVER forget to call this method to free up resources -
+     * even if you got an exception from one of the other methods (or when
+     * getting an Exception on the Input- or OutputStreams). Sometimes these other
+     * methods may throw an exception, saying that the underlying channel is
+     * closed (this can happen, e.g., if the other server sent a close message.)
+     * However, as long as you have not called the <code>close()</code>
+     * method, you may be wasting (local) resources.
+     */
+    public void close() {
+        synchronized (this) {
+            if (flag_closed)
+                return;
+
+            flag_closed = true;
+
+            if (x11FakeCookie != null)
+                cm.unRegisterX11Cookie(x11FakeCookie, true);
+
+            try {
+                cm.closeChannel(cn, "Closed due to user request", true);
+            }
+            catch (IOException ignored) {
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SimpleServerSessionCallback.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+/**
+ * A basic ServerSessionCallback implementation.
+ * <p>
+ * Note: you should derive from this class instead of implementing
+ * the {@link ServerSessionCallback} interface directly. This way
+ * your code works also in case the interface gets extended in future
+ * versions.
+ *
+ * @author Christian
+ *
+ */
+public class SimpleServerSessionCallback implements ServerSessionCallback {
+    public Runnable requestShell(ServerSession ss) throws IOException {
+        return null;
+    }
+
+    public Runnable requestExec(ServerSession ss, String command) throws IOException {
+        return null;
+    }
+
+    public Runnable requestSubsystem(ServerSession ss, String subsystem) throws IOException {
+        return null;
+    }
+
+    public Runnable requestPtyReq(ServerSession ss, PtySettings pty) throws IOException {
+        return null;
+    }
+
+    /**
+     * By default, silently ignore passwd environment variables.
+     */
+    public Runnable requestEnv(ServerSession ss, String name, String value) throws IOException {
+        return new Runnable() {
+            public void run() {
+                /* Do nothing */
+            }
+        };
+    }
+
+    public void requestWindowChange(ServerSession ss, int term_width_columns, int term_height_rows,
+                                    int term_width_pixels, int term_height_pixels) throws IOException {
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/StreamGobbler.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+
+/**
+ * A <code>StreamGobbler</code> is an InputStream that uses an internal worker
+ * thread to constantly consume input from another InputStream. It uses a buffer
+ * to store the consumed data. The buffer size is automatically adjusted, if needed.
+ * <p/>
+ * This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR
+ * InputStreams with instances of this class, then you don't have to bother about
+ * the shared window of STDOUT and STDERR in the low level SSH-2 protocol,
+ * since all arriving data will be immediatelly consumed by the worker threads.
+ * Also, as a side effect, the streams will be buffered (e.g., single byte
+ * read() operations are faster).
+ * <p/>
+ * Other SSH for Java libraries include this functionality by default in
+ * their STDOUT and STDERR InputStream implementations, however, please be aware
+ * that this approach has also a downside:
+ * <p/>
+ * If you do not call the StreamGobbler's <code>read()</code> method often enough
+ * and the peer is constantly sending huge amounts of data, then you will sooner or later
+ * encounter a low memory situation due to the aggregated data (well, it also depends on the Java heap size).
+ * Joe Average will like this class anyway - a paranoid programmer would never use such an approach.
+ * <p/>
+ * The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't",
+ * see http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class StreamGobbler extends InputStream {
+    class GobblerThread extends Thread {
+        @Override
+        public void run() {
+            byte[] buff = new byte[8192];
+
+            while (true) {
+                try {
+                    int avail = is.read(buff);
+
+                    synchronized (synchronizer) {
+                        if (avail <= 0) {
+                            isEOF = true;
+                            synchronizer.notifyAll();
+                            break;
+                        }
+
+                        int space_available = buffer.length - write_pos;
+
+                        if (space_available < avail) {
+                            /* compact/resize buffer */
+                            int unread_size = write_pos - read_pos;
+                            int need_space = unread_size + avail;
+                            byte[] new_buffer = buffer;
+
+                            if (need_space > buffer.length) {
+                                int inc = need_space / 3;
+                                inc = (inc < 256) ? 256 : inc;
+                                inc = (inc > 8192) ? 8192 : inc;
+                                new_buffer = new byte[need_space + inc];
+                            }
+
+                            if (unread_size > 0)
+                                System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size);
+
+                            buffer = new_buffer;
+                            read_pos = 0;
+                            write_pos = unread_size;
+                        }
+
+                        System.arraycopy(buff, 0, buffer, write_pos, avail);
+                        write_pos += avail;
+                        synchronizer.notifyAll();
+                    }
+                }
+                catch (IOException e) {
+                    synchronized (synchronizer) {
+                        exception = e;
+                        synchronizer.notifyAll();
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    private InputStream is;
+
+    private final Object synchronizer = new Object();
+
+    private boolean isEOF = false;
+    private boolean isClosed = false;
+    private IOException exception = null;
+
+    private byte[] buffer = new byte[2048];
+    private int read_pos = 0;
+    private int write_pos = 0;
+
+    public StreamGobbler(InputStream is) {
+        this.is = is;
+        GobblerThread t = new GobblerThread();
+        t.setDaemon(true);
+        t.start();
+    }
+
+    @Override
+    public int read() throws IOException {
+        synchronized (synchronizer) {
+            if (isClosed)
+                throw new IOException("This StreamGobbler is closed.");
+
+            while (read_pos == write_pos) {
+                if (exception != null)
+                    throw exception;
+
+                if (isEOF)
+                    return -1;
+
+                try {
+                    synchronizer.wait();
+                }
+                catch (InterruptedException e) {
+                    throw new InterruptedIOException();
+                }
+            }
+
+            return buffer[read_pos++] & 0xff;
+        }
+    }
+
+    @Override
+    public int available() throws IOException {
+        synchronized (synchronizer) {
+            if (isClosed)
+                throw new IOException("This StreamGobbler is closed.");
+
+            return write_pos - read_pos;
+        }
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public void close() throws IOException {
+        synchronized (synchronizer) {
+            if (isClosed)
+                return;
+
+            isClosed = true;
+            isEOF = true;
+            synchronizer.notifyAll();
+            is.close();
+        }
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (b == null)
+            throw new NullPointerException();
+
+        if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
+            throw new IndexOutOfBoundsException();
+
+        if (len == 0)
+            return 0;
+
+        synchronized (synchronizer) {
+            if (isClosed)
+                throw new IOException("This StreamGobbler is closed.");
+
+            while (read_pos == write_pos) {
+                if (exception != null)
+                    throw exception;
+
+                if (isEOF)
+                    return -1;
+
+                try {
+                    synchronizer.wait();
+                }
+                catch (InterruptedException e) {
+                    throw new InterruptedIOException();
+                }
+            }
+
+            int avail = write_pos - read_pos;
+            avail = (avail > len) ? len : avail;
+            System.arraycopy(buffer, read_pos, b, off, avail);
+            read_pos += avail;
+            return avail;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/Version.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,26 @@
+package ch.ethz.ssh2;
+
+/**
+ * Provides version information from the manifest.
+ *
+ * @version $Id: Version.java 140 2014-04-15 13:16:25Z dkocher@sudo.ch $
+ */
+public class Version {
+    public static String getSpecification() {
+        Package pkg = Version.class.getPackage();
+        return (pkg == null) ? "SNAPSHOT" : pkg.getSpecificationVersion() == null ? "SNAPSHOT" : pkg.getSpecificationVersion();
+    }
+
+    public static String getImplementation() {
+        Package pkg = Version.class.getPackage();
+        return (pkg == null) ? "SNAPSHOT" : pkg.getImplementationVersion() == null ? "SNAPSHOT" : pkg.getImplementationVersion();
+    }
+
+    /**
+     * A simple main method that prints the version and exits
+     */
+    public static void main(String[] args) {
+        System.out.println("Version: " + getSpecification());
+        System.out.println("Implementation: " + getImplementation());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/auth/AgentIdentity.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.auth;
+
+public interface AgentIdentity {
+    String getAlgName();
+    byte[] getPublicKeyBlob();
+    byte[] sign(byte[] data);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/auth/AgentProxy.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.auth;
+
+import java.util.Collection;
+
+public interface AgentProxy {
+    public Collection<AgentIdentity> getIdentities();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/auth/AuthenticationManager.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.auth;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+import ch.ethz.ssh2.InteractiveCallback;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.crypto.PEMDecoder;
+import ch.ethz.ssh2.packets.PacketServiceAccept;
+import ch.ethz.ssh2.packets.PacketServiceRequest;
+import ch.ethz.ssh2.packets.PacketUserauthBanner;
+import ch.ethz.ssh2.packets.PacketUserauthFailure;
+import ch.ethz.ssh2.packets.PacketUserauthInfoRequest;
+import ch.ethz.ssh2.packets.PacketUserauthInfoResponse;
+import ch.ethz.ssh2.packets.PacketUserauthRequestInteractive;
+import ch.ethz.ssh2.packets.PacketUserauthRequestNone;
+import ch.ethz.ssh2.packets.PacketUserauthRequestPassword;
+import ch.ethz.ssh2.packets.PacketUserauthRequestPublicKey;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.packets.TypesWriter;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.transport.ClientTransportManager;
+import ch.ethz.ssh2.transport.MessageHandler;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: AuthenticationManager.java 161 2014-05-01 18:01:55Z dkocher@sudo.ch $
+ */
+public class AuthenticationManager implements MessageHandler {
+    private ClientTransportManager tm;
+
+    private final BlockingQueue<byte[]> packets
+        = new ArrayBlockingQueue<byte[]>(5);
+
+    private boolean connectionClosed = false;
+
+    private String banner;
+
+    private Set<String> remainingMethods
+        = new HashSet<String>();
+
+    private boolean isPartialSuccess = false;
+
+    private boolean authenticated = false;
+    private boolean initDone = false;
+
+    public AuthenticationManager(ClientTransportManager tm) {
+        this.tm = tm;
+    }
+
+    private byte[] deQueue() throws IOException {
+        if (connectionClosed) {
+            throw(IOException) new IOException("The connection is closed.").initCause(tm.getReasonClosedCause());
+        }
+
+        // Wait for packet
+        try {
+            return packets.take();
+        }
+        catch (InterruptedException e) {
+            throw new InterruptedIOException(e.getMessage());
+        }
+    }
+
+    byte[] getNextMessage() throws IOException {
+        while (true) {
+            byte[] message = deQueue();
+
+            switch (message[0]) {
+                case Packets.SSH_MSG_USERAUTH_BANNER:
+                    // The server may send an SSH_MSG_USERAUTH_BANNER message at any
+                    // time after this authentication protocol starts and before
+                    // authentication is successful.
+                    PacketUserauthBanner sb = new PacketUserauthBanner(message);
+                    banner = sb.getBanner();
+                    break;
+
+                default:
+                    return message;
+            }
+        }
+    }
+
+    public Set<String> getRemainingMethods(String user) throws IOException {
+        initialize(user);
+        return remainingMethods;
+    }
+
+    public String getBanner() {
+        return banner;
+    }
+
+    public boolean getPartialSuccess() {
+        return isPartialSuccess;
+    }
+
+    private boolean initialize(String user) throws IOException {
+        if (initDone == false) {
+            tm.registerMessageHandler(this, 0, 255);
+            PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
+            tm.sendMessage(sr.getPayload());
+            final PacketServiceAccept accept = new PacketServiceAccept(this.getNextMessage());
+            PacketUserauthRequestNone auth = new PacketUserauthRequestNone("ssh-connection", user);
+            tm.sendMessage(auth.getPayload());
+            byte[] message = this.getNextMessage();
+            initDone = true;
+
+            switch (message[0]) {
+                case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                    authenticated = true;
+                    tm.removeMessageHandler(this);
+                    return true;
+
+                case Packets.SSH_MSG_USERAUTH_FAILURE:
+                    PacketUserauthFailure puf = new PacketUserauthFailure(message);
+                    remainingMethods = puf.getAuthThatCanContinue();
+                    isPartialSuccess = puf.isPartialSuccess();
+                    return false;
+            }
+
+            throw new PacketTypeException(message[0]);
+        }
+
+        return authenticated;
+    }
+
+    public boolean authenticatePublicKey(String user, AgentProxy proxy) throws IOException {
+        initialize(user);
+        boolean success;
+
+        for (AgentIdentity identity : proxy.getIdentities()) {
+            success = authenticatePublicKey(user, identity);
+
+            if (success) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean authenticatePublicKey(String user, AgentIdentity identity) throws IOException {
+        if (!remainingMethods.contains("publickey")) {
+            throw new IOException("Authentication method not supported");
+        }
+
+        byte[] pubKeyBlob = identity.getPublicKeyBlob();
+
+        if (pubKeyBlob == null) {
+            return false;
+        }
+
+        TypesWriter tw = new TypesWriter();
+        byte[] H = tm.getSessionIdentifier();
+        tw.writeString(H, 0, H.length);
+        tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+        tw.writeString(user);
+        tw.writeString("ssh-connection");
+        tw.writeString("publickey");
+        tw.writeBoolean(true);
+        tw.writeString(identity.getAlgName());
+        tw.writeString(pubKeyBlob, 0, pubKeyBlob.length);
+        byte[] msg = tw.getBytes();
+        byte[] response = identity.sign(msg);
+        PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey(
+            "ssh-connection", user, identity.getAlgName(), pubKeyBlob, response);
+        tm.sendMessage(ua.getPayload());
+        byte[] message = getNextMessage();
+        final int type = message[0];
+
+        switch (type) {
+            case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                authenticated = true;
+                tm.removeMessageHandler(this);
+                return true;
+
+            case Packets.SSH_MSG_USERAUTH_FAILURE:
+                PacketUserauthFailure puf = new PacketUserauthFailure(message);
+                remainingMethods = puf.getAuthThatCanContinue();
+                isPartialSuccess = puf.isPartialSuccess();
+                return false;
+        }
+
+        throw new PacketTypeException(type);
+    }
+
+    public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
+    throws IOException {
+        KeyPair pair = PEMDecoder.decode(PEMPrivateKey, password);
+        return authenticatePublicKey(user, pair, rnd);
+    }
+
+    public boolean authenticatePublicKey(String user, KeyPair pair, SecureRandom rnd)
+    throws IOException {
+        PrivateKey key = pair.getPrivate();
+
+        try {
+            initialize(user);
+
+            if (!remainingMethods.contains("publickey")) {
+                throw new IOException("Authentication method publickey not supported by the server at this stage.");
+            }
+
+            if (key instanceof DSAPrivateKey) {
+                DSAPrivateKey pk = (DSAPrivateKey) key;
+                byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey) pair.getPublic());
+                TypesWriter tw = new TypesWriter();
+                byte[] H = tm.getSessionIdentifier();
+                tw.writeString(H, 0, H.length);
+                tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+                tw.writeString(user);
+                tw.writeString("ssh-connection");
+                tw.writeString("publickey");
+                tw.writeBoolean(true);
+                tw.writeString("ssh-dss");
+                tw.writeString(pk_enc, 0, pk_enc.length);
+                byte[] msg = tw.getBytes();
+                byte[] ds = DSASHA1Verify.generateSignature(msg, pk, rnd);
+                byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);
+                PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+                        "ssh-dss", pk_enc, ds_enc);
+                tm.sendMessage(ua.getPayload());
+            }
+            else if (key instanceof RSAPrivateKey) {
+                RSAPrivateKey pk = (RSAPrivateKey) key;
+                byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey((RSAPublicKey) pair.getPublic());
+                TypesWriter tw = new TypesWriter();
+                {
+                    byte[] H = tm.getSessionIdentifier();
+                    tw.writeString(H, 0, H.length);
+                    tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+                    tw.writeString(user);
+                    tw.writeString("ssh-connection");
+                    tw.writeString("publickey");
+                    tw.writeBoolean(true);
+                    tw.writeString("ssh-rsa");
+                    tw.writeString(pk_enc, 0, pk_enc.length);
+                }
+                byte[] msg = tw.getBytes();
+                byte[] ds = RSASHA1Verify.generateSignature(msg, pk);
+                byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
+                PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+                        "ssh-rsa", pk_enc, rsa_sig_enc);
+                tm.sendMessage(ua.getPayload());
+            }
+            else if (key instanceof ECPrivateKey) {
+                ECPrivateKey pk = (ECPrivateKey) key;
+                final String algo = ECDSASHA2Verify.ECDSA_SHA2_PREFIX
+                                    + ECDSASHA2Verify.getCurveName(pk.getParams());
+                byte[] pk_enc = ECDSASHA2Verify.encodeSSHECDSAPublicKey((ECPublicKey) pair.getPublic());
+                TypesWriter tw = new TypesWriter();
+                {
+                    byte[] H = tm.getSessionIdentifier();
+                    tw.writeString(H, 0, H.length);
+                    tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+                    tw.writeString(user);
+                    tw.writeString("ssh-connection");
+                    tw.writeString("publickey");
+                    tw.writeBoolean(true);
+                    tw.writeString(algo);
+                    tw.writeString(pk_enc, 0, pk_enc.length);
+                }
+                byte[] msg = tw.getBytes();
+                byte[] ds = ECDSASHA2Verify.generateSignature(msg, pk);
+                byte[] ec_sig_enc = ECDSASHA2Verify.encodeSSHECDSASignature(ds, pk.getParams());
+                PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+                        algo, pk_enc, ec_sig_enc);
+                tm.sendMessage(ua.getPayload());
+            }
+            else {
+                throw new IOException("Unknown private key type returned by the PEM decoder.");
+            }
+
+            byte[] message = getNextMessage();
+            final int type = message[0];
+
+            switch (type) {
+                case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                    authenticated = true;
+                    tm.removeMessageHandler(this);
+                    return true;
+
+                case Packets.SSH_MSG_USERAUTH_FAILURE:
+                    PacketUserauthFailure puf = new PacketUserauthFailure(message);
+                    remainingMethods = puf.getAuthThatCanContinue();
+                    isPartialSuccess = puf.isPartialSuccess();
+                    return false;
+            }
+
+            throw new PacketTypeException(type);
+        }
+        catch (IOException e) {
+            tm.close(e, false);
+            throw e;
+        }
+    }
+
+    public boolean authenticateNone(String user) throws IOException {
+        try {
+            initialize(user);
+            return authenticated;
+        }
+        catch (IOException e) {
+            tm.close(e, false);
+            throw e;
+        }
+    }
+
+    public boolean authenticatePassword(String user, String pass) throws IOException {
+        try {
+            initialize(user);
+
+            if (!remainingMethods.contains("password")) {
+                throw new IOException("Authentication method not supported");
+            }
+
+            PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
+            tm.sendMessage(ua.getPayload());
+            byte[] message = getNextMessage();
+            final int type = message[0];
+
+            switch (type) {
+                case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                    authenticated = true;
+                    tm.removeMessageHandler(this);
+                    return true;
+
+                case Packets.SSH_MSG_USERAUTH_FAILURE:
+                    PacketUserauthFailure puf = new PacketUserauthFailure(message);
+                    remainingMethods = puf.getAuthThatCanContinue();
+                    isPartialSuccess = puf.isPartialSuccess();
+                    return false;
+            }
+
+            throw new PacketTypeException(type);
+        }
+        catch (IOException e) {
+            tm.close(e, false);
+            throw e;
+        }
+    }
+
+    public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException {
+        try {
+            initialize(user);
+
+            if (!remainingMethods.contains("keyboard-interactive")) {
+                throw new IOException(
+                    "Authentication method keyboard-interactive not supported by the server at this stage.");
+            }
+
+            PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
+                    submethods);
+            tm.sendMessage(ua.getPayload());
+
+            while (true) {
+                byte[] message = getNextMessage();
+                final int type = message[0];
+
+                switch (type) {
+                    case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                        authenticated = true;
+                        tm.removeMessageHandler(this);
+                        return true;
+
+                    case Packets.SSH_MSG_USERAUTH_FAILURE:
+                        PacketUserauthFailure puf = new PacketUserauthFailure(message);
+                        remainingMethods = puf.getAuthThatCanContinue();
+                        isPartialSuccess = puf.isPartialSuccess();
+                        return false;
+
+                    case Packets.SSH_MSG_USERAUTH_INFO_REQUEST:
+                        PacketUserauthInfoRequest info = new PacketUserauthInfoRequest(message);
+                        String[] responses;
+
+                        try {
+                            responses = cb.replyToChallenge(info.getName(), info.getInstruction(), info.getNumPrompts(),
+                                                            info.getPrompt(), info.getEcho());
+                        }
+                        catch (Exception e) {
+                            throw new IOException("Exception in callback.", e);
+                        }
+
+                        PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
+                        tm.sendMessage(puir.getPayload());
+                        continue;
+                }
+
+                throw new PacketTypeException(type);
+            }
+        }
+        catch (IOException e) {
+            tm.close(e, false);
+            throw e;
+        }
+    }
+
+    public void handleFailure(final IOException failure) {
+        connectionClosed = true;
+    }
+
+    public void handleMessage(byte[] message) throws IOException {
+        packets.add(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/auth/ServerAuthenticationManager.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,128 @@
+
+package ch.ethz.ssh2.auth;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import ch.ethz.ssh2.AuthenticationResult;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.ServerAuthenticationCallback;
+import ch.ethz.ssh2.channel.ChannelManager;
+import ch.ethz.ssh2.packets.PacketServiceAccept;
+import ch.ethz.ssh2.packets.PacketServiceRequest;
+import ch.ethz.ssh2.packets.PacketUserauthBanner;
+import ch.ethz.ssh2.packets.PacketUserauthFailure;
+import ch.ethz.ssh2.packets.PacketUserauthSuccess;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.server.ServerConnectionState;
+import ch.ethz.ssh2.transport.MessageHandler;
+
+public class ServerAuthenticationManager implements MessageHandler {
+    private final ServerConnectionState state;
+
+    public ServerAuthenticationManager(ServerConnectionState state) {
+        this.state = state;
+        state.tm.registerMessageHandler(this, 0, 255);
+    }
+
+    private void sendresult(AuthenticationResult result) throws IOException {
+        if (AuthenticationResult.SUCCESS == result) {
+            PacketUserauthSuccess pus = new PacketUserauthSuccess();
+            state.tm.sendAsynchronousMessage(pus.getPayload());
+            state.tm.removeMessageHandler(this);
+            state.tm.registerMessageHandler(this, 50, 79);
+            state.cm = new ChannelManager(state);
+            state.flag_auth_completed = true;
+        }
+        else {
+            Set<String> remaining_methods = new HashSet<String>();
+
+            if (state.cb_auth != null) {
+                remaining_methods.addAll(Arrays.asList(
+                                             state.cb_auth.getRemainingAuthMethods(state.conn)));
+            }
+
+            PacketUserauthFailure puf = new PacketUserauthFailure(remaining_methods,
+                    AuthenticationResult.PARTIAL_SUCCESS == result);
+            state.tm.sendAsynchronousMessage(puf.getPayload());
+        }
+    }
+
+    public void handleFailure(final IOException failure) {
+        //
+    }
+
+    public void handleMessage(byte[] msg) throws IOException {
+        /* Ignore all authentication messages after successful auth */
+        if (state.flag_auth_completed) {
+            return;
+        }
+
+        if (!state.flag_auth_serviceRequested) {
+            /* Must be PacketServiceRequest */
+            PacketServiceRequest psr = new PacketServiceRequest(msg);
+
+            if (!"ssh-userauth".equals(psr.getServiceName())) {
+                throw new IOException("SSH protocol error, expected ssh-userauth service request");
+            }
+
+            PacketServiceAccept psa = new PacketServiceAccept("ssh-userauth");
+            state.tm.sendAsynchronousMessage(psa.getPayload());
+            String banner = state.cb_auth.initAuthentication(state.conn);
+
+            if (banner != null) {
+                PacketUserauthBanner pub = new PacketUserauthBanner(banner);
+                state.tm.sendAsynchronousMessage(pub.getPayload());
+            }
+
+            state.flag_auth_serviceRequested = true;
+            return;
+        }
+
+        ServerAuthenticationCallback cb = state.cb_auth;
+        TypesReader tr = new TypesReader(msg);
+        int packet_type = tr.readByte();
+
+        if (packet_type == Packets.SSH_MSG_USERAUTH_REQUEST) {
+            String username = tr.readString("UTF-8");
+            String service = tr.readString();
+            String method = tr.readString();
+
+            if (!"ssh-connection".equals(service)) {
+                sendresult(AuthenticationResult.FAILURE);
+                return;
+            }
+
+            if ("none".equals(method)) {
+                if (cb != null) {
+                    sendresult(cb.authenticateWithNone(state.conn, username));
+                    return;
+                }
+            }
+
+            if ("password".equals(method)) {
+                boolean flag_change_pass = tr.readBoolean();
+
+                if (flag_change_pass) {
+                    sendresult(AuthenticationResult.FAILURE);
+                    return;
+                }
+
+                String password = tr.readString("UTF-8");
+
+                if (cb != null) {
+                    sendresult(cb.authenticateWithPassword(state.conn, username, password));
+                    return;
+                }
+            }
+
+            sendresult(AuthenticationResult.FAILURE);
+            return;
+        }
+
+        throw new PacketTypeException(packet_type);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/AuthAgentForwardThread.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,546 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed 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 ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import ch.ethz.ssh2.AuthAgentCallback;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
+
+/**
+ * AuthAgentForwardThread.
+ *
+ * @author Kenny Root
+ * @version $Id$
+ */
+public class AuthAgentForwardThread extends Thread implements IChannelWorkerThread {
+    private static final byte[] SSH_AGENT_FAILURE = {0, 0, 0, 1, 5}; // 5
+    private static final byte[] SSH_AGENT_SUCCESS = {0, 0, 0, 1, 6}; // 6
+
+    private static final int SSH2_AGENTC_REQUEST_IDENTITIES = 11;
+    private static final int SSH2_AGENT_IDENTITIES_ANSWER = 12;
+
+    private static final int SSH2_AGENTC_SIGN_REQUEST = 13;
+    private static final int SSH2_AGENT_SIGN_RESPONSE = 14;
+
+    private static final int SSH2_AGENTC_ADD_IDENTITY = 17;
+    private static final int SSH2_AGENTC_REMOVE_IDENTITY = 18;
+    private static final int SSH2_AGENTC_REMOVE_ALL_IDENTITIES = 19;
+
+//  private static final int SSH_AGENTC_ADD_SMARTCARD_KEY = 20;
+//  private static final int SSH_AGENTC_REMOVE_SMARTCARD_KEY = 21;
+
+    private static final int SSH_AGENTC_LOCK = 22;
+    private static final int SSH_AGENTC_UNLOCK = 23;
+
+    private static final int SSH2_AGENTC_ADD_ID_CONSTRAINED = 25;
+//  private static final int SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED = 26;
+
+    // Constraints for adding keys
+    private static final int SSH_AGENT_CONSTRAIN_LIFETIME = 1;
+    private static final int SSH_AGENT_CONSTRAIN_CONFIRM = 2;
+
+    // Flags for signature requests
+//  private static final int SSH_AGENT_OLD_SIGNATURE = 1;
+
+    private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
+
+    AuthAgentCallback authAgent;
+    OutputStream os;
+    InputStream is;
+    Channel c;
+
+    byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
+
+    public AuthAgentForwardThread(Channel c, AuthAgentCallback authAgent) {
+        this.c = c;
+        this.authAgent = authAgent;
+        log.debug("AuthAgentForwardThread started");
+    }
+
+    @Override
+    public void run() {
+        try {
+            c.cm.registerThread(this);
+        }
+        catch (IOException e) {
+            stopWorking();
+            return;
+        }
+
+        try {
+            c.cm.sendOpenConfirmation(c);
+            is = c.getStdoutStream();
+            os = c.getStdinStream();
+            int totalSize = 4;
+            int readSoFar = 0;
+
+            while (true) {
+                int len;
+
+                try {
+                    len = is.read(buffer, readSoFar, buffer.length - readSoFar);
+                }
+                catch (IOException e) {
+                    stopWorking();
+                    return;
+                }
+
+                if (len <= 0)
+                    break;
+
+                readSoFar += len;
+
+                if (readSoFar >= 4) {
+                    TypesReader tr = new TypesReader(buffer, 0, 4);
+                    totalSize = tr.readUINT32() + 4;
+                }
+
+                if (totalSize == readSoFar) {
+                    TypesReader tr = new TypesReader(buffer, 4, readSoFar - 4);
+                    int messageType = tr.readByte();
+
+                    switch (messageType) {
+                        case SSH2_AGENTC_REQUEST_IDENTITIES:
+                            sendIdentities();
+                            break;
+
+                        case SSH2_AGENTC_ADD_IDENTITY:
+                            addIdentity(tr, false);
+                            break;
+
+                        case SSH2_AGENTC_ADD_ID_CONSTRAINED:
+                            addIdentity(tr, true);
+                            break;
+
+                        case SSH2_AGENTC_REMOVE_IDENTITY:
+                            removeIdentity(tr);
+                            break;
+
+                        case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
+                            removeAllIdentities(tr);
+                            break;
+
+                        case SSH2_AGENTC_SIGN_REQUEST:
+                            processSignRequest(tr);
+                            break;
+
+                        case SSH_AGENTC_LOCK:
+                            processLockRequest(tr);
+                            break;
+
+                        case SSH_AGENTC_UNLOCK:
+                            processUnlockRequest(tr);
+                            break;
+
+                        default:
+                            os.write(SSH_AGENT_FAILURE);
+                            break;
+                    }
+
+                    readSoFar = 0;
+                }
+            }
+
+            c.cm.closeChannel(c, "EOF on both streams reached.", true);
+        }
+        catch (IOException e) {
+            log.debug("IOException in agent forwarder: " + e.getMessage());
+
+            try {
+                is.close();
+            }
+            catch (IOException e1) {
+            }
+
+            try {
+                os.close();
+            }
+            catch (IOException e2) {
+            }
+
+            try {
+                c.cm.closeChannel(c, "IOException in agent forwarder (" + e.getMessage() + ")", true);
+            }
+            catch (IOException e3) {
+            }
+        }
+    }
+
+    public void stopWorking() {
+        try {
+            /* This will lead to an IOException in the is.read() call */
+            is.close();
+        }
+        catch (IOException e) {
+        }
+    }
+
+    /**
+     * @return whether the agent is locked
+     */
+    private boolean failWhenLocked() throws IOException {
+        if (authAgent.isAgentLocked()) {
+            os.write(SSH_AGENT_FAILURE);
+            return true;
+        }
+        else
+            return false;
+    }
+
+    private void sendIdentities() throws IOException {
+        Map<String, byte[]> keys = null;
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(SSH2_AGENT_IDENTITIES_ANSWER);
+        int numKeys = 0;
+
+        if (!authAgent.isAgentLocked())
+            keys = authAgent.retrieveIdentities();
+
+        if (keys != null)
+            numKeys = keys.size();
+
+        tw.writeUINT32(numKeys);
+
+        if (keys != null) {
+            for (Entry<String, byte[]> entry : keys.entrySet()) {
+                byte[] keyBytes = entry.getValue();
+                tw.writeString(keyBytes, 0, keyBytes.length);
+                tw.writeString(entry.getKey());
+            }
+        }
+
+        sendPacket(tw.getBytes());
+    }
+
+    /**
+     * @param tr
+     */
+    private void addIdentity(TypesReader tr, boolean checkConstraints) {
+        try {
+            if (failWhenLocked())
+                return;
+
+            String type = tr.readString();
+            String comment;
+            String keyType;
+            KeySpec pubSpec;
+            KeySpec privSpec;
+
+            if (type.equals("ssh-rsa")) {
+                keyType = "RSA";
+                BigInteger n = tr.readMPINT();
+                BigInteger e = tr.readMPINT();
+                BigInteger d = tr.readMPINT();
+                BigInteger iqmp = tr.readMPINT();
+                BigInteger p = tr.readMPINT();
+                BigInteger q = tr.readMPINT();
+                comment = tr.readString();
+                // Derive the extra values Java needs.
+                BigInteger dmp1 = d.mod(p.subtract(BigInteger.ONE));
+                BigInteger dmq1 = d.mod(q.subtract(BigInteger.ONE));
+                pubSpec = new RSAPublicKeySpec(n, e);
+                privSpec = new RSAPrivateCrtKeySpec(n, e, d, p, q, dmp1, dmq1, iqmp);
+            }
+            else if (type.equals("ssh-dss")) {
+                keyType = "DSA";
+                BigInteger p = tr.readMPINT();
+                BigInteger q = tr.readMPINT();
+                BigInteger g = tr.readMPINT();
+                BigInteger y = tr.readMPINT();
+                BigInteger x = tr.readMPINT();
+                comment = tr.readString();
+                pubSpec = new DSAPublicKeySpec(y, p, q, g);
+                privSpec = new DSAPrivateKeySpec(x, p, q, g);
+            }
+            else if (type.equals("ecdsa-sha2-nistp256")) {
+                keyType = "EC";
+                String curveName = tr.readString();
+                byte[] groupBytes = tr.readByteString();
+                BigInteger exponent = tr.readMPINT();
+                comment = tr.readString();
+
+                if (!"nistp256".equals(curveName)) {
+                    log.debug("Invalid curve name for ecdsa-sha2-nistp256: " + curveName);
+                    os.write(SSH_AGENT_FAILURE);
+                    return;
+                }
+
+                ECParameterSpec nistp256 = ECDSASHA2Verify.EllipticCurves.nistp256;
+                ECPoint group = ECDSASHA2Verify.decodeECPoint(groupBytes, nistp256.getCurve());
+
+                if (group == null) {
+                    log.debug("No groupfor ecdsa-sha2-nistp256: ");
+                    os.write(SSH_AGENT_FAILURE);
+                    return;
+                }
+
+                pubSpec = new ECPublicKeySpec(group, nistp256);
+                privSpec = new ECPrivateKeySpec(exponent, nistp256);
+            }
+            else {
+                log.debug("Unknown key type: " + type);
+                os.write(SSH_AGENT_FAILURE);
+                return;
+            }
+
+            PublicKey pubKey;
+            PrivateKey privKey;
+
+            try {
+                KeyFactory kf = KeyFactory.getInstance(keyType);
+                pubKey = kf.generatePublic(pubSpec);
+                privKey = kf.generatePrivate(privSpec);
+            }
+            catch (NoSuchAlgorithmException ex) {
+                // TODO: log error
+                os.write(SSH_AGENT_FAILURE);
+                return;
+            }
+            catch (InvalidKeySpecException ex) {
+                // TODO: log error
+                os.write(SSH_AGENT_FAILURE);
+                return;
+            }
+
+            KeyPair pair = new KeyPair(pubKey, privKey);
+            boolean confirmUse = false;
+            int lifetime = 0;
+
+            if (checkConstraints) {
+                while (tr.remain() > 0) {
+                    int constraint = tr.readByte();
+
+                    if (constraint == SSH_AGENT_CONSTRAIN_CONFIRM)
+                        confirmUse = true;
+                    else if (constraint == SSH_AGENT_CONSTRAIN_LIFETIME)
+                        lifetime = tr.readUINT32();
+                    else {
+                        // Unknown constraint. Bail.
+                        os.write(SSH_AGENT_FAILURE);
+                        return;
+                    }
+                }
+            }
+
+            if (authAgent.addIdentity(pair, comment, confirmUse, lifetime))
+                os.write(SSH_AGENT_SUCCESS);
+            else
+                os.write(SSH_AGENT_FAILURE);
+        }
+        catch (IOException e) {
+            try {
+                os.write(SSH_AGENT_FAILURE);
+            }
+            catch (IOException e1) {
+            }
+        }
+    }
+
+    /**
+     * @param tr
+     */
+    private void removeIdentity(TypesReader tr) {
+        try {
+            if (failWhenLocked())
+                return;
+
+            byte[] publicKey = tr.readByteString();
+
+            if (authAgent.removeIdentity(publicKey))
+                os.write(SSH_AGENT_SUCCESS);
+            else
+                os.write(SSH_AGENT_FAILURE);
+        }
+        catch (IOException e) {
+            try {
+                os.write(SSH_AGENT_FAILURE);
+            }
+            catch (IOException e1) {
+            }
+        }
+    }
+
+    /**
+     * @param tr
+     */
+    private void removeAllIdentities(TypesReader tr) {
+        try {
+            if (failWhenLocked())
+                return;
+
+            if (authAgent.removeAllIdentities())
+                os.write(SSH_AGENT_SUCCESS);
+            else
+                os.write(SSH_AGENT_FAILURE);
+        }
+        catch (IOException e) {
+            try {
+                os.write(SSH_AGENT_FAILURE);
+            }
+            catch (IOException e1) {
+            }
+        }
+    }
+
+    private void processSignRequest(TypesReader tr) {
+        try {
+            if (failWhenLocked())
+                return;
+
+            byte[] publicKeyBytes = tr.readByteString();
+            byte[] challenge = tr.readByteString();
+            int flags = tr.readUINT32();
+
+            if (flags != 0) {
+                // We don't understand any flags; abort!
+                os.write(SSH_AGENT_FAILURE);
+                return;
+            }
+
+            KeyPair pair = authAgent.getKeyPair(publicKeyBytes);
+
+            if (pair == null) {
+                os.write(SSH_AGENT_FAILURE);
+                return;
+            }
+
+            byte[] response;
+            PrivateKey privKey = pair.getPrivate();
+
+            if (privKey instanceof RSAPrivateKey) {
+                byte[] signature = RSASHA1Verify.generateSignature(challenge,
+                                   (RSAPrivateKey) privKey);
+                response = RSASHA1Verify.encodeSSHRSASignature(signature);
+            }
+            else if (privKey instanceof DSAPrivateKey) {
+                byte[] signature = DSASHA1Verify.generateSignature(challenge,
+                                   (DSAPrivateKey) privKey, new SecureRandom());
+                response = DSASHA1Verify.encodeSSHDSASignature(signature);
+            }
+            else if (privKey instanceof ECPrivateKey) {
+                ECPrivateKey pk = (ECPrivateKey) privKey;
+                byte[] signature = ECDSASHA2Verify.generateSignature(challenge, pk);
+                response = ECDSASHA2Verify.encodeSSHECDSASignature(signature, pk.getParams());
+            }
+            else {
+                os.write(SSH_AGENT_FAILURE);
+                return;
+            }
+
+            TypesWriter tw = new TypesWriter();
+            tw.writeByte(SSH2_AGENT_SIGN_RESPONSE);
+            tw.writeString(response, 0, response.length);
+            sendPacket(tw.getBytes());
+        }
+        catch (IOException e) {
+            try {
+                os.write(SSH_AGENT_FAILURE);
+            }
+            catch (IOException e1) {
+            }
+        }
+    }
+
+    /**
+     * @param tr
+     */
+    private void processLockRequest(TypesReader tr) {
+        try {
+            if (failWhenLocked())
+                return;
+
+            String lockPassphrase = tr.readString();
+
+            if (!authAgent.setAgentLock(lockPassphrase)) {
+                os.write(SSH_AGENT_FAILURE);
+                return;
+            }
+            else
+                os.write(SSH_AGENT_SUCCESS);
+        }
+        catch (IOException e) {
+            try {
+                os.write(SSH_AGENT_FAILURE);
+            }
+            catch (IOException e1) {
+            }
+        }
+    }
+
+    /**
+     * @param tr
+     */
+    private void processUnlockRequest(TypesReader tr) {
+        try {
+            String unlockPassphrase = tr.readString();
+
+            if (authAgent.requestAgentUnlock(unlockPassphrase))
+                os.write(SSH_AGENT_SUCCESS);
+            else
+                os.write(SSH_AGENT_FAILURE);
+        }
+        catch (IOException e) {
+            try {
+                os.write(SSH_AGENT_FAILURE);
+            }
+            catch (IOException e1) {
+            }
+        }
+    }
+
+    /**
+     * @param tw
+     * @throws IOException
+     */
+    private void sendPacket(byte[] message) throws IOException {
+        TypesWriter packet = new TypesWriter();
+        packet.writeUINT32(message.length);
+        packet.writeBytes(message);
+        os.write(packet.getBytes());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/Channel.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.transport.TransportManager;
+
+/**
+ * Channel.
+ *
+ * @author Christian Plattner
+ * @version $Id: Channel.java 123 2014-04-12 21:11:47Z dkocher@sudo.ch $
+ */
+public class Channel {
+    /*
+          * OK. Here is an important part of the JVM Specification:
+          * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
+          *
+          * Any association between locks and variables is purely conventional.
+          * Locking any lock conceptually flushes all variables from a thread's
+          * working memory, and unlocking any lock forces the writing out to main
+          * memory of all variables that the thread has assigned. That a lock may be
+          * associated with a particular object or a class is purely a convention.
+          * (...)
+          *
+          * If a thread uses a particular shared variable only after locking a
+          * particular lock and before the corresponding unlocking of that same lock,
+          * then the thread will read the shared value of that variable from main
+          * memory after the lock operation, if necessary, and will copy back to main
+          * memory the value most recently assigned to that variable before the
+          * unlock operation.
+          *
+          * This, in conjunction with the mutual exclusion rules for locks, suffices
+          * to guarantee that values are correctly transmitted from one thread to
+          * another through shared variables.
+          *
+          * ====> Always keep that in mind when modifying the Channel/ChannelManger
+          * code.
+          *
+          */
+
+    public static final int STATE_OPENING = 1;
+    public static final int STATE_OPEN = 2;
+    public static final int STATE_CLOSED = 4;
+
+    static final int CHANNEL_BUFFER_SIZE = 32 * 1024 * 3 * 2;
+
+    /*
+          * To achieve correctness, the following rules have to be respected when
+          * accessing this object:
+          */
+
+    // These fields can always be read
+    final ChannelManager cm;
+    final ChannelOutputStream stdinStream;
+    final ChannelInputStream stdoutStream;
+    final ChannelInputStream stderrStream;
+
+    // In case this channel belongs to a server-side session.
+    ServerSessionImpl ss;
+
+    // These two fields will only be written while the Channel is in state
+    // STATE_OPENING.
+    // The code makes sure that the two fields are written out when the state is
+    // changing to STATE_OPEN.
+    // Therefore, if you know that the Channel is in state STATE_OPEN, then you
+    // can read these two fields without synchronizing on the Channel. However, make
+    // sure that you get the latest values (e.g., flush caches by synchronizing on any
+    // object). However, to be on the safe side, you can lock the channel.
+
+    int localID = -1;
+    int remoteID = -1;
+
+    /*
+          * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
+          * msg.
+          *
+          * This is a little bit complicated, but we have to do it in that way, since
+          * we cannot keep a lock on the Channel during the send operation (this
+          * would block sometimes the receiver thread, and, in extreme cases, can
+          * lead to a deadlock on both sides of the connection (senders are blocked
+          * since the receive buffers on the other side are full, and receiver
+          * threads wait for the senders to finish). It all depends on the
+          * implementation on the other side. But we cannot make any assumptions, we
+          * have to assume the worst case. Confused? Just believe me.
+          */
+
+    /*
+          * If you send a message on a channel, then you have to aquire the
+          * "channelSendLock" and check the "closeMessageSent" flag (this variable
+          * may only be accessed while holding the "channelSendLock" !!!
+          *
+          * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
+          * above.
+          */
+
+    final Object channelSendLock = new Object();
+    boolean closeMessageSent = false;
+
+    /*
+          * Stop memory fragmentation by allocating this often used buffer.
+          * May only be used while holding the channelSendLock
+          */
+
+    final byte[] msgWindowAdjust = new byte[9];
+
+    // If you access (read or write) any of the following fields, then you have
+    // to synchronize on the channel.
+
+    int state = STATE_OPENING;
+
+    boolean closeMessageRecv = false;
+
+    /* This is a stupid implementation. At the moment we can only wait
+          * for one pending request per channel.
+          */
+    int successCounter = 0;
+    int failedCounter = 0;
+
+    int localWindow = 0; /* locally, we use a small window, < 2^31 */
+    long remoteWindow = 0; /* long for readable  2^32 - 1 window support */
+
+    int localMaxPacketSize = -1;
+    int remoteMaxPacketSize = -1;
+
+    final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
+    final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];
+
+    int stdoutReadpos = 0;
+    int stdoutWritepos = 0;
+    int stderrReadpos = 0;
+    int stderrWritepos = 0;
+
+    boolean EOF = false;
+
+    Integer exit_status;
+
+    String exit_signal;
+
+    // we keep the x11 cookie so that this channel can be closed when this
+    // specific x11 forwarding gets stopped
+
+    String hexX11FakeCookie;
+
+    // reasonClosed is special, since we sometimes need to access it
+    // while holding the channelSendLock.
+    // We protect it with a private short term lock.
+
+    private final Object reasonClosedLock = new Object();
+    private IOException reasonClosed = null;
+
+    public Channel(ChannelManager cm) {
+        this.cm = cm;
+        this.localWindow = CHANNEL_BUFFER_SIZE;
+        this.localMaxPacketSize = TransportManager.MAX_PACKET_SIZE;
+        this.stdinStream = new ChannelOutputStream(this);
+        this.stdoutStream = new ChannelInputStream(this, false);
+        this.stderrStream = new ChannelInputStream(this, true);
+    }
+
+    /* Methods to allow access from classes outside of this package */
+
+    public ChannelInputStream getStderrStream() {
+        return stderrStream;
+    }
+
+    public ChannelOutputStream getStdinStream() {
+        return stdinStream;
+    }
+
+    public ChannelInputStream getStdoutStream() {
+        return stdoutStream;
+    }
+
+    public String getExitSignal() {
+        synchronized (this) {
+            return exit_signal;
+        }
+    }
+
+    public Integer getExitStatus() {
+        synchronized (this) {
+            return exit_status;
+        }
+    }
+
+    public IOException getReasonClosed() {
+        synchronized (reasonClosedLock) {
+            return reasonClosed;
+        }
+    }
+
+    public void setReasonClosed(IOException e) {
+        synchronized (reasonClosedLock) {
+            this.reasonClosed = e;
+        }
+    }
+
+    public int getState() {
+        return this.state;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/ChannelClosedException.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2011 David Kocher. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+
+/**
+ * @version $Id: ChannelClosedException.java 3183 2007-07-30 19:22:34Z dkocher $
+ */
+public class ChannelClosedException extends IOException {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    public ChannelClosedException(String s) {
+        super(s);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/ChannelInputStream.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * ChannelInputStream.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class ChannelInputStream extends InputStream {
+    Channel c;
+
+    boolean isClosed = false;
+    boolean isEOF = false;
+    boolean extendedFlag = false;
+
+    ChannelInputStream(Channel c, boolean isExtended) {
+        this.c = c;
+        this.extendedFlag = isExtended;
+    }
+
+    @Override
+    public int available() throws IOException {
+        if (isEOF)
+            return 0;
+
+        int avail = c.cm.getAvailable(c, extendedFlag);
+        /* We must not return -1 on EOF */
+        return (avail > 0) ? avail : 0;
+    }
+
+    @Override
+    public void close() throws IOException {
+        isClosed = true;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (b == null)
+            throw new NullPointerException();
+
+        if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
+            throw new IndexOutOfBoundsException();
+
+        if (len == 0)
+            return 0;
+
+        if (isEOF)
+            return -1;
+
+        int ret = c.cm.getChannelData(c, extendedFlag, b, off, len);
+
+        if (ret == -1) {
+            isEOF = true;
+        }
+
+        return ret;
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public int read() throws IOException {
+        /* Yes, this stream is pure and unbuffered, a single byte read() is slow */
+        final byte b[] = new byte[1];
+        int ret = read(b, 0, 1);
+
+        if (ret != 1)
+            return -1;
+
+        return b[0] & 0xff;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/ChannelManager.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,1660 @@
+/*
+
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import ch.ethz.ssh2.AuthAgentCallback;
+import ch.ethz.ssh2.ChannelCondition;
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.PtySettings;
+import ch.ethz.ssh2.ServerConnectionCallback;
+import ch.ethz.ssh2.ServerSessionCallback;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.PacketChannelAuthAgentReq;
+import ch.ethz.ssh2.packets.PacketChannelFailure;
+import ch.ethz.ssh2.packets.PacketChannelOpenConfirmation;
+import ch.ethz.ssh2.packets.PacketChannelOpenFailure;
+import ch.ethz.ssh2.packets.PacketChannelSuccess;
+import ch.ethz.ssh2.packets.PacketGlobalCancelForwardRequest;
+import ch.ethz.ssh2.packets.PacketGlobalForwardRequest;
+import ch.ethz.ssh2.packets.PacketOpenDirectTCPIPChannel;
+import ch.ethz.ssh2.packets.PacketOpenSessionChannel;
+import ch.ethz.ssh2.packets.PacketSessionExecCommand;
+import ch.ethz.ssh2.packets.PacketSessionPtyRequest;
+import ch.ethz.ssh2.packets.PacketSessionStartShell;
+import ch.ethz.ssh2.packets.PacketSessionSubsystemRequest;
+import ch.ethz.ssh2.packets.PacketSessionX11Request;
+import ch.ethz.ssh2.packets.PacketWindowChange;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.server.ServerConnectionState;
+import ch.ethz.ssh2.transport.MessageHandler;
+import ch.ethz.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
+ * @version $Id: ChannelManager.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public class ChannelManager implements MessageHandler {
+    private static final Logger log = Logger.getLogger(ChannelManager.class);
+
+    private final ServerConnectionState server_state;
+    private final TransportManager tm;
+
+    private final Map<String, X11ServerData> x11_magic_cookies = new HashMap<String, X11ServerData>();
+
+    private final List<Channel> channels = new ArrayList<Channel>();
+    private int nextLocalChannel = 100;
+    private boolean shutdown = false;
+    private int globalSuccessCounter = 0;
+    private int globalFailedCounter = 0;
+
+    private final HashMap<Integer, RemoteForwardingData> remoteForwardings = new HashMap<Integer, RemoteForwardingData>();
+
+    private AuthAgentCallback authAgent;
+
+    private final List<IChannelWorkerThread> listenerThreads = new ArrayList<IChannelWorkerThread>();
+
+    private boolean listenerThreadsAllowed = true;
+
+    /**
+     * Constructor for client-mode.
+     *
+     * @param tm
+     */
+    public ChannelManager(TransportManager tm) {
+        this.server_state = null;
+        this.tm = tm;
+        tm.registerMessageHandler(this, 80, 100);
+    }
+
+    /**
+     * Constructor for server-mode.
+     *
+     * @param state
+     */
+    public ChannelManager(ServerConnectionState state) {
+        this.server_state = state;
+        this.tm = state.tm;
+        tm.registerMessageHandler(this, 80, 100);
+    }
+
+    private Channel getChannel(int id) {
+        synchronized (channels) {
+            for (Channel c : channels) {
+                if (c.localID == id) {
+                    return c;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private void removeChannel(int id) {
+        synchronized (channels) {
+            for (Channel c : channels) {
+                if (c.localID == id) {
+                    channels.remove(c);
+                    break;
+                }
+            }
+        }
+    }
+
+    private int addChannel(Channel c) {
+        synchronized (channels) {
+            channels.add(c);
+            return nextLocalChannel++;
+        }
+    }
+
+    private void waitUntilChannelOpen(Channel c) throws IOException {
+        synchronized (c) {
+            while (c.state == Channel.STATE_OPENING) {
+                try {
+                    c.wait();
+                }
+                catch (InterruptedException e) {
+                    throw new InterruptedIOException(e.getMessage());
+                }
+            }
+
+            if (c.state != Channel.STATE_OPEN) {
+                removeChannel(c.localID);
+                throw c.getReasonClosed();
+            }
+        }
+    }
+
+    private void waitForGlobalSuccessOrFailure() throws IOException {
+        synchronized (channels) {
+            while ((globalSuccessCounter == 0) && (globalFailedCounter == 0)) {
+                if (shutdown) {
+                    throw new IOException("The connection is being shutdown");
+                }
+
+                try {
+                    channels.wait();
+                }
+                catch (InterruptedException e) {
+                    throw new InterruptedIOException(e.getMessage());
+                }
+            }
+
+            if ((globalFailedCounter == 0) && (globalSuccessCounter == 1)) {
+                return;
+            }
+
+            if ((globalFailedCounter == 1) && (globalSuccessCounter == 0)) {
+                throw new IOException("The server denied the request (did you enable port forwarding?)");
+            }
+
+            throw new IOException("Illegal state. The server sent " + globalSuccessCounter
+                                  + " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages.");
+        }
+    }
+
+    private void waitForChannelSuccessOrFailure(Channel c) throws IOException {
+        synchronized (c) {
+            while ((c.successCounter == 0) && (c.failedCounter == 0)) {
+                if (c.state != Channel.STATE_OPEN) {
+                    throw c.getReasonClosed();
+                }
+
+                try {
+                    c.wait();
+                }
+                catch (InterruptedException ignore) {
+                    throw new InterruptedIOException();
+                }
+            }
+
+            if ((c.failedCounter == 0) && (c.successCounter == 1)) {
+                return;
+            }
+
+            if ((c.failedCounter == 1) && (c.successCounter == 0)) {
+                throw new IOException("The server denied the request.");
+            }
+
+            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;
+        }
+
+        log.debug("Closing all X11 channels for the given fake cookie");
+        List<Channel> channel_copy = new ArrayList<Channel>();
+
+        synchronized (channels) {
+            channel_copy.addAll(channels);
+        }
+
+        for (Channel c : channel_copy) {
+            synchronized (c) {
+                if (hexFakeCookie.equals(c.hexX11FakeCookie) == false) {
+                    continue;
+                }
+            }
+
+            try {
+                closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
+            }
+            catch (IOException ignored) {
+            }
+        }
+    }
+
+    public X11ServerData checkX11Cookie(String hexFakeCookie) {
+        synchronized (x11_magic_cookies) {
+            if (hexFakeCookie != null) {
+                return x11_magic_cookies.get(hexFakeCookie);
+            }
+        }
+
+        return null;
+    }
+
+    public void closeAllChannels() {
+        log.debug("Closing all channels");
+        List<Channel> channel_copy = new ArrayList<Channel>();
+
+        synchronized (channels) {
+            channel_copy.addAll(channels);
+        }
+
+        for (Channel c : channel_copy) {
+            try {
+                closeChannel(c, "Closing all channels", true);
+            }
+            catch (IOException ignored) {
+            }
+        }
+    }
+
+    public void closeChannel(Channel c, String reason, boolean force) throws IOException {
+        this.closeChannel(c, new ChannelClosedException(reason), force);
+    }
+
+    public void closeChannel(Channel c, IOException 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) {
+                return;
+            }
+
+            tm.sendMessage(msg);
+            c.closeMessageSent = true;
+        }
+
+        log.debug("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);
+        }
+
+        log.debug("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 c.getReasonClosed();
+                    }
+
+                    if (c.state != Channel.STATE_OPEN) {
+                        throw new ChannelClosedException("SSH channel in strange state. (" + c.state + ")");
+                    }
+
+                    if (c.remoteWindow != 0) {
+                        break;
+                    }
+
+                    try {
+                        c.wait();
+                    }
+                    catch (InterruptedException e) {
+                        throw new InterruptedIOException(e.getMessage());
+                    }
+                }
+
+                /* 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) {
+                    throw 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) {
+            if (remoteForwardings.get(bindPort) != null) {
+                throw new IOException("There is already a forwarding for remote port " + bindPort);
+            }
+
+            remoteForwardings.put(bindPort, rfd);
+        }
+
+        synchronized (channels) {
+            globalSuccessCounter = globalFailedCounter = 0;
+        }
+
+        PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
+        tm.sendMessage(pgf.getPayload());
+        log.debug("Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
+
+        try {
+            waitForGlobalSuccessOrFailure();
+        }
+        catch (IOException e) {
+            synchronized (remoteForwardings) {
+                remoteForwardings.remove(bindPort);
+            }
+
+            throw e;
+        }
+
+        return bindPort;
+    }
+
+    public void requestCancelGlobalForward(int bindPort) throws IOException {
+        RemoteForwardingData rfd;
+
+        synchronized (remoteForwardings) {
+            rfd = remoteForwardings.get(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());
+        log.debug("Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
+        waitForGlobalSuccessOrFailure();
+
+        /* Only now we are sure that no more forwarded connections will arrive */
+
+        synchronized (remoteForwardings) {
+            remoteForwardings.remove(bindPort);
+        }
+    }
+
+    /**
+     * @param agent
+     * @throws IOException
+     */
+    public void requestChannelAgentForwarding(Channel c, AuthAgentCallback authAgent) throws IOException {
+        synchronized (this) {
+            if (this.authAgent != null)
+                throw new IllegalStateException("Auth agent already exists");
+
+            this.authAgent = authAgent;
+        }
+
+        synchronized (channels) {
+            globalSuccessCounter = globalFailedCounter = 0;
+        }
+
+        log.debug("Requesting agent forwarding");
+        PacketChannelAuthAgentReq aar = new PacketChannelAuthAgentReq(c.remoteID);
+
+        synchronized (c.channelSendLock) {
+            if (c.closeMessageSent) {
+                throw c.getReasonClosed();
+            }
+
+            tm.sendMessage(aar.getPayload());
+        }
+
+        try {
+            waitForChannelSuccessOrFailure(c);
+        }
+        catch (IOException e) {
+            authAgent = null;
+            throw e;
+        }
+    }
+
+    public void registerThread(IChannelWorkerThread thr) throws IOException {
+        synchronized (listenerThreads) {
+            if (listenerThreadsAllowed == false) {
+                throw new IOException("Too late, this connection is closed.");
+            }
+
+            listenerThreads.add(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
+        }
+
+        log.debug("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 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 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 c.getReasonClosed();
+            }
+
+            tm.sendMessage(spr.getPayload());
+        }
+
+        try {
+            waitForChannelSuccessOrFailure(c);
+        }
+        catch (IOException e) {
+            throw new IOException("PTY request failed", e);
+        }
+    }
+
+    public void requestWindowChange(Channel c, int term_width_characters, int term_height_characters,
+                                    int term_width_pixels, int term_height_pixels) throws IOException {
+        PacketWindowChange pwc;
+
+        synchronized (c) {
+            if (c.state != Channel.STATE_OPEN) {
+                throw c.getReasonClosed();
+            }
+
+            pwc = new PacketWindowChange(c.remoteID, term_width_characters, term_height_characters,
+                                         term_width_pixels, term_height_pixels);
+            c.successCounter = c.failedCounter = 0;
+        }
+
+        synchronized (c.channelSendLock) {
+            if (c.closeMessageSent) {
+                throw c.getReasonClosed();
+            }
+
+            tm.sendMessage(pwc.getPayload());
+        }
+
+        try {
+            waitForChannelSuccessOrFailure(c);
+        }
+        catch (IOException e) {
+            throw new IOException("The window-change request failed.", 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 c.getReasonClosed();
+            }
+
+            psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
+                                              x11AuthenticationCookie, x11ScreenNumber);
+            c.successCounter = c.failedCounter = 0;
+        }
+
+        synchronized (c.channelSendLock) {
+            if (c.closeMessageSent) {
+                throw c.getReasonClosed();
+            }
+
+            tm.sendMessage(psr.getPayload());
+        }
+
+        log.debug("Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
+
+        try {
+            waitForChannelSuccessOrFailure(c);
+        }
+        catch (IOException e) {
+            throw new IOException("The X11 request failed.", e);
+        }
+    }
+
+    public void requestSubSystem(Channel c, String subSystemName) throws IOException {
+        PacketSessionSubsystemRequest ssr;
+
+        synchronized (c) {
+            if (c.state != Channel.STATE_OPEN) {
+                throw c.getReasonClosed();
+            }
+
+            ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
+            c.successCounter = c.failedCounter = 0;
+        }
+
+        synchronized (c.channelSendLock) {
+            if (c.closeMessageSent) {
+                throw c.getReasonClosed();
+            }
+
+            tm.sendMessage(ssr.getPayload());
+        }
+
+        try {
+            waitForChannelSuccessOrFailure(c);
+        }
+        catch (IOException e) {
+            throw new IOException("The subsystem request failed.", e);
+        }
+    }
+
+    public void requestExecCommand(Channel c, String cmd) throws IOException {
+        this.requestExecCommand(c, cmd, null);
+    }
+
+    /**
+     * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
+     */
+    public void requestExecCommand(Channel c, String cmd, String charsetName) throws IOException {
+        PacketSessionExecCommand sm;
+
+        synchronized (c) {
+            if (c.state != Channel.STATE_OPEN) {
+                throw c.getReasonClosed();
+            }
+
+            sm = new PacketSessionExecCommand(c.remoteID, true, cmd, charsetName);
+            c.successCounter = c.failedCounter = 0;
+        }
+
+        synchronized (c.channelSendLock) {
+            if (c.closeMessageSent) {
+                throw c.getReasonClosed();
+            }
+
+            tm.sendMessage(sm.getPayload());
+        }
+
+        log.debug("Executing command (channel " + c.localID + ", '" + cmd + "')");
+
+        try {
+            waitForChannelSuccessOrFailure(c);
+        }
+        catch (IOException e) {
+            throw new IOException("The execute request failed.", e);
+        }
+    }
+
+    public void requestShell(Channel c) throws IOException {
+        PacketSessionStartShell sm;
+
+        synchronized (c) {
+            if (c.state != Channel.STATE_OPEN) {
+                throw c.getReasonClosed();
+            }
+
+            sm = new PacketSessionStartShell(c.remoteID, true);
+            c.successCounter = c.failedCounter = 0;
+        }
+
+        synchronized (c.channelSendLock) {
+            if (c.closeMessageSent) {
+                throw c.getReasonClosed();
+            }
+
+            tm.sendMessage(sm.getPayload());
+        }
+
+        try {
+            waitForChannelSuccessOrFailure(c);
+        }
+        catch (IOException e) {
+            throw new IOException("The shell request failed.", e);
+        }
+    }
+
+    public void msgChannelExtendedData(byte[] msg) throws IOException {
+        if (msg.length <= 13) {
+            throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (%d)", msg.length));
+        }
+
+        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 PacketTypeException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
+        }
+
+        if (dataType != Packets.SSH_EXTENDED_DATA_STDERR) {
+            throw new PacketFormatException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
+        }
+
+        if (len != (msg.length - 13)) {
+            throw new PacketFormatException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msg.length - 13)
+                                            + ", got " + len + ")");
+        }
+
+        log.debug("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 PacketTypeException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
+                                              + c.state + ")");
+            }
+
+            if (c.localWindow < len) {
+                throw new PacketFormatException("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 (at least one of the conditions must be fulfilled)
+     * @return all current events
+     */
+    public int waitForCondition(Channel c, long timeout, int condition_mask) throws IOException {
+        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;
+                        }
+                    }
+                }
+
+                try {
+                    if (timeout > 0) {
+                        c.wait(timeout);
+                    }
+                    else {
+                        c.wait();
+                    }
+                }
+                catch (InterruptedException e) {
+                    throw new InterruptedIOException(e.getMessage());
+                }
+            }
+        }
+    }
+
+    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 e) {
+                    throw new InterruptedIOException(e.getMessage());
+                }
+            }
+
+            /* 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) {
+            log.debug("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) {
+                    tm.sendMessage(msg);
+                }
+            }
+        }
+
+        return copylen;
+    }
+
+    public void msgChannelData(byte[] msg) throws IOException {
+        if (msg.length <= 9) {
+            throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_DATA message has wrong size (%d)", msg.length));
+        }
+
+        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 PacketTypeException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
+        }
+
+        if (len != (msg.length - 9)) {
+            throw new PacketFormatException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msg.length - 9) + ", got "
+                                            + len + ")");
+        }
+
+        log.debug("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 PacketTypeException("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) throws IOException {
+        if (msg.length != 9) {
+            throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (%d)", msg.length));
+        }
+
+        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 PacketTypeException("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();
+        }
+
+        log.debug("Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
+    }
+
+    public void msgChannelOpen(byte[] msg) throws IOException {
+        TypesReader tr = new TypesReader(msg);
+        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());
+                    log.warning("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;
+
+            synchronized (remoteForwardings) {
+                rfd = remoteForwardings.get(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());
+                log.debug("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;
+        }
+
+        if ((server_state != null) && ("session".equals(channelType))) {
+            ServerConnectionCallback cb;
+
+            synchronized (server_state) {
+                cb = server_state.cb_conn;
+            }
+
+            if (cb == null) {
+                tm.sendAsynchronousMessage(new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
+                                           "Sessions are currently not enabled", "en").getPayload());
+                return;
+            }
+
+            final 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);
+                c.state = Channel.STATE_OPEN;
+                c.ss = new ServerSessionImpl(c);
+            }
+
+            PacketChannelOpenConfirmation pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID,
+                    c.localWindow, c.localMaxPacketSize);
+            tm.sendAsynchronousMessage(pcoc.getPayload());
+            c.ss.sscb = cb.acceptSession(c.ss);
+            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());
+        log.warning("The peer tried to open an unsupported channel type (" + channelType + ")");
+    }
+
+    /* Starts the given runnable in a foreground (non-daemon) thread */
+    private void runAsync(Runnable r) {
+        Thread t = new Thread(r);
+        t.start();
+    }
+
+    public void msgChannelRequest(byte[] msg) throws IOException {
+        TypesReader tr = new TypesReader(msg);
+        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);
+        }
+
+        ServerSessionImpl server_session = null;
+
+        if (server_state != null) {
+            synchronized (c) {
+                server_session = c.ss;
+            }
+        }
+
+        String type = tr.readString("US-ASCII");
+        boolean wantReply = tr.readBoolean();
+        log.debug("Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
+
+        if (type.equals("exit-status")) {
+            if (wantReply) {
+                throw new IOException(
+                    "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-status 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 = exit_status;
+                c.notifyAll();
+            }
+
+            log.debug("Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
+            return;
+        }
+
+        if ((server_state == null) && (type.equals("exit-signal"))) {
+            if (wantReply) {
+                throw new IOException(
+                    "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-signal 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();
+            }
+
+            log.debug("Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
+            return;
+        }
+
+        if ((server_session != null) && (type.equals("pty-req"))) {
+            PtySettings pty = new PtySettings();
+            pty.term = tr.readString();
+            pty.term_width_characters = tr.readUINT32();
+            pty.term_height_characters = tr.readUINT32();
+            pty.term_width_pixels = tr.readUINT32();
+            pty.term_height_pixels = tr.readUINT32();
+            pty.terminal_modes = tr.readByteString();
+
+            if (tr.remain() != 0) {
+                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+            }
+
+            Runnable run_after_sending_success = null;
+            ServerSessionCallback sscb = server_session.getServerSessionCallback();
+
+            if (sscb != null) {
+                run_after_sending_success = sscb.requestPtyReq(server_session, pty);
+            }
+
+            if (wantReply) {
+                if (run_after_sending_success != null) {
+                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
+                }
+                else {
+                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+                }
+            }
+
+            if (run_after_sending_success != null) {
+                runAsync(run_after_sending_success);
+            }
+
+            return;
+        }
+
+        if ((server_session != null) && (type.equals("shell"))) {
+            if (tr.remain() != 0) {
+                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+            }
+
+            Runnable run_after_sending_success = null;
+            ServerSessionCallback sscb = server_session.getServerSessionCallback();
+
+            if (sscb != null) {
+                run_after_sending_success = sscb.requestShell(server_session);
+            }
+
+            if (wantReply) {
+                if (run_after_sending_success != null) {
+                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
+                }
+                else {
+                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+                }
+            }
+
+            if (run_after_sending_success != null) {
+                runAsync(run_after_sending_success);
+            }
+
+            return;
+        }
+
+        if ((server_session != null) && (type.equals("exec"))) {
+            String command = tr.readString();
+
+            if (tr.remain() != 0) {
+                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+            }
+
+            Runnable run_after_sending_success = null;
+            ServerSessionCallback sscb = server_session.getServerSessionCallback();
+
+            if (sscb != null) {
+                run_after_sending_success = sscb.requestExec(server_session, command);
+            }
+
+            if (wantReply) {
+                if (run_after_sending_success != null) {
+                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
+                }
+                else {
+                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+                }
+            }
+
+            if (run_after_sending_success != null) {
+                runAsync(run_after_sending_success);
+            }
+
+            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) {
+            tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+        }
+
+        log.debug("Channel request '" + type + "' is not known, ignoring it");
+    }
+
+    public void msgChannelEOF(byte[] msg) throws IOException {
+        if (msg.length != 5) {
+            throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_EOF message has wrong size (%d)", msg.length));
+        }
+
+        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 PacketTypeException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
+        }
+
+        synchronized (c) {
+            c.EOF = true;
+            c.notifyAll();
+        }
+
+        log.debug("Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
+    }
+
+    public void msgChannelClose(byte[] msg) throws IOException {
+        if (msg.length != 5) {
+            throw new PacketFormatException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msg.length + ")");
+        }
+
+        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(new ChannelClosedException("Close requested by remote"));
+            c.closeMessageRecv = true;
+            removeChannel(c.localID);
+            c.notifyAll();
+        }
+
+        log.debug("Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
+    }
+
+    public void msgChannelSuccess(byte[] msg) throws IOException {
+        if (msg.length != 5) {
+            throw new PacketFormatException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msg.length + ")");
+        }
+
+        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 PacketTypeException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
+        }
+
+        synchronized (c) {
+            c.successCounter++;
+            c.notifyAll();
+        }
+
+        log.debug("Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
+    }
+
+    public void msgChannelFailure(byte[] msg) throws IOException {
+        if (msg.length != 5) {
+            throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_FAILURE message has wrong size (%d)", msg.length));
+        }
+
+        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 PacketTypeException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
+        }
+
+        synchronized (c) {
+            c.failedCounter++;
+            c.notifyAll();
+        }
+
+        log.debug("Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
+    }
+
+    public void msgChannelOpenConfirmation(byte[] msg) throws IOException {
+        PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg);
+        Channel c = getChannel(sm.getRecipientChannelID());
+
+        if (c == null) {
+            throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
+                                          + sm.getRecipientChannelID());
+        }
+
+        synchronized (c) {
+            if (c.state != Channel.STATE_OPENING) {
+                throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
+                                              + sm.getRecipientChannelID());
+            }
+
+            c.remoteID = sm.getSenderChannelID();
+            c.remoteWindow = sm.getInitialWindowSize() & 0xFFFFffffL; /* convert UINT32 to long */
+            c.remoteMaxPacketSize = sm.getMaxPacketSize();
+            c.state = Channel.STATE_OPEN;
+            c.notifyAll();
+        }
+
+        log.debug("Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.getRecipientChannelID() + " / remote: "
+                  + sm.getSenderChannelID() + ")");
+    }
+
+    public void msgChannelOpenFailure(byte[] msg) throws IOException {
+        if (msg.length < 5) {
+            throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (%d)", msg.length));
+        }
+
+        TypesReader tr = new TypesReader(msg);
+        tr.readByte(); // skip packet type
+        int id = tr.readUINT32(); /* sender channel */
+        Channel c = getChannel(id);
+
+        if (c == null) {
+            throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
+        }
+
+        int reasonCode = tr.readUINT32();
+        String description = tr.readString("UTF-8");
+        String reasonCodeSymbolicName;
+
+        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 + ")";
+        }
+
+        StringBuilder descriptionBuffer = new StringBuilder();
+        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(new ChannelClosedException(String.format("The server refused to open the channel (%s, '%s')",
+                              reasonCodeSymbolicName, descriptionBuffer.toString())));
+            c.notifyAll();
+        }
+
+        log.debug("Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
+    }
+
+    public void msgGlobalRequest(byte[] msg) throws IOException {
+        /* Currently we do not support any kind of global request */
+        TypesReader tr = new TypesReader(msg);
+        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 */
+        log.debug("Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
+    }
+
+    public void msgGlobalSuccess() throws IOException {
+        synchronized (channels) {
+            globalSuccessCounter++;
+            channels.notifyAll();
+        }
+
+        log.debug("Got SSH_MSG_REQUEST_SUCCESS");
+    }
+
+    public void msgGlobalFailure() throws IOException {
+        synchronized (channels) {
+            globalFailedCounter++;
+            channels.notifyAll();
+        }
+
+        log.debug("Got SSH_MSG_REQUEST_FAILURE");
+    }
+
+    public void handleFailure(final IOException failure) {
+        log.debug("HandleMessage: got shutdown");
+
+        synchronized (listenerThreads) {
+            for (IChannelWorkerThread lat : listenerThreads) {
+                lat.stopWorking();
+            }
+
+            listenerThreadsAllowed = false;
+        }
+
+        synchronized (channels) {
+            shutdown = true;
+
+            for (Channel c : channels) {
+                synchronized (c) {
+                    c.EOF = true;
+                    c.state = Channel.STATE_CLOSED;
+                    c.setReasonClosed(failure);
+                    c.closeMessageRecv = true;
+                    c.notifyAll();
+                }
+            }
+
+            channels.clear();
+            channels.notifyAll(); /* Notify global response waiters */
+        }
+    }
+
+    public void handleMessage(byte[] msg) throws IOException {
+        switch (msg[0]) {
+            case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
+                msgChannelOpenConfirmation(msg);
+                break;
+
+            case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
+                msgChannelWindowAdjust(msg);
+                break;
+
+            case Packets.SSH_MSG_CHANNEL_DATA:
+                msgChannelData(msg);
+                break;
+
+            case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
+                msgChannelExtendedData(msg);
+                break;
+
+            case Packets.SSH_MSG_CHANNEL_REQUEST:
+                msgChannelRequest(msg);
+                break;
+
+            case Packets.SSH_MSG_CHANNEL_EOF:
+                msgChannelEOF(msg);
+                break;
+
+            case Packets.SSH_MSG_CHANNEL_OPEN:
+                msgChannelOpen(msg);
+                break;
+
+            case Packets.SSH_MSG_CHANNEL_CLOSE:
+                msgChannelClose(msg);
+                break;
+
+            case Packets.SSH_MSG_CHANNEL_SUCCESS:
+                msgChannelSuccess(msg);
+                break;
+
+            case Packets.SSH_MSG_CHANNEL_FAILURE:
+                msgChannelFailure(msg);
+                break;
+
+            case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
+                msgChannelOpenFailure(msg);
+                break;
+
+            case Packets.SSH_MSG_GLOBAL_REQUEST:
+                msgGlobalRequest(msg);
+                break;
+
+            case Packets.SSH_MSG_REQUEST_SUCCESS:
+                msgGlobalSuccess();
+                break;
+
+            case Packets.SSH_MSG_REQUEST_FAILURE:
+                msgGlobalFailure();
+                break;
+
+            default:
+                throw new PacketTypeException(msg[0]);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/ChannelOutputStream.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * ChannelOutputStream.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class ChannelOutputStream extends OutputStream {
+    Channel c;
+
+    boolean isClosed = false;
+
+    ChannelOutputStream(Channel c) {
+        this.c = c;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        byte[] buff = new byte[1];
+        buff[0] = (byte) b;
+        write(buff, 0, 1);
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (isClosed == false) {
+            isClosed = true;
+            c.cm.sendEOF(c);
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+        if (isClosed)
+            throw new IOException("This OutputStream is closed.");
+
+        /* This is a no-op, since this stream is unbuffered */
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (isClosed)
+            throw new IOException("This OutputStream is closed.");
+
+        if (b == null)
+            throw new NullPointerException();
+
+        if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
+            throw new IndexOutOfBoundsException();
+
+        if (len == 0)
+            return;
+
+        c.cm.sendData(c, b, off, len);
+    }
+
+    @Override
+    public void write(byte[] b) throws IOException {
+        write(b, 0, b.length);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/DynamicAcceptThread.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,295 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed 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 ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NoRouteToHostException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import net.sourceforge.jsocks.Proxy;
+import net.sourceforge.jsocks.ProxyMessage;
+import net.sourceforge.jsocks.Socks4Message;
+import net.sourceforge.jsocks.Socks5Message;
+import net.sourceforge.jsocks.SocksException;
+import net.sourceforge.jsocks.server.ServerAuthenticator;
+import net.sourceforge.jsocks.server.ServerAuthenticatorNone;
+
+/**
+ * DynamicAcceptThread.
+ *
+ * @author Kenny Root
+ * @version $Id$
+ */
+public class DynamicAcceptThread extends Thread implements IChannelWorkerThread {
+    private ChannelManager cm;
+    private ServerSocket ss;
+
+    class DynamicAcceptRunnable implements Runnable {
+        private static final int idleTimeout    = 180000; //3 minutes
+
+        private ServerAuthenticator auth;
+        private Socket sock;
+        private InputStream in;
+        private OutputStream out;
+        private ProxyMessage msg;
+
+        public DynamicAcceptRunnable(ServerAuthenticator auth, Socket sock) {
+            this.auth = auth;
+            this.sock = sock;
+            setName("DynamicAcceptRunnable");
+        }
+
+        public void run() {
+            try {
+                startSession();
+            }
+            catch (IOException ioe) {
+                int error_code = Proxy.SOCKS_FAILURE;
+
+                if (ioe instanceof SocksException)
+                    error_code = ((SocksException) ioe).errCode;
+                else if (ioe instanceof NoRouteToHostException)
+                    error_code = Proxy.SOCKS_HOST_UNREACHABLE;
+                else if (ioe instanceof ConnectException)
+                    error_code = Proxy.SOCKS_CONNECTION_REFUSED;
+                else if (ioe instanceof InterruptedIOException)
+                    error_code = Proxy.SOCKS_TTL_EXPIRE;
+
+                if (error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED
+                        || error_code < 0) {
+                    error_code = Proxy.SOCKS_FAILURE;
+                }
+
+                sendErrorMessage(error_code);
+            }
+            finally {
+                if (auth != null)
+                    auth.endSession();
+            }
+        }
+
+        private ProxyMessage readMsg(InputStream in) throws IOException {
+            PushbackInputStream push_in;
+
+            if (in instanceof PushbackInputStream)
+                push_in = (PushbackInputStream) in;
+            else
+                push_in = new PushbackInputStream(in);
+
+            int version = push_in.read();
+            push_in.unread(version);
+            ProxyMessage msg;
+
+            if (version == 5) {
+                msg = new Socks5Message(push_in, false);
+            }
+            else if (version == 4) {
+                msg = new Socks4Message(push_in, false);
+            }
+            else {
+                throw new SocksException(Proxy.SOCKS_FAILURE);
+            }
+
+            return msg;
+        }
+
+        private void sendErrorMessage(int error_code) {
+            ProxyMessage err_msg;
+
+            if (msg instanceof Socks4Message)
+                err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED);
+            else
+                err_msg = new Socks5Message(error_code);
+
+            try {
+                err_msg.write(out);
+            }
+            catch (IOException ioe) {
+            }
+        }
+
+        private void handleRequest(ProxyMessage msg) throws IOException {
+            if (!auth.checkRequest(msg))
+                throw new SocksException(Proxy.SOCKS_FAILURE);
+
+            switch (msg.command) {
+                case Proxy.SOCKS_CMD_CONNECT:
+                    onConnect(msg);
+                    break;
+
+                default:
+                    throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED);
+            }
+        }
+
+        private void startSession() throws IOException {
+            sock.setSoTimeout(idleTimeout);
+
+            try {
+                auth = auth.startSession(sock);
+            }
+            catch (IOException ioe) {
+                System.out.println("Could not start SOCKS session");
+                ioe.printStackTrace();
+                auth = null;
+                return;
+            }
+
+            if (auth == null) { // Authentication failed
+                System.out.println("SOCKS auth failed");
+                return;
+            }
+
+            in = auth.getInputStream();
+            out = auth.getOutputStream();
+            msg = readMsg(in);
+            handleRequest(msg);
+        }
+
+        private void onConnect(ProxyMessage msg) throws IOException {
+            ProxyMessage response = null;
+            Channel cn = null;
+            StreamForwarder r2l = null;
+            StreamForwarder l2r = null;
+
+            if (msg instanceof Socks5Message) {
+                response = new Socks5Message(Proxy.SOCKS_SUCCESS, (InetAddress)null, 0);
+            }
+            else {
+                response = new Socks4Message(Socks4Message.REPLY_OK, (InetAddress)null, 0);
+            }
+
+            response.write(out);
+            String destHost = msg.host;
+
+            if (msg.ip != null)
+                destHost = msg.ip.getHostAddress();
+
+            try {
+                /*
+                 * This may fail, e.g., if the remote port is closed (in
+                 * optimistic terms: not open yet)
+                 */
+                cn = cm.openDirectTCPIPChannel(destHost, msg.port,
+                                               "127.0.0.1", 0);
+            }
+            catch (IOException e) {
+                /*
+                 * Simply close the local socket and wait for the next incoming
+                 * connection
+                 */
+                try {
+                    sock.close();
+                }
+                catch (IOException ignore) {
+                }
+
+                return;
+            }
+
+            try {
+                r2l = new StreamForwarder(cn, null, sock, cn.stdoutStream, out, "RemoteToLocal");
+                l2r = new StreamForwarder(cn, r2l, sock, in, cn.stdinStream, "LocalToRemote");
+            }
+            catch (IOException e) {
+                try {
+                    /*
+                     * This message is only visible during debugging, since we
+                     * discard the channel immediatelly
+                     */
+                    cn.cm.closeChannel(cn,
+                                       "Weird error during creation of StreamForwarder ("
+                                       + e.getMessage() + ")", true);
+                }
+                catch (IOException ignore) {
+                }
+
+                return;
+            }
+
+            r2l.setDaemon(true);
+            l2r.setDaemon(true);
+            r2l.start();
+            l2r.start();
+        }
+    }
+
+    public DynamicAcceptThread(ChannelManager cm, int local_port)
+    throws IOException {
+        this.cm = cm;
+        setName("DynamicAcceptThread");
+        ss = new ServerSocket(local_port);
+    }
+
+    public DynamicAcceptThread(ChannelManager cm, InetSocketAddress localAddress)
+    throws IOException {
+        this.cm = cm;
+        ss = new ServerSocket();
+        ss.bind(localAddress);
+    }
+
+    @Override
+    public void run() {
+        try {
+            cm.registerThread(this);
+        }
+        catch (IOException e) {
+            stopWorking();
+            return;
+        }
+
+        while (true) {
+            Socket sock = null;
+
+            try {
+                sock = ss.accept();
+            }
+            catch (IOException e) {
+                stopWorking();
+                return;
+            }
+
+            DynamicAcceptRunnable dar = new DynamicAcceptRunnable(new ServerAuthenticatorNone(), sock);
+            Thread t = new Thread(dar);
+            t.setDaemon(true);
+            t.start();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see ch.ethz.ssh2.channel.IChannelWorkerThread#stopWorking()
+     */
+    public void stopWorking() {
+        try {
+            /* This will lead to an IOException in the ss.accept() call */
+            ss.close();
+        }
+        catch (IOException e) {
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/IChannelWorkerThread.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+/**
+ * IChannelWorkerThread.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+interface IChannelWorkerThread {
+    public void stopWorking();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/LocalAcceptThread.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * LocalAcceptThread.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class LocalAcceptThread extends Thread implements IChannelWorkerThread {
+    ChannelManager cm;
+    String host_to_connect;
+    int port_to_connect;
+
+    final ServerSocket ss;
+
+    public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
+    throws IOException {
+        this.cm = cm;
+        this.host_to_connect = host_to_connect;
+        this.port_to_connect = port_to_connect;
+        ss = new ServerSocket(local_port);
+    }
+
+    public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
+                             int port_to_connect) throws IOException {
+        this.cm = cm;
+        this.host_to_connect = host_to_connect;
+        this.port_to_connect = port_to_connect;
+        ss = new ServerSocket();
+        ss.bind(localAddress);
+    }
+
+    public ServerSocket getServerSocket() {
+        return ss;
+    }
+
+    @Override
+    public void run() {
+        try {
+            cm.registerThread(this);
+        }
+        catch (IOException e) {
+            stopWorking();
+            return;
+        }
+
+        while (true) {
+            Socket s = null;
+
+            try {
+                s = ss.accept();
+            }
+            catch (IOException e) {
+                stopWorking();
+                return;
+            }
+
+            Channel cn = null;
+            StreamForwarder r2l = null;
+            StreamForwarder l2r = null;
+
+            try {
+                /* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */
+                cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
+                                               .getPort());
+            }
+            catch (IOException e) {
+                /* Simply close the local socket and wait for the next incoming connection */
+                try {
+                    s.close();
+                }
+                catch (IOException ignore) {
+                }
+
+                continue;
+            }
+
+            try {
+                r2l = new StreamForwarder(cn, null, null, cn.stdoutStream, s.getOutputStream(), "RemoteToLocal");
+                l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
+            }
+            catch (IOException e) {
+                try {
+                    /* This message is only visible during debugging, since we discard the channel immediatelly */
+                    cn.cm.closeChannel(cn, e, true);
+                }
+                catch (IOException ignore) {
+                }
+
+                continue;
+            }
+
+            r2l.setDaemon(true);
+            l2r.setDaemon(true);
+            r2l.start();
+            l2r.start();
+        }
+    }
+
+    public void stopWorking() {
+        try {
+            /* This will lead to an IOException in the ss.accept() call */
+            ss.close();
+        }
+        catch (IOException ignored) {
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/RemoteAcceptThread.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import ch.ethz.ssh2.log.Logger;
+
+/**
+ * RemoteAcceptThread.
+ *
+ * @author Christian Plattner
+ * @version $Id: RemoteAcceptThread.java 119 2014-04-12 20:30:58Z dkocher@sudo.ch $
+ */
+public class RemoteAcceptThread extends Thread {
+    private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
+
+    Channel c;
+
+    String remoteConnectedAddress;
+    int remoteConnectedPort;
+    String remoteOriginatorAddress;
+    int remoteOriginatorPort;
+    String targetAddress;
+    int targetPort;
+
+    Socket s;
+
+    public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
+                              String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort) {
+        this.c = c;
+        this.remoteConnectedAddress = remoteConnectedAddress;
+        this.remoteConnectedPort = remoteConnectedPort;
+        this.remoteOriginatorAddress = remoteOriginatorAddress;
+        this.remoteOriginatorPort = remoteOriginatorPort;
+        this.targetAddress = targetAddress;
+        this.targetPort = targetPort;
+        log.debug("RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
+                  + remoteOriginatorAddress + "/" + remoteOriginatorPort);
+    }
+
+    @Override
+    public void run() {
+        try {
+            c.cm.sendOpenConfirmation(c);
+            s = new Socket(targetAddress, targetPort);
+            StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(),
+                    "RemoteToLocal");
+            StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
+                    "LocalToRemote");
+            /* No need to start two threads, one can be executed in the current thread */
+            r2l.setDaemon(true);
+            r2l.start();
+            l2r.run();
+
+            while (r2l.isAlive()) {
+                try {
+                    r2l.join();
+                }
+                catch (InterruptedException ignored) {
+                }
+            }
+
+            /* If the channel is already closed, then this is a no-op */
+            c.cm.closeChannel(c, "EOF on both streams reached.", true);
+            s.close();
+        }
+        catch (IOException e) {
+            log.warning("IOException in proxy code: " + e.getMessage());
+
+            try {
+                c.cm.closeChannel(c, e, true);
+            }
+            catch (IOException ignored) {
+            }
+
+            try {
+                if (s != null)
+                    s.close();
+            }
+            catch (IOException ignored) {
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/RemoteForwardingData.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+/**
+ * RemoteForwardingData. Data about a requested remote forwarding.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class RemoteForwardingData {
+    public String bindAddress;
+    public int bindPort;
+
+    String targetAddress;
+    int targetPort;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/RemoteX11AcceptThread.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * RemoteX11AcceptThread.
+ *
+ * @author Christian Plattner
+ * @version $Id: RemoteX11AcceptThread.java 119 2014-04-12 20:30:58Z dkocher@sudo.ch $
+ */
+public class RemoteX11AcceptThread extends Thread {
+    private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class);
+
+    Channel c;
+
+    String remoteOriginatorAddress;
+    int remoteOriginatorPort;
+
+    Socket s;
+
+    public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort) {
+        this.c = c;
+        this.remoteOriginatorAddress = remoteOriginatorAddress;
+        this.remoteOriginatorPort = remoteOriginatorPort;
+    }
+
+    @Override
+    public void run() {
+        try {
+            /* Send Open Confirmation */
+            c.cm.sendOpenConfirmation(c);
+            /* Read startup packet from client */
+            OutputStream remote_os = c.getStdinStream();
+            InputStream remote_is = c.getStdoutStream();
+            /* The following code is based on the protocol description given in:
+             * Scheifler/Gettys,
+             * X Windows System: Core and Extension Protocols:
+             * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X
+             * (from the ETH library - after being here for almost ten
+             * years one of the few books I borrowed... sad but true =)
+             */
+            /*
+             * Client startup:
+             *
+             * 1 0X42 MSB first/0x6c lSB first - byteorder
+             * 1 - unused
+             * 2 card16 - protocol-major-version
+             * 2 card16 - protocol-minor-version
+             * 2 n - lenght of authorization-protocol-name
+             * 2 d - lenght of authorization-protocol-data
+             * 2 - unused
+             * string8 - authorization-protocol-name
+             * p - unused, p=pad(n)
+             * string8 - authorization-protocol-data
+             * q - unused, q=pad(d)
+             *
+             * pad(X) = (4 - (X mod 4)) mod 4
+             *
+             * Server response:
+             *
+             * 1 (0 failed, 2 authenticate, 1 success)
+             * ...
+             *
+             */
+            /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */
+            byte[] header = new byte[6];
+
+            if (remote_is.read(header) != 6)
+                throw new IOException("Unexpected EOF on X11 startup!");
+
+            if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first
+                throw new IOException("Unknown endian format in X11 message!");
+
+            /* Yes, I came up with this myself - shall I file an application for a patent? =) */
+            int idxMSB = (header[0] == 0x42) ? 0 : 1;
+            /* Read authorization data header */
+            byte[] auth_buff = new byte[6];
+
+            if (remote_is.read(auth_buff) != 6)
+                throw new IOException("Unexpected EOF on X11 startup!");
+
+            int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8) | (auth_buff[1 - idxMSB] & 0xff);
+            int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8) | (auth_buff[3 - idxMSB] & 0xff);
+
+            if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256))
+                throw new IOException("Buggy X11 authorization data");
+
+            int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);
+            int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);
+            byte[] authProtocolName = new byte[authProtocolNameLength];
+            byte[] authProtocolData = new byte[authProtocolDataLength];
+            byte[] paddingBuffer = new byte[4];
+
+            if (remote_is.read(authProtocolName) != authProtocolNameLength)
+                throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)");
+
+            if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding)
+                throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)");
+
+            if (remote_is.read(authProtocolData) != authProtocolDataLength)
+                throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)");
+
+            if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)
+                throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)");
+
+            if ("MIT-MAGIC-COOKIE-1".equals(StringEncoder.GetString(authProtocolName)) == false)
+                throw new IOException("Unknown X11 authorization protocol!");
+
+            if (authProtocolDataLength != 16)
+                throw new IOException("Wrong data length for X11 authorization data!");
+
+            StringBuilder tmp = new StringBuilder(32);
+
+            for (int i = 0; i < authProtocolData.length; i++) {
+                String digit2 = Integer.toHexString(authProtocolData[i] & 0xff);
+                tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
+            }
+
+            String hexEncodedFakeCookie = tmp.toString();
+
+            /* Order is very important here - it may be that a certain x11 forwarding
+             * gets disabled right in the moment when we check and register our connection
+             * */
+
+            synchronized (c) {
+                /* Please read the comment in Channel.java */
+                c.hexX11FakeCookie = hexEncodedFakeCookie;
+            }
+
+            /* Now check our fake cookie directory to see if we produced this cookie */
+            X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie);
+
+            if (sd == null)
+                throw new IOException("Invalid X11 cookie received.");
+
+            /* If the session which corresponds to this cookie is closed then we will
+             * detect this: the session's close code will close all channels
+             * with the session's assigned x11 fake cookie.
+             */
+            s = new Socket(sd.hostname, sd.port);
+            OutputStream x11_os = s.getOutputStream();
+            InputStream x11_is = s.getInputStream();
+            /* Now we are sending the startup packet to the real X11 server */
+            x11_os.write(header);
+
+            if (sd.x11_magic_cookie == null) {
+                byte[] emptyAuthData = new byte[6];
+                /* empty auth data, hopefully you are connecting to localhost =) */
+                x11_os.write(emptyAuthData);
+            }
+            else {
+                if (sd.x11_magic_cookie.length != 16)
+                    throw new IOException("The real X11 cookie has an invalid length!");
+
+                /* send X11 cookie specified by client */
+                x11_os.write(auth_buff);
+                x11_os.write(authProtocolName); /* re-use */
+                x11_os.write(paddingBuffer, 0, authProtocolNamePadding);
+                x11_os.write(sd.x11_magic_cookie);
+                x11_os.write(paddingBuffer, 0, authProtocolDataPadding);
+            }
+
+            x11_os.flush();
+            /* Start forwarding traffic */
+            StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is, x11_os, "RemoteToX11");
+            StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote");
+            /* No need to start two threads, one can be executed in the current thread */
+            r2l.setDaemon(true);
+            r2l.start();
+            l2r.run();
+
+            while (r2l.isAlive()) {
+                try {
+                    r2l.join();
+                }
+                catch (InterruptedException ignored) {
+                }
+            }
+
+            /* If the channel is already closed, then this is a no-op */
+            c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);
+            s.close();
+        }
+        catch (IOException e) {
+            log.warning("IOException in X11 proxy code: " + e.getMessage());
+
+            try {
+                c.cm.closeChannel(c, e, true);
+            }
+            catch (IOException ignored) {
+            }
+
+            try {
+                if (s != null)
+                    s.close();
+            }
+            catch (IOException ignored) {
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/ServerSessionImpl.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,50 @@
+
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import ch.ethz.ssh2.ServerSession;
+import ch.ethz.ssh2.ServerSessionCallback;
+
+public class ServerSessionImpl implements ServerSession {
+    Channel c;
+    public ServerSessionCallback sscb;
+
+    public ServerSessionImpl(Channel c) {
+        this.c = c;
+    }
+
+    public int getState() {
+        return c.getState();
+    }
+
+    public InputStream getStdout() {
+        return c.getStdoutStream();
+    }
+
+    public InputStream getStderr() {
+        return c.getStderrStream();
+    }
+
+    public OutputStream getStdin() {
+        return c.getStdinStream();
+    }
+
+    public void close() {
+        try {
+            c.cm.closeChannel(c, "Closed due to server request", true);
+        }
+        catch (IOException ignored) {
+        }
+    }
+
+    public synchronized ServerSessionCallback getServerSessionCallback() {
+        return sscb;
+    }
+
+    public synchronized void setServerSessionCallback(ServerSessionCallback sscb) {
+        this.sscb = sscb;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/StreamForwarder.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * A StreamForwarder forwards data between two given streams.
+ * If two StreamForwarder threads are used (one for each direction)
+ * then one can be configured to shutdown the underlying channel/socket
+ * if both threads have finished forwarding (EOF).
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class StreamForwarder extends Thread {
+    OutputStream os;
+    InputStream is;
+    byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
+    Channel c;
+    StreamForwarder sibling;
+    Socket s;
+    String mode;
+
+    StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
+    throws IOException {
+        this.is = is;
+        this.os = os;
+        this.mode = mode;
+        this.c = c;
+        this.sibling = sibling;
+        this.s = s;
+    }
+
+    @Override
+    public void run() {
+        try {
+            while (true) {
+                int len = is.read(buffer);
+
+                if (len <= 0)
+                    break;
+
+                os.write(buffer, 0, len);
+                os.flush();
+            }
+        }
+        catch (IOException e) {
+            try {
+                c.cm.closeChannel(c, e, true);
+            }
+            catch (IOException ignored) {
+            }
+        }
+        finally {
+            try {
+                os.close();
+            }
+            catch (IOException ignored) {
+            }
+
+            try {
+                is.close();
+            }
+            catch (IOException ignored) {
+            }
+
+            if (sibling != null) {
+                while (sibling.isAlive()) {
+                    try {
+                        sibling.join();
+                    }
+                    catch (InterruptedException ignored) {
+                    }
+                }
+
+                try {
+                    c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
+                }
+                catch (IOException ignored) {
+                }
+
+                try {
+                    if (s != null)
+                        s.close();
+                }
+                catch (IOException ignored) {
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/X11ServerData.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+/**
+ * X11ServerData. Data regarding an x11 forwarding target.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ *
+ */
+public class X11ServerData {
+    public String hostname;
+    public int port;
+    public byte[] x11_magic_cookie; /* not the remote (fake) one, the local (real) one */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/compression/CompressionFactory.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,88 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed 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 ch.ethz.ssh2.compression;
+
+import java.util.Vector;
+
+/**
+ * @author Kenny Root
+ *
+ */
+public class CompressionFactory {
+    static class CompressorEntry {
+        String type;
+        String compressorClass;
+
+        public CompressorEntry(String type, String compressorClass) {
+            this.type = type;
+            this.compressorClass = compressorClass;
+        }
+    }
+
+    static Vector<CompressorEntry> compressors = new Vector<CompressorEntry>();
+
+    static {
+        /* Higher Priority First */
+        compressors.addElement(new CompressorEntry("zlib", "ch.ethz.ssh2.compression.Zlib"));
+        compressors.addElement(new CompressorEntry("zlib@openssh.com", "ch.ethz.ssh2.compression.ZlibOpenSSH"));
+        compressors.addElement(new CompressorEntry("none", ""));
+    }
+
+    public static String[] getDefaultCompressorList() {
+        String list[] = new String[compressors.size()];
+
+        for (int i = 0; i < compressors.size(); i++) {
+            CompressorEntry ce = compressors.elementAt(i);
+            list[i] = new String(ce.type);
+        }
+
+        return list;
+    }
+
+    public static void checkCompressorList(String[] compressorCandidates) {
+        for (int i = 0; i < compressorCandidates.length; i++)
+            getEntry(compressorCandidates[i]);
+    }
+
+    public static Compressor createCompressor(String type) {
+        try {
+            CompressorEntry ce = getEntry(type);
+
+            if ("".equals(ce.compressorClass))
+                return null;
+
+            Class<?> cc = Class.forName(ce.compressorClass);
+            Compressor cmp = (Compressor) cc.newInstance();
+            return cmp;
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("Cannot instantiate " + type);
+        }
+    }
+
+    private static CompressorEntry getEntry(String type) {
+        for (int i = 0; i < compressors.size(); i++) {
+            CompressorEntry ce = compressors.elementAt(i);
+
+            if (ce.type.equals(type))
+                return ce;
+        }
+
+        throw new IllegalArgumentException("Unkown algorithm " + type);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/compression/Compressor.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,32 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed 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 ch.ethz.ssh2.compression;
+
+/**
+ * @author Kenny Root
+ *
+ */
+public interface Compressor {
+    int getBufferSize();
+
+    int compress(byte[] buf, int start, int len, byte[] output);
+
+    byte[] uncompress(byte[] buf, int start, int[] len);
+
+    boolean canCompressPreauth();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/compression/Zlib.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,131 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed 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 ch.ethz.ssh2.compression;
+
+import com.jcraft.jzlib.JZlib;
+import com.jcraft.jzlib.ZStream;
+
+/**
+ * @author Kenny Root
+ *
+ */
+public class Zlib implements Compressor {
+    static private final int DEFAULT_BUF_SIZE = 4096;
+    static private final int LEVEL = 5;
+
+    private ZStream deflate;
+    private byte[] deflate_tmpbuf;
+
+    private ZStream inflate;
+    private byte[] inflate_tmpbuf;
+    private byte[] inflated_buf;
+
+    public Zlib() {
+        deflate = new ZStream();
+        inflate = new ZStream();
+        deflate.deflateInit(LEVEL);
+        inflate.inflateInit();
+        deflate_tmpbuf = new byte[DEFAULT_BUF_SIZE];
+        inflate_tmpbuf = new byte[DEFAULT_BUF_SIZE];
+        inflated_buf = new byte[DEFAULT_BUF_SIZE];
+    }
+
+    public boolean canCompressPreauth() {
+        return true;
+    }
+
+    public int getBufferSize() {
+        return DEFAULT_BUF_SIZE;
+    }
+
+    public int compress(byte[] buf, int start, int len, byte[] output) {
+        deflate.next_in = buf;
+        deflate.next_in_index = start;
+        deflate.avail_in = len - start;
+
+        if ((buf.length + 1024) > deflate_tmpbuf.length) {
+            deflate_tmpbuf = new byte[buf.length + 1024];
+        }
+
+        deflate.next_out = deflate_tmpbuf;
+        deflate.next_out_index = 0;
+        deflate.avail_out = output.length;
+
+        if (deflate.deflate(JZlib.Z_PARTIAL_FLUSH) != JZlib.Z_OK) {
+            System.err.println("compress: compression failure");
+        }
+
+        if (deflate.avail_in > 0) {
+            System.err.println("compress: deflated data too large");
+        }
+
+        int outputlen = output.length - deflate.avail_out;
+        System.arraycopy(deflate_tmpbuf, 0, output, 0, outputlen);
+        return outputlen;
+    }
+
+    public byte[] uncompress(byte[] buffer, int start, int[] length) {
+        int inflated_end = 0;
+        inflate.next_in = buffer;
+        inflate.next_in_index = start;
+        inflate.avail_in = length[0];
+
+        while (true) {
+            inflate.next_out = inflate_tmpbuf;
+            inflate.next_out_index = 0;
+            inflate.avail_out = DEFAULT_BUF_SIZE;
+            int status = inflate.inflate(JZlib.Z_PARTIAL_FLUSH);
+
+            switch (status) {
+                case JZlib.Z_OK:
+                    if (inflated_buf.length < inflated_end + DEFAULT_BUF_SIZE
+                            - inflate.avail_out) {
+                        byte[] foo = new byte[inflated_end + DEFAULT_BUF_SIZE
+                                              - inflate.avail_out];
+                        System.arraycopy(inflated_buf, 0, foo, 0, inflated_end);
+                        inflated_buf = foo;
+                    }
+
+                    System.arraycopy(inflate_tmpbuf, 0, inflated_buf, inflated_end,
+                                     DEFAULT_BUF_SIZE - inflate.avail_out);
+                    inflated_end += (DEFAULT_BUF_SIZE - inflate.avail_out);
+                    length[0] = inflated_end;
+                    break;
+
+                case JZlib.Z_BUF_ERROR:
+                    if (inflated_end > buffer.length - start) {
+                        byte[] foo = new byte[inflated_end + start];
+                        System.arraycopy(buffer, 0, foo, 0, start);
+                        System.arraycopy(inflated_buf, 0, foo, start, inflated_end);
+                        buffer = foo;
+                    }
+                    else {
+                        System.arraycopy(inflated_buf, 0, buffer, start,
+                                         inflated_end);
+                    }
+
+                    length[0] = inflated_end;
+                    return buffer;
+
+                default:
+                    System.err.println("uncompress: inflate returnd " + status);
+                    return null;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/compression/ZlibOpenSSH.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed 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 ch.ethz.ssh2.compression;
+
+/**
+ * Defines how zlib@openssh.org compression works.
+ * See
+ * http://www.openssh.org/txt/draft-miller-secsh-compression-delayed-00.txt
+ * compression is disabled until userauth has occurred.
+ *
+ * @author Matt Johnston
+ *
+ */
+public class ZlibOpenSSH extends Zlib {
+
+    public boolean canCompressPreauth() {
+        return false;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/Base64.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+
+/**
+ * Basic Base64 Support.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class Base64 {
+    static final char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
+
+    public static char[] encode(byte[] content) {
+        CharArrayWriter cw = new CharArrayWriter((4 * content.length) / 3);
+        int idx = 0;
+        int x = 0;
+
+        for (int i = 0; i < content.length; i++) {
+            if (idx == 0)
+                x = (content[i] & 0xff) << 16;
+            else if (idx == 1)
+                x = x | ((content[i] & 0xff) << 8);
+            else
+                x = x | (content[i] & 0xff);
+
+            idx++;
+
+            if (idx == 3) {
+                cw.write(alphabet[x >> 18]);
+                cw.write(alphabet[(x >> 12) & 0x3f]);
+                cw.write(alphabet[(x >> 6) & 0x3f]);
+                cw.write(alphabet[x & 0x3f]);
+                idx = 0;
+            }
+        }
+
+        if (idx == 1) {
+            cw.write(alphabet[x >> 18]);
+            cw.write(alphabet[(x >> 12) & 0x3f]);
+            cw.write('=');
+            cw.write('=');
+        }
+
+        if (idx == 2) {
+            cw.write(alphabet[x >> 18]);
+            cw.write(alphabet[(x >> 12) & 0x3f]);
+            cw.write(alphabet[(x >> 6) & 0x3f]);
+            cw.write('=');
+        }
+
+        return cw.toCharArray();
+    }
+
+    public static byte[] decode(char[] message) throws IOException {
+        byte buff[] = new byte[4];
+        byte dest[] = new byte[message.length];
+        int bpos = 0;
+        int destpos = 0;
+
+        for (int i = 0; i < message.length; i++) {
+            int c = message[i];
+
+            if ((c == '\n') || (c == '\r') || (c == ' ') || (c == '\t'))
+                continue;
+
+            if ((c >= 'A') && (c <= 'Z')) {
+                buff[bpos++] = (byte)(c - 'A');
+            }
+            else if ((c >= 'a') && (c <= 'z')) {
+                buff[bpos++] = (byte)((c - 'a') + 26);
+            }
+            else if ((c >= '0') && (c <= '9')) {
+                buff[bpos++] = (byte)((c - '0') + 52);
+            }
+            else if (c == '+') {
+                buff[bpos++] = 62;
+            }
+            else if (c == '/') {
+                buff[bpos++] = 63;
+            }
+            else if (c == '=') {
+                buff[bpos++] = 64;
+            }
+            else {
+                throw new IOException("Illegal char in base64 code.");
+            }
+
+            if (bpos == 4) {
+                bpos = 0;
+
+                if (buff[0] == 64)
+                    break;
+
+                if (buff[1] == 64)
+                    throw new IOException("Unexpected '=' in base64 code.");
+
+                if (buff[2] == 64) {
+                    int v = (((buff[0] & 0x3f) << 6) | ((buff[1] & 0x3f)));
+                    dest[destpos++] = (byte)(v >> 4);
+                    break;
+                }
+                else if (buff[3] == 64) {
+                    int v = (((buff[0] & 0x3f) << 12) | ((buff[1] & 0x3f) << 6) | ((buff[2] & 0x3f)));
+                    dest[destpos++] = (byte)(v >> 10);
+                    dest[destpos++] = (byte)(v >> 2);
+                    break;
+                }
+                else {
+                    int v = (((buff[0] & 0x3f) << 18) | ((buff[1] & 0x3f) << 12) | ((buff[2] & 0x3f) << 6) | ((buff[3] & 0x3f)));
+                    dest[destpos++] = (byte)(v >> 16);
+                    dest[destpos++] = (byte)(v >> 8);
+                    dest[destpos++] = (byte)(v);
+                }
+            }
+        }
+
+        byte[] res = new byte[destpos];
+        System.arraycopy(dest, 0, res, 0, destpos);
+        return res;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/CryptoWishList.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+import ch.ethz.ssh2.compression.CompressionFactory;
+import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.transport.KexManager;
+
+/**
+ * CryptoWishList.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class CryptoWishList {
+    public String[] kexAlgorithms = KexManager.getDefaultClientKexAlgorithmList();
+    public String[] serverHostKeyAlgorithms = KexManager.getDefaultServerHostkeyAlgorithmList();
+    public String[] c2s_enc_algos = BlockCipherFactory.getDefaultCipherList();
+    public String[] s2c_enc_algos = BlockCipherFactory.getDefaultCipherList();
+    public String[] c2s_mac_algos = MAC.getMacList();
+    public String[] s2c_mac_algos = MAC.getMacList();
+    public String[] c2s_comp_algos = CompressionFactory.getDefaultCompressorList();
+    public String[] s2c_comp_algos = CompressionFactory.getDefaultCompressorList();
+
+    public static CryptoWishList forServer() {
+        CryptoWishList cwl = new CryptoWishList();
+        cwl.kexAlgorithms = KexManager.getDefaultServerKexAlgorithmList();
+        return cwl;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/KeyMaterial.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.crypto.digest.HashForSSH2Types;
+
+/**
+ * Establishes key material for iv/key/mac (both directions).
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class KeyMaterial {
+    public byte[] initial_iv_client_to_server;
+    public byte[] initial_iv_server_to_client;
+    public byte[] enc_key_client_to_server;
+    public byte[] enc_key_server_to_client;
+    public byte[] integrity_key_client_to_server;
+    public byte[] integrity_key_server_to_client;
+
+    private static byte[] calculateKey(HashForSSH2Types sh, BigInteger K, byte[] H, byte type, byte[] SessionID,
+                                       int keyLength) throws IOException {
+        byte[] res = new byte[keyLength];
+        int dglen = sh.getDigestLength();
+        int numRounds = (keyLength + dglen - 1) / dglen;
+        byte[][] tmp = new byte[numRounds][];
+        sh.reset();
+        sh.updateBigInt(K);
+        sh.updateBytes(H);
+        sh.updateByte(type);
+        sh.updateBytes(SessionID);
+        tmp[0] = sh.getDigest();
+        int off = 0;
+        int produced = Math.min(dglen, keyLength);
+        System.arraycopy(tmp[0], 0, res, off, produced);
+        keyLength -= produced;
+        off += produced;
+
+        for (int i = 1; i < numRounds; i++) {
+            sh.updateBigInt(K);
+            sh.updateBytes(H);
+
+            for (int j = 0; j < i; j++) {
+                sh.updateBytes(tmp[j]);
+            }
+
+            tmp[i] = sh.getDigest();
+            produced = Math.min(dglen, keyLength);
+            System.arraycopy(tmp[i], 0, res, off, produced);
+            keyLength -= produced;
+            off += produced;
+        }
+
+        return res;
+    }
+
+    public static KeyMaterial create(String hashType, byte[] H, BigInteger K, byte[] SessionID, int keyLengthCS,
+                                     int blockSizeCS, int macLengthCS, int keyLengthSC, int blockSizeSC, int macLengthSC)
+    throws IOException {
+        KeyMaterial km = new KeyMaterial();
+        HashForSSH2Types sh = new HashForSSH2Types(hashType);
+        km.initial_iv_client_to_server = calculateKey(sh, K, H, (byte) 'A', SessionID, blockSizeCS);
+        km.initial_iv_server_to_client = calculateKey(sh, K, H, (byte) 'B', SessionID, blockSizeSC);
+        km.enc_key_client_to_server = calculateKey(sh, K, H, (byte) 'C', SessionID, keyLengthCS);
+        km.enc_key_server_to_client = calculateKey(sh, K, H, (byte) 'D', SessionID, keyLengthSC);
+        km.integrity_key_client_to_server = calculateKey(sh, K, H, (byte) 'E', SessionID, macLengthCS);
+        km.integrity_key_server_to_client = calculateKey(sh, K, H, (byte) 'F', SessionID, macLengthSC);
+        return km;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/PEMDecoder.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,444 @@
+
+package ch.ethz.ssh2.crypto;
+
+import java.io.BufferedReader;
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.DigestException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+
+import ch.ethz.ssh2.crypto.cipher.AES;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.cipher.CBCMode;
+import ch.ethz.ssh2.crypto.cipher.DES;
+import ch.ethz.ssh2.crypto.cipher.DESede;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+
+/**
+ * PEM Support.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PEMDecoder.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class PEMDecoder {
+    public static final int PEM_RSA_PRIVATE_KEY = 1;
+    public static final int PEM_DSA_PRIVATE_KEY = 2;
+    public static final int PEM_EC_PRIVATE_KEY = 3;
+
+    private static final int hexToInt(char c) {
+        if ((c >= 'a') && (c <= 'f')) {
+            return (c - 'a') + 10;
+        }
+
+        if ((c >= 'A') && (c <= 'F')) {
+            return (c - 'A') + 10;
+        }
+
+        if ((c >= '0') && (c <= '9')) {
+            return (c - '0');
+        }
+
+        throw new IllegalArgumentException("Need hex char");
+    }
+
+    private static byte[] hexToByteArray(String hex) {
+        if (hex == null)
+            throw new IllegalArgumentException("null argument");
+
+        if ((hex.length() % 2) != 0)
+            throw new IllegalArgumentException("Uneven string length in hex encoding.");
+
+        byte decoded[] = new byte[hex.length() / 2];
+
+        for (int i = 0; i < decoded.length; i++) {
+            int hi = hexToInt(hex.charAt(i * 2));
+            int lo = hexToInt(hex.charAt((i * 2) + 1));
+            decoded[i] = (byte)(hi * 16 + lo);
+        }
+
+        return decoded;
+    }
+
+    private static byte[] generateKeyFromPasswordSaltWithMD5(byte[] password, byte[] salt, int keyLen)
+    throws IOException {
+        if (salt.length < 8)
+            throw new IllegalArgumentException("Salt needs to be at least 8 bytes for key generation.");
+
+        MessageDigest md5;
+
+        try {
+            md5 = MessageDigest.getInstance("MD5");
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new IllegalArgumentException("VM does not support MD5", e);
+        }
+
+        byte[] key = new byte[keyLen];
+        byte[] tmp = new byte[md5.getDigestLength()];
+
+        while (true) {
+            md5.update(password, 0, password.length);
+            md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the
+            // salt in this step.
+            // This took me two hours until I got AES-xxx running.
+            int copy = (keyLen < tmp.length) ? keyLen : tmp.length;
+
+            try {
+                md5.digest(tmp, 0, tmp.length);
+            }
+            catch (DigestException e) {
+                IOException ex = new IOException("could not digest password");
+                ex.initCause(e);
+                throw ex;
+            }
+
+            System.arraycopy(tmp, 0, key, key.length - keyLen, copy);
+            keyLen -= copy;
+
+            if (keyLen == 0)
+                return key;
+
+            md5.update(tmp, 0, tmp.length);
+        }
+    }
+
+    private static byte[] removePadding(byte[] buff, int blockSize) throws IOException {
+        /* Removes RFC 1423/PKCS #7 padding */
+        int rfc_1423_padding = buff[buff.length - 1] & 0xff;
+
+        if ((rfc_1423_padding < 1) || (rfc_1423_padding > blockSize))
+            throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
+
+        for (int i = 2; i <= rfc_1423_padding; i++) {
+            if (buff[buff.length - i] != rfc_1423_padding)
+                throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
+        }
+
+        byte[] tmp = new byte[buff.length - rfc_1423_padding];
+        System.arraycopy(buff, 0, tmp, 0, buff.length - rfc_1423_padding);
+        return tmp;
+    }
+
+    public static final PEMStructure parsePEM(char[] pem) throws IOException {
+        PEMStructure ps = new PEMStructure();
+        String line = null;
+        BufferedReader br = new BufferedReader(new CharArrayReader(pem));
+        String endLine = null;
+
+        while (true) {
+            line = br.readLine();
+
+            if (line == null)
+                throw new IOException("Invalid PEM structure, '-----BEGIN...' missing");
+
+            line = line.trim();
+
+            if (line.startsWith("-----BEGIN DSA PRIVATE KEY-----")) {
+                endLine = "-----END DSA PRIVATE KEY-----";
+                ps.pemType = PEM_DSA_PRIVATE_KEY;
+                break;
+            }
+
+            if (line.startsWith("-----BEGIN RSA PRIVATE KEY-----")) {
+                endLine = "-----END RSA PRIVATE KEY-----";
+                ps.pemType = PEM_RSA_PRIVATE_KEY;
+                break;
+            }
+
+            if (line.startsWith("-----BEGIN EC PRIVATE KEY-----")) {
+                endLine = "-----END EC PRIVATE KEY-----";
+                ps.pemType = PEM_EC_PRIVATE_KEY;
+                break;
+            }
+        }
+
+        while (true) {
+            line = br.readLine();
+
+            if (line == null)
+                throw new IOException("Invalid PEM structure, " + endLine + " missing");
+
+            line = line.trim();
+            int sem_idx = line.indexOf(':');
+
+            if (sem_idx == -1)
+                break;
+
+            String name = line.substring(0, sem_idx + 1);
+            String value = line.substring(sem_idx + 1);
+            String values[] = value.split(",");
+
+            for (int i = 0; i < values.length; i++)
+                values[i] = values[i].trim();
+
+            // Proc-Type: 4,ENCRYPTED
+            // DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483
+
+            if ("Proc-Type:".equals(name)) {
+                ps.procType = values;
+                continue;
+            }
+
+            if ("DEK-Info:".equals(name)) {
+                ps.dekInfo = values;
+                continue;
+            }
+
+            /* Ignore line */
+        }
+
+        StringBuffer keyData = new StringBuffer();
+
+        while (true) {
+            if (line == null)
+                throw new IOException("Invalid PEM structure, " + endLine + " missing");
+
+            line = line.trim();
+
+            if (line.startsWith(endLine))
+                break;
+
+            keyData.append(line);
+            line = br.readLine();
+        }
+
+        char[] pem_chars = new char[keyData.length()];
+        keyData.getChars(0, pem_chars.length, pem_chars, 0);
+        ps.data = Base64.decode(pem_chars);
+
+        if (ps.data.length == 0)
+            throw new IOException("Invalid PEM structure, no data available");
+
+        return ps;
+    }
+
+    private static final void decryptPEM(PEMStructure ps, byte[] pw) throws IOException {
+        if (ps.dekInfo == null)
+            throw new IOException("Broken PEM, no mode and salt given, but encryption enabled");
+
+        if (ps.dekInfo.length != 2)
+            throw new IOException("Broken PEM, DEK-Info is incomplete!");
+
+        String algo = ps.dekInfo[0];
+        byte[] salt = hexToByteArray(ps.dekInfo[1]);
+        BlockCipher bc = null;
+
+        if (algo.equals("DES-EDE3-CBC")) {
+            DESede des3 = new DESede();
+            des3.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
+            bc = new CBCMode(des3, salt, false);
+        }
+        else if (algo.equals("DES-CBC")) {
+            DES des = new DES();
+            des.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 8));
+            bc = new CBCMode(des, salt, false);
+        }
+        else if (algo.equals("AES-128-CBC")) {
+            AES aes = new AES();
+            aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 16));
+            bc = new CBCMode(aes, salt, false);
+        }
+        else if (algo.equals("AES-192-CBC")) {
+            AES aes = new AES();
+            aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
+            bc = new CBCMode(aes, salt, false);
+        }
+        else if (algo.equals("AES-256-CBC")) {
+            AES aes = new AES();
+            aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 32));
+            bc = new CBCMode(aes, salt, false);
+        }
+        else {
+            throw new IOException("Cannot decrypt PEM structure, unknown cipher " + algo);
+        }
+
+        if ((ps.data.length % bc.getBlockSize()) != 0)
+            throw new IOException("Invalid PEM structure, size of encrypted block is not a multiple of "
+                                  + bc.getBlockSize());
+
+        /* Now decrypt the content */
+        byte[] dz = new byte[ps.data.length];
+
+        for (int i = 0; i < ps.data.length / bc.getBlockSize(); i++) {
+            bc.transformBlock(ps.data, i * bc.getBlockSize(), dz, i * bc.getBlockSize());
+        }
+
+        /* Now check and remove RFC 1423/PKCS #7 padding */
+        dz = removePadding(dz, bc.getBlockSize());
+        ps.data = dz;
+        ps.dekInfo = null;
+        ps.procType = null;
+    }
+
+    public static final boolean isPEMEncrypted(PEMStructure ps) throws IOException {
+        if (ps.procType == null)
+            return false;
+
+        if (ps.procType.length != 2)
+            throw new IOException("Unknown Proc-Type field.");
+
+        if ("4".equals(ps.procType[0]) == false)
+            throw new IOException("Unknown Proc-Type field (" + ps.procType[0] + ")");
+
+        if ("ENCRYPTED".equals(ps.procType[1]))
+            return true;
+
+        return false;
+    }
+
+    public static KeyPair decode(char[] pem, String password) throws IOException {
+        PEMStructure ps = parsePEM(pem);
+        return decode(ps, password);
+    }
+
+    public static KeyPair decode(PEMStructure ps, String password) throws IOException {
+        if (isPEMEncrypted(ps)) {
+            if (password == null)
+                throw new IOException("PEM is encrypted, but no password was specified");
+
+            decryptPEM(ps, password.getBytes("ISO-8859-1"));
+        }
+
+        if (ps.pemType == PEM_DSA_PRIVATE_KEY) {
+            SimpleDERReader dr = new SimpleDERReader(ps.data);
+            byte[] seq = dr.readSequenceAsByteArray();
+
+            if (dr.available() != 0)
+                throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
+
+            dr.resetInput(seq);
+            BigInteger version = dr.readInt();
+
+            if (version.compareTo(BigInteger.ZERO) != 0)
+                throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream.");
+
+            BigInteger p = dr.readInt();
+            BigInteger q = dr.readInt();
+            BigInteger g = dr.readInt();
+            BigInteger y = dr.readInt();
+            BigInteger x = dr.readInt();
+
+            if (dr.available() != 0)
+                throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
+
+            DSAPrivateKeySpec privSpec = new DSAPrivateKeySpec(x, p, q, g);
+            DSAPublicKeySpec pubSpec = new DSAPublicKeySpec(y, p, q, g);
+            return generateKeyPair("DSA", privSpec, pubSpec);
+        }
+
+        if (ps.pemType == PEM_RSA_PRIVATE_KEY) {
+            SimpleDERReader dr = new SimpleDERReader(ps.data);
+            byte[] seq = dr.readSequenceAsByteArray();
+
+            if (dr.available() != 0)
+                throw new IOException("Padding in RSA PRIVATE KEY DER stream.");
+
+            dr.resetInput(seq);
+            BigInteger version = dr.readInt();
+
+            if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0))
+                throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream.");
+
+            BigInteger n = dr.readInt();
+            BigInteger e = dr.readInt();
+            BigInteger d = dr.readInt();
+            // TODO: is this right?
+            BigInteger primeP = dr.readInt();
+            BigInteger primeQ = dr.readInt();
+            BigInteger expP = dr.readInt();
+            BigInteger expQ = dr.readInt();
+            BigInteger coeff = dr.readInt();
+            RSAPrivateKeySpec privSpec = new RSAPrivateCrtKeySpec(n, e, d, primeP, primeQ, expP, expQ, coeff);
+            RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(n, e);
+            return generateKeyPair("RSA", privSpec, pubSpec);
+        }
+
+        if (ps.pemType == PEM_EC_PRIVATE_KEY) {
+            SimpleDERReader dr = new SimpleDERReader(ps.data);
+            byte[] seq = dr.readSequenceAsByteArray();
+
+            if (dr.available() != 0)
+                throw new IOException("Padding in EC PRIVATE KEY DER stream.");
+
+            dr.resetInput(seq);
+            BigInteger version = dr.readInt();
+
+            if ((version.compareTo(BigInteger.ONE) != 0))
+                throw new IOException("Wrong version (" + version + ") in EC PRIVATE KEY DER stream.");
+
+            byte[] privateBytes = dr.readOctetString();
+            String curveOid = null;
+            byte[] publicBytes = null;
+
+            while (dr.available() > 0) {
+                int type = dr.readConstructedType();
+                SimpleDERReader cr = dr.readConstructed();
+
+                switch (type) {
+                    case 0:
+                        curveOid = cr.readOid();
+                        break;
+
+                    case 1:
+                        publicBytes = cr.readOctetString();
+                        break;
+                }
+            }
+
+            ECParameterSpec params = ECDSASHA2Verify.getCurveForOID(curveOid);
+
+            if (params == null)
+                throw new IOException("invalid OID");
+
+            BigInteger s = new BigInteger(privateBytes);
+            byte[] publicBytesSlice = new byte[publicBytes.length - 1];
+            System.arraycopy(publicBytes, 1, publicBytesSlice, 0, publicBytesSlice.length);
+            ECPoint w = ECDSASHA2Verify.decodeECPoint(publicBytesSlice, params.getCurve());
+            ECPrivateKeySpec privSpec = new ECPrivateKeySpec(s, params);
+            ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, params);
+            return generateKeyPair("EC", privSpec, pubSpec);
+        }
+
+        throw new IOException("PEM problem: it is of unknown type");
+    }
+
+    /**
+     * Generate a {@code KeyPair} given an {@code algorithm} and {@code KeySpec}.
+     */
+    private static KeyPair generateKeyPair(String algorithm, KeySpec privSpec, KeySpec pubSpec)
+    throws IOException {
+        try {
+            final KeyFactory kf = KeyFactory.getInstance(algorithm);
+            final PublicKey pubKey = kf.generatePublic(pubSpec);
+            final PrivateKey privKey = kf.generatePrivate(privSpec);
+            return new KeyPair(pubKey, privKey);
+        }
+        catch (NoSuchAlgorithmException ex) {
+            IOException ioex = new IOException();
+            ioex.initCause(ex);
+            throw ioex;
+        }
+        catch (InvalidKeySpecException ex) {
+            IOException ioex = new IOException("invalid keyspec");
+            ioex.initCause(ex);
+            throw ioex;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/PEMDecryptException.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2006-2014 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+import java.io.IOException;
+
+/**
+ * @version $Id: PEMDecryptException.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class PEMDecryptException extends IOException {
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    public PEMDecryptException(String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/PEMStructure.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+/**
+ * Parsed PEM structure.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class PEMStructure {
+    public int pemType;
+    String dekInfo[];
+    String procType[];
+    byte[] data;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/SimpleDERReader.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,205 @@
+package ch.ethz.ssh2.crypto;
+
+import java.io.IOException;
+
+import java.math.BigInteger;
+
+/**
+ * SimpleDERReader.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: SimpleDERReader.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class SimpleDERReader {
+    private static final int CONSTRUCTED = 0x20;
+
+    byte[] buffer;
+    int pos;
+    int count;
+
+    public SimpleDERReader(byte[] b) {
+        resetInput(b);
+    }
+
+    public SimpleDERReader(byte[] b, int off, int len) {
+        resetInput(b, off, len);
+    }
+
+    public void resetInput(byte[] b) {
+        resetInput(b, 0, b.length);
+    }
+
+    public void resetInput(byte[] b, int off, int len) {
+        buffer = b;
+        pos = off;
+        count = len;
+    }
+
+    private byte readByte() throws IOException {
+        if (count <= 0)
+            throw new IOException("DER byte array: out of data");
+
+        count--;
+        return buffer[pos++];
+    }
+
+    private byte[] readBytes(int len) throws IOException {
+        if (len > count)
+            throw new IOException("DER byte array: out of data");
+
+        byte[] b = new byte[len];
+        System.arraycopy(buffer, pos, b, 0, len);
+        pos += len;
+        count -= len;
+        return b;
+    }
+
+    public int available() {
+        return count;
+    }
+
+    private int readLength() throws IOException {
+        int len = readByte() & 0xff;
+
+        if ((len & 0x80) == 0)
+            return len;
+
+        int remain = len & 0x7F;
+
+        if (remain == 0)
+            return -1;
+
+        len = 0;
+
+        while (remain > 0) {
+            len = len << 8;
+            len = len | (readByte() & 0xff);
+            remain--;
+        }
+
+        return len;
+    }
+
+    public int ignoreNextObject() throws IOException {
+        int type = readByte() & 0xff;
+        int len = readLength();
+
+        if ((len < 0) || len > available())
+            throw new IOException("Illegal len in DER object (" + len  + ")");
+
+        readBytes(len);
+        return type;
+    }
+
+    public BigInteger readInt() throws IOException {
+        int type = readByte() & 0xff;
+
+        if (type != 0x02)
+            throw new IOException("Expected DER Integer, but found type " + type);
+
+        int len = readLength();
+
+        if ((len < 0) || len > available())
+            throw new IOException("Illegal len in DER object (" + len  + ")");
+
+        byte[] b = readBytes(len);
+        BigInteger bi = new BigInteger(b);
+        return bi;
+    }
+
+    public int readConstructedType() throws IOException {
+        int type = readByte() & 0xff;
+
+        if ((type & CONSTRUCTED) != CONSTRUCTED)
+            throw new IOException("Expected constructed type, but was " + type);
+
+        return type & 0x1f;
+    }
+
+    public SimpleDERReader readConstructed() throws IOException {
+        int len = readLength();
+
+        if ((len < 0) || len > available())
+            throw new IOException("Illegal len in DER object (" + len  + ")");
+
+        SimpleDERReader cr = new SimpleDERReader(buffer, pos, len);
+        pos += len;
+        count -= len;
+        return cr;
+    }
+
+    public byte[] readSequenceAsByteArray() throws IOException {
+        int type = readByte() & 0xff;
+
+        if (type != 0x30)
+            throw new IOException("Expected DER Sequence, but found type " + type);
+
+        int len = readLength();
+
+        if ((len < 0) || len > available())
+            throw new IOException("Illegal len in DER object (" + len  + ")");
+
+        byte[] b = readBytes(len);
+        return b;
+    }
+
+    public String readOid() throws IOException {
+        int type = readByte() & 0xff;
+
+        if (type != 0x06)
+            throw new IOException("Expected DER OID, but found type " + type);
+
+        int len = readLength();
+
+        if ((len < 1) || len > available())
+            throw new IOException("Illegal len in DER object (" + len  + ")");
+
+        byte[] b = readBytes(len);
+        long value = 0;
+        StringBuilder sb = new StringBuilder(64);
+
+        switch (b[0] / 40) {
+            case 0:
+                sb.append('0');
+                break;
+
+            case 1:
+                sb.append('1');
+                b[0] -= 40;
+                break;
+
+            default:
+                sb.append('2');
+                b[0] -= 80;
+                break;
+        }
+
+        for (int i = 0; i < len; i++) {
+            value = (value << 7) + (b[i] & 0x7F);
+
+            if ((b[i] & 0x80) == 0) {
+                sb.append('.');
+                sb.append(value);
+                value = 0;
+            }
+        }
+
+        return sb.toString();
+    }
+
+    public byte[] readOctetString() throws IOException {
+        int type = readByte() & 0xff;
+
+        if (type != 0x04 && type != 0x03)
+            throw new IOException("Expected DER Octetstring, but found type " + type);
+
+        int len = readLength();
+
+        if ((len < 0) || len > available())
+            throw new IOException("Illegal len in DER object (" + len  + ")");
+
+        byte[] b = readBytes(len);
+        return b;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/AES.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,661 @@
+package ch.ethz.ssh2.crypto.cipher;
+
+/*
+ This file was shamelessly taken from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * An implementation of the AES (Rijndael), from FIPS-197.
+ * <p>
+ * For further details see: <a
+ * href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/
+ * </a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper
+ * and C code at <a
+ * href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/
+ * </a>
+ *
+ * There are three levels of tradeoff of speed vs memory Because java has no
+ * preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4
+ * 256 word tables for encryption and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a
+ * total of 2Kbytes, adding 12 rotate operations per round to compute the values
+ * contained in the other tables from the contents of the first
+ *
+ * The slowest version uses no static tables at all and computes the values in
+ * each round
+ * <p>
+ * This file contains the fast version with 8Kbytes of static tables for round
+ * precomputation
+ *
+ * @author See comments in the source file
+ * @version 2.50, 03/15/10
+ */
+public class AES implements BlockCipher {
+    // The S box
+    private static final byte[] S = { (byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107,
+                                      (byte) 111, (byte) 197, (byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171,
+                                      (byte) 118, (byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240,
+                                      (byte) 173, (byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192, (byte) 183,
+                                      (byte) 253, (byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204, (byte) 52, (byte) 165,
+                                      (byte) 229, (byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21, (byte) 4, (byte) 199, (byte) 35,
+                                      (byte) 195, (byte) 24, (byte) 150, (byte) 5, (byte) 154, (byte) 7, (byte) 18, (byte) 128, (byte) 226,
+                                      (byte) 235, (byte) 39, (byte) 178, (byte) 117, (byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27,
+                                      (byte) 110, (byte) 90, (byte) 160, (byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227,
+                                      (byte) 47, (byte) 132, (byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177,
+                                      (byte) 91, (byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207,
+                                      (byte) 208, (byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133, (byte) 69,
+                                      (byte) 249, (byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168, (byte) 81, (byte) 163,
+                                      (byte) 64, (byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245, (byte) 188, (byte) 182, (byte) 218,
+                                      (byte) 33, (byte) 16, (byte) 255, (byte) 243, (byte) 210, (byte) 205, (byte) 12, (byte) 19, (byte) 236,
+                                      (byte) 95, (byte) 151, (byte) 68, (byte) 23, (byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100,
+                                      (byte) 93, (byte) 25, (byte) 115, (byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42,
+                                      (byte) 144, (byte) 136, (byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11,
+                                      (byte) 219, (byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92,
+                                      (byte) 194, (byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121, (byte) 231,
+                                      (byte) 200, (byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169, (byte) 108, (byte) 86,
+                                      (byte) 244, (byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8, (byte) 186, (byte) 120, (byte) 37,
+                                      (byte) 46, (byte) 28, (byte) 166, (byte) 180, (byte) 198, (byte) 232, (byte) 221, (byte) 116, (byte) 31,
+                                      (byte) 75, (byte) 189, (byte) 139, (byte) 138, (byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72,
+                                      (byte) 3, (byte) 246, (byte) 14, (byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193,
+                                      (byte) 29, (byte) 158, (byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142,
+                                      (byte) 148, (byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223,
+                                      (byte) 140, (byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104, (byte) 65,
+                                      (byte) 153, (byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22,
+                                    };
+
+    // The inverse S-box
+    private static final byte[] Si = { (byte) 82, (byte) 9, (byte) 106, (byte) 213, (byte) 48, (byte) 54, (byte) 165,
+                                       (byte) 56, (byte) 191, (byte) 64, (byte) 163, (byte) 158, (byte) 129, (byte) 243, (byte) 215, (byte) 251,
+                                       (byte) 124, (byte) 227, (byte) 57, (byte) 130, (byte) 155, (byte) 47, (byte) 255, (byte) 135, (byte) 52,
+                                       (byte) 142, (byte) 67, (byte) 68, (byte) 196, (byte) 222, (byte) 233, (byte) 203, (byte) 84, (byte) 123,
+                                       (byte) 148, (byte) 50, (byte) 166, (byte) 194, (byte) 35, (byte) 61, (byte) 238, (byte) 76, (byte) 149,
+                                       (byte) 11, (byte) 66, (byte) 250, (byte) 195, (byte) 78, (byte) 8, (byte) 46, (byte) 161, (byte) 102,
+                                       (byte) 40, (byte) 217, (byte) 36, (byte) 178, (byte) 118, (byte) 91, (byte) 162, (byte) 73, (byte) 109,
+                                       (byte) 139, (byte) 209, (byte) 37, (byte) 114, (byte) 248, (byte) 246, (byte) 100, (byte) 134, (byte) 104,
+                                       (byte) 152, (byte) 22, (byte) 212, (byte) 164, (byte) 92, (byte) 204, (byte) 93, (byte) 101, (byte) 182,
+                                       (byte) 146, (byte) 108, (byte) 112, (byte) 72, (byte) 80, (byte) 253, (byte) 237, (byte) 185, (byte) 218,
+                                       (byte) 94, (byte) 21, (byte) 70, (byte) 87, (byte) 167, (byte) 141, (byte) 157, (byte) 132, (byte) 144,
+                                       (byte) 216, (byte) 171, (byte) 0, (byte) 140, (byte) 188, (byte) 211, (byte) 10, (byte) 247, (byte) 228,
+                                       (byte) 88, (byte) 5, (byte) 184, (byte) 179, (byte) 69, (byte) 6, (byte) 208, (byte) 44, (byte) 30,
+                                       (byte) 143, (byte) 202, (byte) 63, (byte) 15, (byte) 2, (byte) 193, (byte) 175, (byte) 189, (byte) 3,
+                                       (byte) 1, (byte) 19, (byte) 138, (byte) 107, (byte) 58, (byte) 145, (byte) 17, (byte) 65, (byte) 79,
+                                       (byte) 103, (byte) 220, (byte) 234, (byte) 151, (byte) 242, (byte) 207, (byte) 206, (byte) 240, (byte) 180,
+                                       (byte) 230, (byte) 115, (byte) 150, (byte) 172, (byte) 116, (byte) 34, (byte) 231, (byte) 173, (byte) 53,
+                                       (byte) 133, (byte) 226, (byte) 249, (byte) 55, (byte) 232, (byte) 28, (byte) 117, (byte) 223, (byte) 110,
+                                       (byte) 71, (byte) 241, (byte) 26, (byte) 113, (byte) 29, (byte) 41, (byte) 197, (byte) 137, (byte) 111,
+                                       (byte) 183, (byte) 98, (byte) 14, (byte) 170, (byte) 24, (byte) 190, (byte) 27, (byte) 252, (byte) 86,
+                                       (byte) 62, (byte) 75, (byte) 198, (byte) 210, (byte) 121, (byte) 32, (byte) 154, (byte) 219, (byte) 192,
+                                       (byte) 254, (byte) 120, (byte) 205, (byte) 90, (byte) 244, (byte) 31, (byte) 221, (byte) 168, (byte) 51,
+                                       (byte) 136, (byte) 7, (byte) 199, (byte) 49, (byte) 177, (byte) 18, (byte) 16, (byte) 89, (byte) 39,
+                                       (byte) 128, (byte) 236, (byte) 95, (byte) 96, (byte) 81, (byte) 127, (byte) 169, (byte) 25, (byte) 181,
+                                       (byte) 74, (byte) 13, (byte) 45, (byte) 229, (byte) 122, (byte) 159, (byte) 147, (byte) 201, (byte) 156,
+                                       (byte) 239, (byte) 160, (byte) 224, (byte) 59, (byte) 77, (byte) 174, (byte) 42, (byte) 245, (byte) 176,
+                                       (byte) 200, (byte) 235, (byte) 187, (byte) 60, (byte) 131, (byte) 83, (byte) 153, (byte) 97, (byte) 23,
+                                       (byte) 43, (byte) 4, (byte) 126, (byte) 186, (byte) 119, (byte) 214, (byte) 38, (byte) 225, (byte) 105,
+                                       (byte) 20, (byte) 99, (byte) 85, (byte) 33, (byte) 12, (byte) 125,
+                                     };
+
+    // vector used in calculating key schedule (powers of x in GF(256))
+    private static final int[] rcon = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab,
+                                        0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91
+                                      };
+
+    // precomputation tables of calculations for rounds
+    private static final int[] T0 = { 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6,
+                                      0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
+                                      0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
+                                      0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775,
+                                      0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551,
+                                      0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, 0x65232346,
+                                      0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+                                      0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36,
+                                      0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd,
+                                      0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179,
+                                      0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
+                                      0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
+                                      0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d,
+                                      0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf,
+                                      0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
+                                      0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8,
+                                      0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
+                                      0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16,
+                                      0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
+                                      0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5,
+                                      0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac,
+                                      0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
+                                      0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
+                                      0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890,
+                                      0x05030306, 0x01f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199,
+                                      0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07,
+                                      0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+                                      0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182,
+                                      0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c
+                                    };
+
+    private static final int[] T1 = { 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd,
+                                      0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
+                                      0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
+                                      0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2,
+                                      0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4,
+                                      0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, 0x23234665,
+                                      0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
+                                      0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d,
+                                      0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e,
+                                      0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8,
+                                      0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
+                                      0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf,
+                                      0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe,
+                                      0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75,
+                                      0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f,
+                                      0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac,
+                                      0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
+                                      0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d,
+                                      0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
+                                      0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532,
+                                      0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa,
+                                      0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f,
+                                      0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21,
+                                      0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8,
+                                      0x03030605, 0xf6f6f701, 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958,
+                                      0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789,
+                                      0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
+                                      0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3,
+                                      0x999929b0, 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a
+                                    };
+
+    private static final int[] T2 = { 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b,
+                                      0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
+                                      0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
+                                      0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7,
+                                      0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5,
+                                      0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, 0x23466523,
+                                      0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
+                                      0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b,
+                                      0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3,
+                                      0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1,
+                                      0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
+                                      0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45,
+                                      0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3,
+                                      0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da,
+                                      0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec,
+                                      0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64,
+                                      0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
+                                      0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b,
+                                      0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
+                                      0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7,
+                                      0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56,
+                                      0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25,
+                                      0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f,
+                                      0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848,
+                                      0x03060503, 0xf6f701f6, 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1,
+                                      0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e,
+                                      0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
+                                      0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341,
+                                      0x9929b099, 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16
+                                    };
+
+    private static final int[] T3 = { 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b,
+                                      0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
+                                      0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
+                                      0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7,
+                                      0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5,
+                                      0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, 0x46652323,
+                                      0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
+                                      0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b,
+                                      0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3,
+                                      0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1,
+                                      0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
+                                      0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545,
+                                      0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3,
+                                      0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada,
+                                      0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec,
+                                      0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464,
+                                      0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
+                                      0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b,
+                                      0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
+                                      0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7,
+                                      0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656,
+                                      0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525,
+                                      0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f,
+                                      0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848,
+                                      0x06050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1,
+                                      0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x07898e8e,
+                                      0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
+                                      0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141,
+                                      0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616
+                                    };
+
+    private static final int[] Tinv0 = { 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f,
+                                         0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
+                                         0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b,
+                                         0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275,
+                                         0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5,
+                                         0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94,
+                                         0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+                                         0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65,
+                                         0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040,
+                                         0x069f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x055dc471, 0x6fd40604,
+                                         0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879,
+                                         0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
+                                         0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793,
+                                         0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0x0aba93e2, 0xe52aa0c0, 0x43e0223c,
+                                         0x1d171b12, 0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3,
+                                         0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7,
+                                         0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
+                                         0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56,
+                                         0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f,
+                                         0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f,
+                                         0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, 0xf418596e,
+                                         0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
+                                         0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733,
+                                         0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e,
+                                         0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92,
+                                         0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1,
+                                         0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+                                         0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839,
+                                         0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0
+                                       };
+
+    private static final int[] Tinv1 = { 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1,
+                                         0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
+                                         0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6,
+                                         0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a,
+                                         0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582,
+                                         0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487,
+                                         0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
+                                         0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd,
+                                         0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa,
+                                         0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f,
+                                         0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db,
+                                         0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb,
+                                         0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f,
+                                         0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43,
+                                         0x171b121d, 0x0d090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, 0xdd99eebb, 0x607fa3fd,
+                                         0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca,
+                                         0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3,
+                                         0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8,
+                                         0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4,
+                                         0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe,
+                                         0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4,
+                                         0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4,
+                                         0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315,
+                                         0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x0496e4df, 0xb5d19ee3,
+                                         0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252,
+                                         0x5610e933, 0x47d66d13, 0x61d79a8c, 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed,
+                                         0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
+                                         0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971,
+                                         0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042
+                                       };
+
+    private static final int[] Tinv2 = { 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145,
+                                         0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
+                                         0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9,
+                                         0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89,
+                                         0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231,
+                                         0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, 0xde94876c,
+                                         0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
+                                         0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4,
+                                         0x0506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef,
+                                         0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x06046fd4,
+                                         0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee,
+                                         0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff,
+                                         0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7,
+                                         0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0,
+                                         0x1b121d17, 0x090e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60,
+                                         0x01f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc,
+                                         0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330,
+                                         0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c,
+                                         0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf,
+                                         0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80,
+                                         0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418,
+                                         0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409,
+                                         0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8,
+                                         0x04f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5,
+                                         0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, 0x67b35a1d, 0xdb9252d2,
+                                         0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5,
+                                         0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
+                                         0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101,
+                                         0x0c08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257
+                                       };
+
+    private static final int[] Tinv3 = { 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d,
+                                         0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
+                                         0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3,
+                                         0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2,
+                                         0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a,
+                                         0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde,
+                                         0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
+                                         0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da,
+                                         0x06d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6, 0x0b39ec83, 0x40aaef60,
+                                         0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x046fd406,
+                                         0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8,
+                                         0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e,
+                                         0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757,
+                                         0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022,
+                                         0x121d171b, 0x0e0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f,
+                                         0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31,
+                                         0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2,
+                                         0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d,
+                                         0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad,
+                                         0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d,
+                                         0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859,
+                                         0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f,
+                                         0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7,
+                                         0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1,
+                                         0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db,
+                                         0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c,
+                                         0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
+                                         0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8,
+                                         0x08deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8
+                                       };
+
+    private int shift(int r, int shift) {
+        return (((r >>> shift) | (r << (32 - shift))));
+    }
+
+    /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+    private static final int m1 = 0x80808080;
+    private static final int m2 = 0x7f7f7f7f;
+    private static final int m3 = 0x0000001b;
+
+    private int FFmulX(int x) {
+        return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
+    }
+
+    /*
+     * The following defines provide alternative definitions of FFmulX that
+     * might give improved performance if a fast 32-bit multiply is not
+     * available.
+     *
+     * private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x &
+     * m2) < < 1) ^ ((u >>> 3) | (u >>> 6)); } private static final int m4 =
+     * 0x1b1b1b1b; private int FFmulX(int x) { int u = x & m1; return ((x & m2) < <
+     * 1) ^ ((u - (u >>> 7)) & m4); }
+     *
+     */
+
+    private int inv_mcol(int x) {
+        int f2 = FFmulX(x);
+        int f4 = FFmulX(f2);
+        int f8 = FFmulX(f4);
+        int f9 = x ^ f8;
+        return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
+    }
+
+    private int subWord(int x) {
+        return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16) | S[(x >> 24) & 255] << 24);
+    }
+
+    /**
+     * Calculate the necessary round keys The number of calculations depends on
+     * key size and block size AES specified a fixed block size of 128 bits and
+     * key sizes 128/192/256 bits This code is written assuming those are the
+     * only possible values
+     */
+    private int[][] generateWorkingKey(byte[] key, boolean forEncryption) {
+        int KC = key.length / 4; // key length in words
+        int t;
+
+        if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length)) {
+            throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+        }
+
+        ROUNDS = KC + 6; // This is not always true for the generalized
+        // Rijndael that allows larger block sizes
+        int[][] W = new int[ROUNDS + 1][4]; // 4 words in a block
+        //
+        // copy the key into the round key array
+        //
+        t = 0;
+
+        for (int i = 0; i < key.length; t++) {
+            W[t >> 2][t & 3] = (key[i] & 0xff) | ((key[i + 1] & 0xff) << 8) | ((key[i + 2] & 0xff) << 16)
+                               | (key[i + 3] << 24);
+            i += 4;
+        }
+
+        //
+        // while not enough round key material calculated
+        // calculate new values
+        //
+        int k = (ROUNDS + 1) << 2;
+
+        for (int i = KC; (i < k); i++) {
+            int temp = W[(i - 1) >> 2][(i - 1) & 3];
+
+            if ((i % KC) == 0) {
+                temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1];
+            }
+            else if ((KC > 6) && ((i % KC) == 4)) {
+                temp = subWord(temp);
+            }
+
+            W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp;
+        }
+
+        if (!forEncryption) {
+            for (int j = 1; j < ROUNDS; j++) {
+                for (int i = 0; i < 4; i++) {
+                    W[j][i] = inv_mcol(W[j][i]);
+                }
+            }
+        }
+
+        return W;
+    }
+
+    private int ROUNDS;
+    private int[][] WorkingKey = null;
+    private int C0, C1, C2, C3;
+    private boolean doEncrypt;
+
+    private static final int BLOCK_SIZE = 16;
+
+    /**
+     * default constructor - 128 bit block size.
+     */
+    public AES() {
+    }
+
+    /**
+     * initialise an AES cipher.
+     *
+     * @param forEncryption
+     *            whether or not we are for encryption.
+     * @param key
+     *            the key required to set up the cipher.
+     * @exception IllegalArgumentException
+     *                if the params argument is inappropriate.
+     */
+
+    public final void init(boolean forEncryption, byte[] key) {
+        WorkingKey = generateWorkingKey(key, forEncryption);
+        this.doEncrypt = forEncryption;
+    }
+
+    public final String getAlgorithmName() {
+        return "AES";
+    }
+
+    public final int getBlockSize() {
+        return BLOCK_SIZE;
+    }
+
+    public final int processBlock(byte[] in, int inOff, byte[] out, int outOff) {
+        if (WorkingKey == null) {
+            throw new IllegalStateException("AES engine not initialised");
+        }
+
+        if ((inOff + (32 / 2)) > in.length) {
+            throw new IllegalArgumentException("input buffer too short");
+        }
+
+        if ((outOff + (32 / 2)) > out.length) {
+            throw new IllegalArgumentException("output buffer too short");
+        }
+
+        if (doEncrypt) {
+            unpackBlock(in, inOff);
+            encryptBlock(WorkingKey);
+            packBlock(out, outOff);
+        }
+        else {
+            unpackBlock(in, inOff);
+            decryptBlock(WorkingKey);
+            packBlock(out, outOff);
+        }
+
+        return BLOCK_SIZE;
+    }
+
+    public final void reset() {
+    }
+
+    private void unpackBlock(byte[] bytes, int off) {
+        int index = off;
+        C0 = (bytes[index++] & 0xff);
+        C0 |= (bytes[index++] & 0xff) << 8;
+        C0 |= (bytes[index++] & 0xff) << 16;
+        C0 |= bytes[index++] << 24;
+        C1 = (bytes[index++] & 0xff);
+        C1 |= (bytes[index++] & 0xff) << 8;
+        C1 |= (bytes[index++] & 0xff) << 16;
+        C1 |= bytes[index++] << 24;
+        C2 = (bytes[index++] & 0xff);
+        C2 |= (bytes[index++] & 0xff) << 8;
+        C2 |= (bytes[index++] & 0xff) << 16;
+        C2 |= bytes[index++] << 24;
+        C3 = (bytes[index++] & 0xff);
+        C3 |= (bytes[index++] & 0xff) << 8;
+        C3 |= (bytes[index++] & 0xff) << 16;
+        C3 |= bytes[index++] << 24;
+    }
+
+    private void packBlock(byte[] bytes, int off) {
+        int index = off;
+        bytes[index++] = (byte) C0;
+        bytes[index++] = (byte)(C0 >> 8);
+        bytes[index++] = (byte)(C0 >> 16);
+        bytes[index++] = (byte)(C0 >> 24);
+        bytes[index++] = (byte) C1;
+        bytes[index++] = (byte)(C1 >> 8);
+        bytes[index++] = (byte)(C1 >> 16);
+        bytes[index++] = (byte)(C1 >> 24);
+        bytes[index++] = (byte) C2;
+        bytes[index++] = (byte)(C2 >> 8);
+        bytes[index++] = (byte)(C2 >> 16);
+        bytes[index++] = (byte)(C2 >> 24);
+        bytes[index++] = (byte) C3;
+        bytes[index++] = (byte)(C3 >> 8);
+        bytes[index++] = (byte)(C3 >> 16);
+        bytes[index++] = (byte)(C3 >> 24);
+    }
+
+    private void encryptBlock(int[][] KW) {
+        int r, r0, r1, r2, r3;
+        C0 ^= KW[0][0];
+        C1 ^= KW[0][1];
+        C2 ^= KW[0][2];
+        C3 ^= KW[0][3];
+
+        for (r = 1; r < ROUNDS - 1;) {
+            r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
+            r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
+            r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
+            r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
+            C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[(r3 >> 24) & 255] ^ KW[r][0];
+            C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[(r0 >> 24) & 255] ^ KW[r][1];
+            C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[(r1 >> 24) & 255] ^ KW[r][2];
+            C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[(r2 >> 24) & 255] ^ KW[r++][3];
+        }
+
+        r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
+        r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
+        r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
+        r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
+        // the final round's table is a simple function of S so we don't use a
+        // whole other four tables for it
+        C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16)
+             ^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0];
+        C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16)
+             ^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1];
+        C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16)
+             ^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2];
+        C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16)
+             ^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3];
+    }
+
+    private void decryptBlock(int[][] KW) {
+        int r, r0, r1, r2, r3;
+        C0 ^= KW[ROUNDS][0];
+        C1 ^= KW[ROUNDS][1];
+        C2 ^= KW[ROUNDS][2];
+        C3 ^= KW[ROUNDS][3];
+
+        for (r = ROUNDS - 1; r > 1;) {
+            r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255]
+                 ^ KW[r][0];
+            r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255]
+                 ^ KW[r][1];
+            r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255]
+                 ^ KW[r][2];
+            r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255]
+                 ^ KW[r--][3];
+            C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[(r1 >> 24) & 255]
+                 ^ KW[r][0];
+            C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[(r2 >> 24) & 255]
+                 ^ KW[r][1];
+            C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[(r3 >> 24) & 255]
+                 ^ KW[r][2];
+            C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[(r0 >> 24) & 255]
+                 ^ KW[r--][3];
+        }
+
+        r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] ^ KW[r][0];
+        r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] ^ KW[r][1];
+        r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] ^ KW[r][2];
+        r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] ^ KW[r--][3];
+        // the final round's table is a simple function of Si so we don't use a
+        // whole other four tables for it
+        C0 = (Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(r2 >> 16) & 255] & 255) << 16)
+             ^ (Si[(r1 >> 24) & 255] << 24) ^ KW[0][0];
+        C1 = (Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16)
+             ^ (Si[(r2 >> 24) & 255] << 24) ^ KW[0][1];
+        C2 = (Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) ^ ((Si[(r0 >> 16) & 255] & 255) << 16)
+             ^ (Si[(r3 >> 24) & 255] << 24) ^ KW[0][2];
+        C3 = (Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) ^ ((Si[(r1 >> 16) & 255] & 255) << 16)
+             ^ (Si[(r0 >> 24) & 255] << 24) ^ KW[0][3];
+    }
+
+    public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
+        processBlock(src, srcoff, dst, dstoff);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/BlockCipher.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+/**
+ * BlockCipher.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public interface BlockCipher {
+    public void init(boolean forEncryption, byte[] key);
+
+    public int getBlockSize();
+
+    public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/BlockCipherFactory.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * BlockCipherFactory.
+ *
+ * @author Christian Plattner
+ * @version $Id: BlockCipherFactory.java 86 2014-04-07 14:15:18Z dkocher@sudo.ch $
+ */
+public class BlockCipherFactory {
+    private static final class CipherEntry {
+        String type;
+        int blocksize;
+        int keysize;
+        String cipherClass;
+
+        public CipherEntry(String type, int blockSize, int keySize, String cipherClass) {
+            this.type = type;
+            this.blocksize = blockSize;
+            this.keysize = keySize;
+            this.cipherClass = cipherClass;
+        }
+    }
+
+    private static final List<CipherEntry> ciphers = new ArrayList<CipherEntry>();
+
+    static {
+        /* Higher Priority First */
+        ciphers.add(new CipherEntry("aes128-ctr", 16, 16, "ch.ethz.ssh2.crypto.cipher.AES"));
+        ciphers.add(new CipherEntry("aes192-ctr", 16, 24, "ch.ethz.ssh2.crypto.cipher.AES"));
+        ciphers.add(new CipherEntry("aes256-ctr", 16, 32, "ch.ethz.ssh2.crypto.cipher.AES"));
+        ciphers.add(new CipherEntry("blowfish-ctr", 8, 16, "ch.ethz.ssh2.crypto.cipher.BlowFish"));
+        ciphers.add(new CipherEntry("aes128-cbc", 16, 16, "ch.ethz.ssh2.crypto.cipher.AES"));
+        ciphers.add(new CipherEntry("aes192-cbc", 16, 24, "ch.ethz.ssh2.crypto.cipher.AES"));
+        ciphers.add(new CipherEntry("aes256-cbc", 16, 32, "ch.ethz.ssh2.crypto.cipher.AES"));
+        ciphers.add(new CipherEntry("blowfish-cbc", 8, 16, "ch.ethz.ssh2.crypto.cipher.BlowFish"));
+        ciphers.add(new CipherEntry("3des-ctr", 8, 24, "ch.ethz.ssh2.crypto.cipher.DESede"));
+        ciphers.add(new CipherEntry("3des-cbc", 8, 24, "ch.ethz.ssh2.crypto.cipher.DESede"));
+    }
+
+    public static String[] getDefaultCipherList() {
+        List<String> list = new ArrayList<String>(ciphers.size());
+
+        for (CipherEntry ce : ciphers) {
+            list.add(ce.type);
+        }
+
+        return list.toArray(new String[ciphers.size()]);
+    }
+
+    public static void checkCipherList(String[] cipherCandidates) {
+        for (String cipherCandidate : cipherCandidates) {
+            getEntry(cipherCandidate);
+        }
+    }
+
+    //  @SuppressWarnings("rawtypes")
+    public static BlockCipher createCipher(String type, boolean encrypt, byte[] key, byte[] iv) {
+        try {
+            CipherEntry ce = getEntry(type);
+            Class<?> cc = Class.forName(ce.cipherClass);
+            BlockCipher bc = (BlockCipher) cc.newInstance();
+
+            if (type.endsWith("-cbc")) {
+                bc.init(encrypt, key);
+                return new CBCMode(bc, iv, encrypt);
+            }
+            else if (type.endsWith("-ctr")) {
+                bc.init(true, key);
+                return new CTRMode(bc, iv, encrypt);
+            }
+
+            throw new IllegalArgumentException("Cannot instantiate " + type);
+        }
+        catch (ClassNotFoundException e) {
+            throw new IllegalArgumentException("Cannot instantiate " + type, e);
+        }
+        catch (InstantiationException e) {
+            throw new IllegalArgumentException("Cannot instantiate " + type, e);
+        }
+        catch (IllegalAccessException e) {
+            throw new IllegalArgumentException("Cannot instantiate " + type, e);
+        }
+    }
+
+    private static CipherEntry getEntry(String type) {
+        for (CipherEntry ce : ciphers) {
+            if (ce.type.equals(type)) {
+                return ce;
+            }
+        }
+
+        throw new IllegalArgumentException("Unkown algorithm " + type);
+    }
+
+    public static int getBlockSize(String type) {
+        CipherEntry ce = getEntry(type);
+        return ce.blocksize;
+    }
+
+    public static int getKeySize(String type) {
+        CipherEntry ce = getEntry(type);
+        return ce.keysize;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/BlowFish.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,376 @@
+package ch.ethz.ssh2.crypto.cipher;
+
+/*
+ This file was shamelessly taken from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * A class that provides Blowfish key encryption operations, such as encoding
+ * data and generating keys. All the algorithms herein are from Applied
+ * Cryptography and implement a simplified cryptography interface.
+ *
+ * @author See comments in the source file
+ * @version 2.50, 03/15/10
+ */
+public class BlowFish implements BlockCipher {
+
+    private final static int[] KP = { 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0,
+                                      0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5,
+                                      0xB5470917, 0x9216D5D9, 0x8979FB1B
+                                    },
+
+                               KS0 = { 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, 0x24A19947,
+                                       0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658,
+                                       0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918,
+                                       0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
+                                       0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF,
+                                       0x7C72E993, 0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C,
+                                       0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60,
+                                       0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
+                                       0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, 0x6A51A0D2,
+                                       0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176,
+                                       0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F,
+                                       0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
+                                       0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6,
+                                       0x409F60C4, 0x5E5C9EC2, 0x196A2463, 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C,
+                                       0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39,
+                                       0xB9D3FBDB, 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
+                                       0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, 0x9E5C57BB,
+                                       0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8,
+                                       0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, 0x9A53E479, 0xB6F84565, 0xD28E49BC,
+                                       0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
+                                       0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB,
+                                       0xF2122B64, 0x8888B812, 0x900DF01C, 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777,
+                                       0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81,
+                                       0xD2ADA8D9, 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
+                                       0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, 0x2464369B,
+                                       0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9,
+                                       0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476,
+                                       0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
+                                       0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A
+                                     },
+
+                               KS1 = { 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71,
+                                       0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65,
+                                       0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26, 0x6382E9C6,
+                                       0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
+                                       0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A,
+                                       0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC,
+                                       0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1,
+                                       0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
+                                       0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, 0x7A584718,
+                                       0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908,
+                                       0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55, 0x81AC77D6,
+                                       0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
+                                       0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6,
+                                       0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D,
+                                       0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1,
+                                       0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
+                                       0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, 0xB5735C90,
+                                       0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA,
+                                       0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099, 0x95F7997E,
+                                       0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
+                                       0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF,
+                                       0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA,
+                                       0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A,
+                                       0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
+                                       0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, 0x3372F092,
+                                       0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E,
+                                       0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484, 0xFDD56705,
+                                       0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
+                                       0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7
+                                     },
+
+                               KS2 = { 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471,
+                                       0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF,
+                                       0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6,
+                                       0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
+                                       0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35,
+                                       0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332,
+                                       0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7,
+                                       0xD096954B, 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
+                                       0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE,
+                                       0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60,
+                                       0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62,
+                                       0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
+                                       0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60,
+                                       0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3,
+                                       0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF,
+                                       0x763BD6EB, 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
+                                       0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659,
+                                       0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086,
+                                       0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187,
+                                       0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
+                                       0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E,
+                                       0x20B45770, 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09,
+                                       0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F,
+                                       0x2868F169, 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
+                                       0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7,
+                                       0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188,
+                                       0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3,
+                                       0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
+                                       0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0
+                                     },
+
+                               KS3 = { 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, 0xD5118E9D,
+                                       0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79,
+                                       0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, 0x2939BBDB, 0xA9BA4650, 0xAC9526E8,
+                                       0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
+                                       0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3,
+                                       0xF752F7DA, 0x3F046F69, 0x77FA0A59, 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797,
+                                       0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472,
+                                       0x5A88F54C, 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
+                                       0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, 0xC3EB9E15,
+                                       0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5,
+                                       0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, 0x4DE81751, 0x3830DC8E, 0x379D5862,
+                                       0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
+                                       0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD,
+                                       0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB,
+                                       0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671,
+                                       0xAF537D5D, 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
+                                       0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, 0xA08839E1,
+                                       0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A,
+                                       0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF,
+                                       0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
+                                       0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532,
+                                       0xE0D392DF, 0xD3A0342B, 0x8971F21E, 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E,
+                                       0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5,
+                                       0xF6FB2299, 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
+                                       0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD,
+                                       0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3,
+                                       0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0,
+                                       0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
+                                       0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6
+                                     };
+
+    // ====================================
+    // Useful constants
+    // ====================================
+
+    private static final int ROUNDS = 16;
+    private static final int BLOCK_SIZE = 8; // bytes = 64 bits
+    private static final int SBOX_SK = 256;
+    private static final int P_SZ = ROUNDS + 2;
+
+    private final int[] S0, S1, S2, S3; // the s-boxes
+    private final int[] P; // the p-array
+
+    private boolean doEncrypt = false;
+
+    private byte[] workingKey = null;
+
+    public BlowFish() {
+        S0 = new int[SBOX_SK];
+        S1 = new int[SBOX_SK];
+        S2 = new int[SBOX_SK];
+        S3 = new int[SBOX_SK];
+        P = new int[P_SZ];
+    }
+
+    /**
+     * initialise a Blowfish cipher.
+     *
+     * @param encrypting
+     *            whether or not we are for encryption.
+     * @param key
+     *            the key required to set up the cipher.
+     * @exception IllegalArgumentException
+     *                if the params argument is inappropriate.
+     */
+    public void init(boolean encrypting, byte[] key) {
+        this.doEncrypt = encrypting;
+        this.workingKey = key;
+        setKey(this.workingKey);
+    }
+
+    public String getAlgorithmName() {
+        return "Blowfish";
+    }
+
+    public final void transformBlock(byte[] in, int inOff, byte[] out, int outOff) {
+        if (workingKey == null) {
+            throw new IllegalStateException("Blowfish not initialised");
+        }
+
+        if (doEncrypt) {
+            encryptBlock(in, inOff, out, outOff);
+        }
+        else {
+            decryptBlock(in, inOff, out, outOff);
+        }
+    }
+
+    public void reset() {
+    }
+
+    public int getBlockSize() {
+        return BLOCK_SIZE;
+    }
+
+    // ==================================
+    // Private Implementation
+    // ==================================
+
+    private int F(int x) {
+        return (((S0[(x >>> 24)] + S1[(x >>> 16) & 0xff]) ^ S2[(x >>> 8) & 0xff]) + S3[x & 0xff]);
+    }
+
+    /**
+     * apply the encryption cycle to each value pair in the table.
+     */
+    private void processTable(int xl, int xr, int[] table) {
+        int size = table.length;
+
+        for (int s = 0; s < size; s += 2) {
+            xl ^= P[0];
+
+            for (int i = 1; i < ROUNDS; i += 2) {
+                xr ^= F(xl) ^ P[i];
+                xl ^= F(xr) ^ P[i + 1];
+            }
+
+            xr ^= P[ROUNDS + 1];
+            table[s] = xr;
+            table[s + 1] = xl;
+            xr = xl; // end of cycle swap
+            xl = table[s];
+        }
+    }
+
+    private void setKey(byte[] key) {
+        /*
+         * - comments are from _Applied Crypto_, Schneier, p338 please be
+         * careful comparing the two, AC numbers the arrays from 1, the enclosed
+         * code from 0.
+         *
+         * (1) Initialise the S-boxes and the P-array, with a fixed string This
+         * string contains the hexadecimal digits of pi (3.141...)
+         */
+        System.arraycopy(KS0, 0, S0, 0, SBOX_SK);
+        System.arraycopy(KS1, 0, S1, 0, SBOX_SK);
+        System.arraycopy(KS2, 0, S2, 0, SBOX_SK);
+        System.arraycopy(KS3, 0, S3, 0, SBOX_SK);
+        System.arraycopy(KP, 0, P, 0, P_SZ);
+        /*
+         * (2) Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with
+         * the second 32-bits of the key, and so on for all bits of the key (up
+         * to P[17]). Repeatedly cycle through the key bits until the entire
+         * P-array has been XOR-ed with the key bits
+         */
+        int keyLength = key.length;
+        int keyIndex = 0;
+
+        for (int i = 0; i < P_SZ; i++) {
+            // get the 32 bits of the key, in 4 * 8 bit chunks
+            int data = 0x0000000;
+
+            for (int j = 0; j < 4; j++) {
+                // create a 32 bit block
+                data = (data << 8) | (key[keyIndex++] & 0xff);
+
+                // wrap when we get to the end of the key
+                if (keyIndex >= keyLength) {
+                    keyIndex = 0;
+                }
+            }
+
+            // XOR the newly created 32 bit chunk onto the P-array
+            P[i] ^= data;
+        }
+
+        /*
+         * (3) Encrypt the all-zero string with the Blowfish algorithm, using
+         * the subkeys described in (1) and (2)
+         *
+         * (4) Replace P1 and P2 with the output of step (3)
+         *
+         * (5) Encrypt the output of step(3) using the Blowfish algorithm, with
+         * the modified subkeys.
+         *
+         * (6) Replace P3 and P4 with the output of step (5)
+         *
+         * (7) Continue the process, replacing all elements of the P-array and
+         * then all four S-boxes in order, with the output of the continuously
+         * changing Blowfish algorithm
+         */
+        processTable(0, 0, P);
+        processTable(P[P_SZ - 2], P[P_SZ - 1], S0);
+        processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1);
+        processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2);
+        processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3);
+    }
+
+    /**
+     * Encrypt the given input starting at the given offset and place the result
+     * in the provided buffer starting at the given offset. The input will be an
+     * exact multiple of our blocksize.
+     */
+    private void encryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) {
+        int xl = BytesTo32bits(src, srcIndex);
+        int xr = BytesTo32bits(src, srcIndex + 4);
+        xl ^= P[0];
+
+        for (int i = 1; i < ROUNDS; i += 2) {
+            xr ^= F(xl) ^ P[i];
+            xl ^= F(xr) ^ P[i + 1];
+        }
+
+        xr ^= P[ROUNDS + 1];
+        Bits32ToBytes(xr, dst, dstIndex);
+        Bits32ToBytes(xl, dst, dstIndex + 4);
+    }
+
+    /**
+     * Decrypt the given input starting at the given offset and place the result
+     * in the provided buffer starting at the given offset. The input will be an
+     * exact multiple of our blocksize.
+     */
+    private void decryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) {
+        int xl = BytesTo32bits(src, srcIndex);
+        int xr = BytesTo32bits(src, srcIndex + 4);
+        xl ^= P[ROUNDS + 1];
+
+        for (int i = ROUNDS; i > 0; i -= 2) {
+            xr ^= F(xl) ^ P[i];
+            xl ^= F(xr) ^ P[i - 1];
+        }
+
+        xr ^= P[0];
+        Bits32ToBytes(xr, dst, dstIndex);
+        Bits32ToBytes(xl, dst, dstIndex + 4);
+    }
+
+    private int BytesTo32bits(byte[] b, int i) {
+        return ((b[i] & 0xff) << 24) | ((b[i + 1] & 0xff) << 16) | ((b[i + 2] & 0xff) << 8) | ((b[i + 3] & 0xff));
+    }
+
+    private void Bits32ToBytes(int in, byte[] b, int offset) {
+        b[offset + 3] = (byte) in;
+        b[offset + 2] = (byte)(in >> 8);
+        b[offset + 1] = (byte)(in >> 16);
+        b[offset] = (byte)(in >> 24);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/CBCMode.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+/**
+ * CBCMode.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class CBCMode implements BlockCipher {
+    BlockCipher tc;
+    int blockSize;
+    boolean doEncrypt;
+
+    byte[] cbc_vector;
+    byte[] tmp_vector;
+
+    public void init(boolean forEncryption, byte[] key) {
+    }
+
+    public CBCMode(BlockCipher tc, byte[] iv, boolean doEncrypt)
+    throws IllegalArgumentException {
+        this.tc = tc;
+        this.blockSize = tc.getBlockSize();
+        this.doEncrypt = doEncrypt;
+
+        if (this.blockSize != iv.length)
+            throw new IllegalArgumentException("IV must be " + blockSize
+                                               + " bytes long! (currently " + iv.length + ")");
+
+        this.cbc_vector = new byte[blockSize];
+        this.tmp_vector = new byte[blockSize];
+        System.arraycopy(iv, 0, cbc_vector, 0, blockSize);
+    }
+
+    public int getBlockSize() {
+        return blockSize;
+    }
+
+    private void encryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
+        for (int i = 0; i < blockSize; i++)
+            cbc_vector[i] ^= src[srcoff + i];
+
+        tc.transformBlock(cbc_vector, 0, dst, dstoff);
+        System.arraycopy(dst, dstoff, cbc_vector, 0, blockSize);
+    }
+
+    private void decryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
+        /* Assume the worst, src and dst are overlapping... */
+        System.arraycopy(src, srcoff, tmp_vector, 0, blockSize);
+        tc.transformBlock(src, srcoff, dst, dstoff);
+
+        for (int i = 0; i < blockSize; i++)
+            dst[dstoff + i] ^= cbc_vector[i];
+
+        /* ...that is why we need a tmp buffer. */
+        byte[] swap = cbc_vector;
+        cbc_vector = tmp_vector;
+        tmp_vector = swap;
+    }
+
+    public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
+        if (doEncrypt)
+            encryptBlock(src, srcoff, dst, dstoff);
+        else
+            decryptBlock(src, srcoff, dst, dstoff);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/CTRMode.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+/**
+ * This is CTR mode as described in draft-ietf-secsh-newmodes-XY.txt
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class CTRMode implements BlockCipher {
+    byte[] X;
+    byte[] Xenc;
+
+    BlockCipher bc;
+    int blockSize;
+    boolean doEncrypt;
+
+    int count = 0;
+
+    public void init(boolean forEncryption, byte[] key) {
+    }
+
+    public CTRMode(BlockCipher tc, byte[] iv, boolean doEnc) throws IllegalArgumentException {
+        bc = tc;
+        blockSize = bc.getBlockSize();
+        doEncrypt = doEnc;
+
+        if (blockSize != iv.length)
+            throw new IllegalArgumentException("IV must be " + blockSize + " bytes long! (currently " + iv.length + ")");
+
+        X = new byte[blockSize];
+        Xenc = new byte[blockSize];
+        System.arraycopy(iv, 0, X, 0, blockSize);
+    }
+
+    public final int getBlockSize() {
+        return blockSize;
+    }
+
+    public final void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
+        bc.transformBlock(X, 0, Xenc, 0);
+
+        for (int i = 0; i < blockSize; i++) {
+            dst[dstoff + i] = (byte)(src[srcoff + i] ^ Xenc[i]);
+        }
+
+        for (int i = (blockSize - 1); i >= 0; i--) {
+            X[i]++;
+
+            if (X[i] != 0)
+                break;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/CipherInputStream.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * CipherInputStream.
+ *
+ * @author Christian Plattner
+ * @version $Id: CipherInputStream.java 11 2011-05-27 14:14:06Z dkocher@sudo.ch $
+ */
+public class CipherInputStream {
+    BlockCipher currentCipher;
+    InputStream bi;
+    byte[] buffer;
+    byte[] enc;
+    int blockSize;
+    int pos;
+
+    /*
+     * We cannot use java.io.BufferedInputStream, since that is not available in
+     * J2ME. Everything could be improved alot here.
+     */
+
+    private static final int BUFF_SIZE = 8192;
+    byte[] input_buffer = new byte[BUFF_SIZE];
+    int input_buffer_pos = 0;
+    int input_buffer_size = 0;
+
+    public CipherInputStream(BlockCipher tc, InputStream bi) {
+        this.bi = bi;
+        changeCipher(tc);
+    }
+
+    private int fill_buffer() throws IOException {
+        input_buffer_pos = 0;
+        input_buffer_size = 0;
+        input_buffer_size = bi.read(input_buffer, 0, BUFF_SIZE);
+        return input_buffer_size;
+    }
+
+    private int internal_read(byte[] b, int off, int len) throws IOException {
+        if (input_buffer_size < 0) {
+            return -1;
+        }
+
+        if (input_buffer_pos >= input_buffer_size) {
+            if (fill_buffer() <= 0) {
+                return -1;
+            }
+        }
+
+        int avail = input_buffer_size - input_buffer_pos;
+        int thiscopy = (len > avail) ? avail : len;
+        System.arraycopy(input_buffer, input_buffer_pos, b, off, thiscopy);
+        input_buffer_pos += thiscopy;
+        return thiscopy;
+    }
+
+    public void changeCipher(BlockCipher bc) {
+        this.currentCipher = bc;
+        blockSize = bc.getBlockSize();
+        buffer = new byte[blockSize];
+        enc = new byte[blockSize];
+        pos = blockSize;
+    }
+
+    private void getBlock() throws IOException {
+        int n = 0;
+
+        while (n < blockSize) {
+            int len = internal_read(enc, n, blockSize - n);
+
+            if (len < 0) {
+                throw new IOException("Cannot read full block, EOF reached.");
+            }
+
+            n += len;
+        }
+
+        try {
+            currentCipher.transformBlock(enc, 0, buffer, 0);
+        }
+        catch (Exception e) {
+            throw new IOException("Error while decrypting block.");
+        }
+
+        pos = 0;
+    }
+
+    public int read(byte[] dst) throws IOException {
+        return read(dst, 0, dst.length);
+    }
+
+    public int read(byte[] dst, int off, int len) throws IOException {
+        int count = 0;
+
+        while (len > 0) {
+            if (pos >= blockSize) {
+                getBlock();
+            }
+
+            int avail = blockSize - pos;
+            int copy = Math.min(avail, len);
+            System.arraycopy(buffer, pos, dst, off, copy);
+            pos += copy;
+            off += copy;
+            len -= copy;
+            count += copy;
+        }
+
+        return count;
+    }
+
+    public int read() throws IOException {
+        if (pos >= blockSize) {
+            getBlock();
+        }
+
+        return buffer[pos++] & 0xff;
+    }
+
+    public int readPlain(byte[] b, int off, int len) throws IOException {
+        if (pos != blockSize) {
+            throw new IOException("Cannot read plain since crypto buffer is not aligned.");
+        }
+
+        int n = 0;
+
+        while (n < len) {
+            int cnt = internal_read(b, off + n, len - n);
+
+            if (cnt < 0) {
+                throw new IOException("Cannot fill buffer, EOF reached.");
+            }
+
+            n += cnt;
+        }
+
+        return n;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/CipherOutputStream.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * CipherOutputStream.
+ *
+ * @author Christian Plattner
+ * @version $Id: CipherOutputStream.java 85 2014-04-07 14:05:09Z dkocher@sudo.ch $
+ */
+public class CipherOutputStream {
+    BlockCipher currentCipher;
+    OutputStream bo;
+    byte[] buffer;
+    byte[] enc;
+    int blockSize;
+    int pos;
+
+    /*
+     * We cannot use java.io.BufferedOutputStream, since that is not available
+     * in J2ME. Everything could be improved here alot.
+     */
+
+    private static final int BUFF_SIZE = 8192;
+    byte[] out_buffer = new byte[BUFF_SIZE];
+    int out_buffer_pos = 0;
+
+    public CipherOutputStream(BlockCipher tc, OutputStream bo) {
+        this.bo = bo;
+        changeCipher(tc);
+    }
+
+    private void internal_write(byte[] src, int off, int len) throws IOException {
+        while (len > 0) {
+            int space = BUFF_SIZE - out_buffer_pos;
+            int copy = (len > space) ? space : len;
+            System.arraycopy(src, off, out_buffer, out_buffer_pos, copy);
+            off += copy;
+            out_buffer_pos += copy;
+            len -= copy;
+
+            if (out_buffer_pos >= BUFF_SIZE) {
+                bo.write(out_buffer, 0, BUFF_SIZE);
+                out_buffer_pos = 0;
+            }
+        }
+    }
+
+    private void internal_write(int b) throws IOException {
+        out_buffer[out_buffer_pos++] = (byte) b;
+
+        if (out_buffer_pos >= BUFF_SIZE) {
+            bo.write(out_buffer, 0, BUFF_SIZE);
+            out_buffer_pos = 0;
+        }
+    }
+
+    public void flush() throws IOException {
+        if (pos != 0) {
+            throw new IOException("FATAL: cannot flush since crypto buffer is not aligned.");
+        }
+
+        if (out_buffer_pos > 0) {
+            bo.write(out_buffer, 0, out_buffer_pos);
+            out_buffer_pos = 0;
+        }
+
+        bo.flush();
+    }
+
+    public void changeCipher(BlockCipher bc) {
+        this.currentCipher = bc;
+        blockSize = bc.getBlockSize();
+        buffer = new byte[blockSize];
+        enc = new byte[blockSize];
+        pos = 0;
+    }
+
+    private void writeBlock() throws IOException {
+        try {
+            currentCipher.transformBlock(buffer, 0, enc, 0);
+        }
+        catch (Exception e) {
+            throw new IOException("Error while decrypting block.", e);
+        }
+
+        internal_write(enc, 0, blockSize);
+        pos = 0;
+    }
+
+    public void write(byte[] src, int off, int len) throws IOException {
+        while (len > 0) {
+            int avail = blockSize - pos;
+            int copy = Math.min(avail, len);
+            System.arraycopy(src, off, buffer, pos, copy);
+            pos += copy;
+            off += copy;
+            len -= copy;
+
+            if (pos >= blockSize) {
+                writeBlock();
+            }
+        }
+    }
+
+    public void write(int b) throws IOException {
+        buffer[pos++] = (byte) b;
+
+        if (pos >= blockSize) {
+            writeBlock();
+        }
+    }
+
+    public void writePlain(int b) throws IOException {
+        if (pos != 0) {
+            throw new IOException("Cannot write plain since crypto buffer is not aligned.");
+        }
+
+        internal_write(b);
+    }
+
+    public void writePlain(byte[] b, int off, int len) throws IOException {
+        if (pos != 0) {
+            throw new IOException("Cannot write plain since crypto buffer is not aligned.");
+        }
+
+        internal_write(b, off, len);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/DES.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,352 @@
+package ch.ethz.ssh2.crypto.cipher;
+
+/*
+ This file is based on the 3DES implementation from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * DES.
+ *
+ * @author See comments in the source file
+ * @version 2.50, 03/15/10
+ *
+ */
+public class DES implements BlockCipher {
+    private int[] workingKey = null;
+
+    /**
+     * standard constructor.
+     */
+    public DES() {
+    }
+
+    /**
+     * initialise a DES cipher.
+     *
+     * @param encrypting
+     *            whether or not we are for encryption.
+     * @param key
+     *            the parameters required to set up the cipher.
+     * @exception IllegalArgumentException
+     *                if the params argument is inappropriate.
+     */
+    public void init(boolean encrypting, byte[] key) {
+        this.workingKey = generateWorkingKey(encrypting, key, 0);
+    }
+
+    public String getAlgorithmName() {
+        return "DES";
+    }
+
+    public int getBlockSize() {
+        return 8;
+    }
+
+    public void transformBlock(byte[] in, int inOff, byte[] out, int outOff) {
+        if (workingKey == null) {
+            throw new IllegalStateException("DES engine not initialised!");
+        }
+
+        desFunc(workingKey, in, inOff, out, outOff);
+    }
+
+    public void reset() {
+    }
+
+    /**
+     * what follows is mainly taken from "Applied Cryptography", by Bruce
+     * Schneier, however it also bears great resemblance to Richard
+     * Outerbridge's D3DES...
+     */
+
+    static short[] Df_Key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
+                              0x10, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67
+                            };
+
+    static short[] bytebit = { 0200, 0100, 040, 020, 010, 04, 02, 01 };
+
+    static int[] bigbyte = { 0x800000, 0x400000, 0x200000, 0x100000, 0x80000, 0x40000, 0x20000, 0x10000, 0x8000,
+                             0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1
+                           };
+
+    /*
+     * Use the key schedule specified in the Standard (ANSI X3.92-1981).
+     */
+
+    static byte[] pc1 = { 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2,
+                          59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12,
+                          4, 27, 19, 11, 3
+                        };
+
+    static byte[] totrot = { 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 };
+
+    static byte[] pc2 = { 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40,
+                          51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
+                        };
+
+    static int[] SP1 = { 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004,
+                         0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+                         0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004,
+                         0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404,
+                         0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400,
+                         0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+                         0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004,
+                         0x00010400, 0x00000000, 0x01010004
+                       };
+
+    static int[] SP2 = { 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020,
+                         0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+                         0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020,
+                         0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020,
+                         0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020,
+                         0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+                         0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000,
+                         0x80100020, 0x80108020, 0x00108000
+                       };
+
+    static int[] SP3 = { 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208,
+                         0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+                         0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208,
+                         0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000,
+                         0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208,
+                         0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+                         0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208,
+                         0x00000008, 0x08020008, 0x00020200
+                       };
+
+    static int[] SP4 = { 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001,
+                         0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+                         0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081,
+                         0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001,
+                         0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081,
+                         0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+                         0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080,
+                         0x00800000, 0x00002000, 0x00802080
+                       };
+
+    static int[] SP5 = { 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000,
+                         0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+                         0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000,
+                         0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100,
+                         0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000,
+                         0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+                         0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000,
+                         0x40080000, 0x02080100, 0x40000100
+                       };
+
+    static int[] SP6 = { 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010,
+                         0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+                         0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010,
+                         0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010,
+                         0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000,
+                         0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+                         0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000,
+                         0x20000000, 0x00400010, 0x20004010
+                       };
+
+    static int[] SP7 = { 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802,
+                         0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+                         0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000,
+                         0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800,
+                         0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800,
+                         0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+                         0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002,
+                         0x04000800, 0x00000800, 0x00200002
+                       };
+
+    static int[] SP8 = { 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040,
+                         0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+                         0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040,
+                         0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000,
+                         0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040,
+                         0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+                         0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040,
+                         0x00040040, 0x10000000, 0x10041000
+                       };
+
+    /**
+     * generate an integer based working key based on our secret key and what we
+     * processing we are planning to do.
+     *
+     * Acknowledgements for this routine go to James Gillogly & Phil Karn.
+     * (whoever, and wherever they are!).
+     */
+    protected int[] generateWorkingKey(boolean encrypting, byte[] key, int off) {
+        int[] newKey = new int[32];
+        boolean[] pc1m = new boolean[56], pcr = new boolean[56];
+
+        for (int j = 0; j < 56; j++) {
+            int l = pc1[j];
+            pc1m[j] = ((key[off + (l >>> 3)] & bytebit[l & 07]) != 0);
+        }
+
+        for (int i = 0; i < 16; i++) {
+            int l, m, n;
+
+            if (encrypting) {
+                m = i << 1;
+            }
+            else {
+                m = (15 - i) << 1;
+            }
+
+            n = m + 1;
+            newKey[m] = newKey[n] = 0;
+
+            for (int j = 0; j < 28; j++) {
+                l = j + totrot[i];
+
+                if (l < 28) {
+                    pcr[j] = pc1m[l];
+                }
+                else {
+                    pcr[j] = pc1m[l - 28];
+                }
+            }
+
+            for (int j = 28; j < 56; j++) {
+                l = j + totrot[i];
+
+                if (l < 56) {
+                    pcr[j] = pc1m[l];
+                }
+                else {
+                    pcr[j] = pc1m[l - 28];
+                }
+            }
+
+            for (int j = 0; j < 24; j++) {
+                if (pcr[pc2[j]]) {
+                    newKey[m] |= bigbyte[j];
+                }
+
+                if (pcr[pc2[j + 24]]) {
+                    newKey[n] |= bigbyte[j];
+                }
+            }
+        }
+
+        //
+        // store the processed key
+        //
+        for (int i = 0; i != 32; i += 2) {
+            int i1, i2;
+            i1 = newKey[i];
+            i2 = newKey[i + 1];
+            newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10) | ((i2 & 0x00fc0000) >>> 10)
+                        | ((i2 & 0x00000fc0) >>> 6);
+            newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16) | ((i2 & 0x0003f000) >>> 4)
+                            | (i2 & 0x0000003f);
+        }
+
+        return newKey;
+    }
+
+    /**
+     * the DES engine.
+     */
+    protected void desFunc(int[] wKey, byte[] in, int inOff, byte[] out, int outOff) {
+        int work, right, left;
+        left = (in[inOff + 0] & 0xff) << 24;
+        left |= (in[inOff + 1] & 0xff) << 16;
+        left |= (in[inOff + 2] & 0xff) << 8;
+        left |= (in[inOff + 3] & 0xff);
+        right = (in[inOff + 4] & 0xff) << 24;
+        right |= (in[inOff + 5] & 0xff) << 16;
+        right |= (in[inOff + 6] & 0xff) << 8;
+        right |= (in[inOff + 7] & 0xff);
+        work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+        right ^= work;
+        left ^= (work << 4);
+        work = ((left >>> 16) ^ right) & 0x0000ffff;
+        right ^= work;
+        left ^= (work << 16);
+        work = ((right >>> 2) ^ left) & 0x33333333;
+        left ^= work;
+        right ^= (work << 2);
+        work = ((right >>> 8) ^ left) & 0x00ff00ff;
+        left ^= work;
+        right ^= (work << 8);
+        right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff;
+        work = (left ^ right) & 0xaaaaaaaa;
+        left ^= work;
+        right ^= work;
+        left = ((left << 1) | ((left >>> 31) & 1)) & 0xffffffff;
+
+        for (int round = 0; round < 8; round++) {
+            int fval;
+            work = (right << 28) | (right >>> 4);
+            work ^= wKey[round * 4 + 0];
+            fval = SP7[work & 0x3f];
+            fval |= SP5[(work >>> 8) & 0x3f];
+            fval |= SP3[(work >>> 16) & 0x3f];
+            fval |= SP1[(work >>> 24) & 0x3f];
+            work = right ^ wKey[round * 4 + 1];
+            fval |= SP8[work & 0x3f];
+            fval |= SP6[(work >>> 8) & 0x3f];
+            fval |= SP4[(work >>> 16) & 0x3f];
+            fval |= SP2[(work >>> 24) & 0x3f];
+            left ^= fval;
+            work = (left << 28) | (left >>> 4);
+            work ^= wKey[round * 4 + 2];
+            fval = SP7[work & 0x3f];
+            fval |= SP5[(work >>> 8) & 0x3f];
+            fval |= SP3[(work >>> 16) & 0x3f];
+            fval |= SP1[(work >>> 24) & 0x3f];
+            work = left ^ wKey[round * 4 + 3];
+            fval |= SP8[work & 0x3f];
+            fval |= SP6[(work >>> 8) & 0x3f];
+            fval |= SP4[(work >>> 16) & 0x3f];
+            fval |= SP2[(work >>> 24) & 0x3f];
+            right ^= fval;
+        }
+
+        right = (right << 31) | (right >>> 1);
+        work = (left ^ right) & 0xaaaaaaaa;
+        left ^= work;
+        right ^= work;
+        left = (left << 31) | (left >>> 1);
+        work = ((left >>> 8) ^ right) & 0x00ff00ff;
+        right ^= work;
+        left ^= (work << 8);
+        work = ((left >>> 2) ^ right) & 0x33333333;
+        right ^= work;
+        left ^= (work << 2);
+        work = ((right >>> 16) ^ left) & 0x0000ffff;
+        left ^= work;
+        right ^= (work << 16);
+        work = ((right >>> 4) ^ left) & 0x0f0f0f0f;
+        left ^= work;
+        right ^= (work << 4);
+        out[outOff + 0] = (byte)((right >>> 24) & 0xff);
+        out[outOff + 1] = (byte)((right >>> 16) & 0xff);
+        out[outOff + 2] = (byte)((right >>> 8) & 0xff);
+        out[outOff + 3] = (byte)(right & 0xff);
+        out[outOff + 4] = (byte)((left >>> 24) & 0xff);
+        out[outOff + 5] = (byte)((left >>> 16) & 0xff);
+        out[outOff + 6] = (byte)((left >>> 8) & 0xff);
+        out[outOff + 7] = (byte)(left & 0xff);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/DESede.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,98 @@
+package ch.ethz.ssh2.crypto.cipher;
+
+/*
+ This file was shamelessly taken (and modified) from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * DESede.
+ *
+ * @author See comments in the source file
+ * @version 2.50, 03/15/10
+ *
+ */
+public class DESede extends DES {
+    private int[] key1 = null;
+    private int[] key2 = null;
+    private int[] key3 = null;
+
+    private boolean encrypt;
+
+    /**
+     * standard constructor.
+     */
+    public DESede() {
+    }
+
+    /**
+     * initialise a DES cipher.
+     *
+     * @param encrypting
+     *            whether or not we are for encryption.
+     * @param key
+     *            the parameters required to set up the cipher.
+     * @exception IllegalArgumentException
+     *                if the params argument is inappropriate.
+     */
+    @Override
+    public void init(boolean encrypting, byte[] key) {
+        key1 = generateWorkingKey(encrypting, key, 0);
+        key2 = generateWorkingKey(!encrypting, key, 8);
+        key3 = generateWorkingKey(encrypting, key, 16);
+        encrypt = encrypting;
+    }
+
+    @Override
+    public String getAlgorithmName() {
+        return "DESede";
+    }
+
+    @Override
+    public int getBlockSize() {
+        return 8;
+    }
+
+    @Override
+    public void transformBlock(byte[] in, int inOff, byte[] out, int outOff) {
+        if (key1 == null) {
+            throw new IllegalStateException("DESede engine not initialised!");
+        }
+
+        if (encrypt) {
+            desFunc(key1, in, inOff, out, outOff);
+            desFunc(key2, out, outOff, out, outOff);
+            desFunc(key3, out, outOff, out, outOff);
+        }
+        else {
+            desFunc(key3, in, inOff, out, outOff);
+            desFunc(key2, out, outOff, out, outOff);
+            desFunc(key1, out, outOff, out, outOff);
+        }
+    }
+
+    @Override
+    public void reset() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/NullCipher.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+/**
+ * NullCipher.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class NullCipher implements BlockCipher {
+    private int blockSize = 8;
+
+    public NullCipher() {
+    }
+
+    public NullCipher(int blockSize) {
+        this.blockSize = blockSize;
+    }
+
+    public void init(boolean forEncryption, byte[] key) {
+    }
+
+    public int getBlockSize() {
+        return blockSize;
+    }
+
+    public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
+        System.arraycopy(src, srcoff, dst, dstoff, blockSize);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/dh/DhExchange.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,147 @@
+/**
+ *
+ */
+package ch.ethz.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.crypto.KeyAgreement;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+
+/**
+ * @author kenny
+ *
+ */
+public class DhExchange extends GenericDhExchange {
+
+    /* Given by the standard */
+
+    private static final BigInteger P1 = new BigInteger(
+        "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+        + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+        + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+        + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+        + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+        + "FFFFFFFFFFFFFFFF", 16);
+
+    private static final BigInteger P14 = new BigInteger(
+        "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+        + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+        + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+        + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+        + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+        + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+        + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+        + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+        + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+        + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+        + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16);
+
+    private static final BigInteger G = BigInteger.valueOf(2);
+
+    /* Client public and private */
+
+    private DHPrivateKey clientPrivate;
+    private DHPublicKey clientPublic;
+
+    /* Server public */
+
+    private DHPublicKey serverPublic;
+    private byte[] f;
+
+    @Override
+    public void init(String name) throws IOException {
+        final DHParameterSpec spec;
+
+        if ("diffie-hellman-group1-sha1".equals(name)) {
+            spec = new DHParameterSpec(P1, G);
+        }
+        else if ("diffie-hellman-group14-sha1".equals(name)) {
+            spec = new DHParameterSpec(P14, G);
+        }
+        else {
+            throw new IllegalArgumentException("Unknown DH group " + name);
+        }
+
+        try {
+            KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
+            kpg.initialize(spec);
+            KeyPair pair = kpg.generateKeyPair();
+            clientPrivate = (DHPrivateKey) pair.getPrivate();
+            clientPublic = (DHPublicKey) pair.getPublic();
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw(IOException) new IOException("No DH keypair generator").initCause(e);
+        }
+        catch (InvalidAlgorithmParameterException e) {
+            throw(IOException) new IOException("Invalid DH parameters").initCause(e);
+        }
+    }
+
+    @Override
+    public byte[] getE() {
+        if (clientPublic == null)
+            throw new IllegalStateException("DhExchange not initialized!");
+
+        return clientPublic.getY().toByteArray();
+    }
+
+    @Override
+    protected byte[] getServerE() {
+        if (serverPublic == null)
+            throw new IllegalStateException("DhExchange not initialized!");
+
+        return serverPublic.getY().toByteArray();
+    }
+
+    @Override
+    public byte[] getF() {
+        return f;
+    }
+
+    @Override
+    public void setF(byte[] f) throws IOException {
+        if (clientPublic == null)
+            throw new IllegalStateException("DhExchange not initialized!");
+
+        final KeyAgreement ka;
+
+        try {
+            KeyFactory kf = KeyFactory.getInstance("DH");
+            DHParameterSpec params = clientPublic.getParams();
+            this.f = f;
+            this.serverPublic = (DHPublicKey) kf.generatePublic(new DHPublicKeySpec(
+                                    new BigInteger(f), params.getP(), params.getG()));
+            ka = KeyAgreement.getInstance("DH");
+            ka.init(clientPrivate);
+            ka.doPhase(serverPublic, true);
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw(IOException) new IOException("No DH key agreement method").initCause(e);
+        }
+        catch (InvalidKeyException e) {
+            throw(IOException) new IOException("Invalid DH key").initCause(e);
+        }
+        catch (InvalidKeySpecException e) {
+            throw(IOException) new IOException("Invalid DH key").initCause(e);
+        }
+
+        sharedSecret = new BigInteger(ka.generateSecret());
+    }
+
+    @Override
+    public String getHashAlgo() {
+        return "SHA1";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/dh/DhGroupExchange.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,108 @@
+
+package ch.ethz.ssh2.crypto.dh;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.io.IOException;
+
+import ch.ethz.ssh2.DHGexParameters;
+import ch.ethz.ssh2.crypto.digest.HashForSSH2Types;
+
+
+/**
+ * DhGroupExchange.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: DhGroupExchange.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class DhGroupExchange {
+    /* Given by the standard */
+
+    private BigInteger p;
+    private BigInteger g;
+
+    /* Client public and private */
+
+    private BigInteger e;
+    private BigInteger x;
+
+    /* Server public */
+
+    private BigInteger f;
+
+    /* Shared secret */
+
+    private BigInteger k;
+
+    public DhGroupExchange(BigInteger p, BigInteger g) {
+        this.p = p;
+        this.g = g;
+    }
+
+    public void init(SecureRandom rnd) {
+        k = null;
+        x = new BigInteger(p.bitLength() - 1, rnd);
+        e = g.modPow(x, p);
+    }
+
+    /**
+     * @return Returns the e.
+     */
+    public BigInteger getE() {
+        if (e == null)
+            throw new IllegalStateException("Not initialized!");
+
+        return e;
+    }
+
+    /**
+     * @return Returns the shared secret k.
+     */
+    public BigInteger getK() {
+        if (k == null)
+            throw new IllegalStateException("Shared secret not yet known, need f first!");
+
+        return k;
+    }
+
+    /**
+     * Sets f and calculates the shared secret.
+     */
+    public void setF(BigInteger f) {
+        if (e == null)
+            throw new IllegalStateException("Not initialized!");
+
+        BigInteger zero = BigInteger.valueOf(0);
+
+        if (zero.compareTo(f) >= 0 || p.compareTo(f) <= 0)
+            throw new IllegalArgumentException("Invalid f specified!");
+
+        this.f = f;
+        this.k = f.modPow(x, p);
+    }
+
+    public byte[] calculateH(String hashAlgo, byte[] clientversion, byte[] serverversion,
+                             byte[] clientKexPayload, byte[] serverKexPayload, byte[] hostKey, DHGexParameters para) throws IOException {
+        HashForSSH2Types hash = new HashForSSH2Types(hashAlgo);
+        hash.updateByteString(clientversion);
+        hash.updateByteString(serverversion);
+        hash.updateByteString(clientKexPayload);
+        hash.updateByteString(serverKexPayload);
+        hash.updateByteString(hostKey);
+
+        if (para.getMin_group_len() > 0)
+            hash.updateUINT32(para.getMin_group_len());
+
+        hash.updateUINT32(para.getPref_group_len());
+
+        if (para.getMax_group_len() > 0)
+            hash.updateUINT32(para.getMax_group_len());
+
+        hash.updateBigInt(p);
+        hash.updateBigInt(g);
+        hash.updateBigInt(e);
+        hash.updateBigInt(f);
+        hash.updateBigInt(k);
+        return hash.getDigest();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/dh/EcDhExchange.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,127 @@
+/**
+ *
+ */
+package ch.ethz.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.crypto.KeyAgreement;
+
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+
+/**
+ * @author kenny
+ *
+ */
+public class EcDhExchange extends GenericDhExchange {
+
+    /* Client public and private */
+
+    private ECPrivateKey clientPrivate;
+    private ECPublicKey clientPublic;
+
+    /* Server public */
+
+    private ECPublicKey serverPublic;
+    private byte[] f;
+
+    @Override
+    public void init(String name) throws IOException {
+        final ECParameterSpec spec;
+
+        if ("ecdh-sha2-nistp256".equals(name)) {
+            spec = ECDSASHA2Verify.EllipticCurves.nistp256;
+        }
+        else if ("ecdh-sha2-nistp384".equals(name)) {
+            spec = ECDSASHA2Verify.EllipticCurves.nistp384;
+        }
+        else if ("ecdh-sha2-nistp521".equals(name)) {
+            spec = ECDSASHA2Verify.EllipticCurves.nistp521;
+        }
+        else {
+            throw new IllegalArgumentException("Unknown EC curve " + name);
+        }
+
+        KeyPairGenerator kpg;
+
+        try {
+            kpg = KeyPairGenerator.getInstance("EC");
+            kpg.initialize(spec);
+            KeyPair pair = kpg.generateKeyPair();
+            clientPrivate = (ECPrivateKey) pair.getPrivate();
+            clientPublic = (ECPublicKey) pair.getPublic();
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw(IOException) new IOException("No DH keypair generator").initCause(e);
+        }
+        catch (InvalidAlgorithmParameterException e) {
+            throw(IOException) new IOException("Invalid DH parameters").initCause(e);
+        }
+    }
+
+    @Override
+    public byte[] getE() {
+        return ECDSASHA2Verify.encodeECPoint(clientPublic.getW(), clientPublic.getParams()
+                                             .getCurve());
+    }
+
+    @Override
+    protected byte[] getServerE() {
+        return ECDSASHA2Verify.encodeECPoint(serverPublic.getW(), serverPublic.getParams()
+                                             .getCurve());
+    }
+
+    @Override
+    public byte[] getF() {
+        return f;
+    }
+
+    @Override
+    public void setF(byte[] f) throws IOException {
+        if (clientPublic == null)
+            throw new IllegalStateException("DhDsaExchange not initialized!");
+
+        final KeyAgreement ka;
+
+        try {
+            KeyFactory kf = KeyFactory.getInstance("EC");
+            ECParameterSpec params = clientPublic.getParams();
+            ECPoint serverPoint = ECDSASHA2Verify.decodeECPoint(f, params.getCurve());
+            this.f = f;
+            this.serverPublic = (ECPublicKey) kf.generatePublic(new ECPublicKeySpec(serverPoint,
+                                params));
+            ka = KeyAgreement.getInstance("ECDH");
+            ka.init(clientPrivate);
+            ka.doPhase(serverPublic, true);
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw(IOException) new IOException("No ECDH key agreement method").initCause(e);
+        }
+        catch (InvalidKeyException e) {
+            throw(IOException) new IOException("Invalid ECDH key").initCause(e);
+        }
+        catch (InvalidKeySpecException e) {
+            throw(IOException) new IOException("Invalid ECDH key").initCause(e);
+        }
+
+        sharedSecret = new BigInteger(ka.generateSecret());
+    }
+
+    @Override
+    public String getHashAlgo() {
+        return ECDSASHA2Verify.getDigestAlgorithmForParams(clientPublic.getParams());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/dh/GenericDhExchange.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,94 @@
+
+package ch.ethz.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.crypto.digest.HashForSSH2Types;
+import ch.ethz.ssh2.log.Logger;
+
+
+/**
+ * DhExchange.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public abstract class GenericDhExchange {
+    private static final Logger log = Logger.getLogger(GenericDhExchange.class);
+
+    /* Shared secret */
+
+    BigInteger sharedSecret;
+
+    protected GenericDhExchange() {
+    }
+
+    public static GenericDhExchange getInstance(String algo) {
+        if (algo.startsWith("ecdh-sha2-")) {
+            return new EcDhExchange();
+        }
+        else {
+            return new DhExchange();
+        }
+    }
+
+    public abstract void init(String name) throws IOException;
+
+    /**
+     * @return Returns the e (public value)
+     * @throws IllegalStateException
+     */
+    public abstract byte[] getE();
+
+    public void setE(BigInteger e)  throws IOException {
+        throw new IOException();
+    }
+
+    /**
+     * @return Returns the server's e (public value)
+     * @throws IllegalStateException
+     */
+    protected abstract byte[] getServerE();
+
+    /**
+     * @return Returns the shared secret k.
+     * @throws IllegalStateException
+     */
+    public BigInteger getK() {
+        if (sharedSecret == null)
+            throw new IllegalStateException("Shared secret not yet known, need f first!");
+
+        return sharedSecret;
+    }
+
+    /**
+     * @param f
+     */
+    public void setF(BigInteger f) throws IOException {
+        setF(f.toByteArray());
+    }
+
+    public abstract byte[] getF();
+
+    public abstract void setF(byte[] f) throws IOException;
+
+    public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
+                             byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException, IOException {
+        HashForSSH2Types hash = new HashForSSH2Types(getHashAlgo());
+        log.debug("Client: '" + new String(clientversion, "ISO-8859-1") + "'");
+        log.debug("Server: '" + new String(serverversion, "ISO-8859-1") + "'");
+        hash.updateByteString(clientversion);
+        hash.updateByteString(serverversion);
+        hash.updateByteString(clientKexPayload);
+        hash.updateByteString(serverKexPayload);
+        hash.updateByteString(hostKey);
+        hash.updateByteString(getE());
+        hash.updateByteString(getServerE());
+        hash.updateBigInt(sharedSecret);
+        return hash.getDigest();
+    }
+
+    public abstract String getHashAlgo();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/Digest.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+
+/**
+ * Digest.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public interface Digest {
+    public int getDigestLength();
+
+    public void update(byte b);
+
+    public void update(byte[] b);
+
+    public void update(byte b[], int off, int len);
+
+    public void reset();
+
+    public void digest(byte[] out) throws DigestException;
+
+    public void digest(byte[] out, int off) throws DigestException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/HMAC.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+
+/**
+ * HMAC.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class HMAC implements Digest {
+    Digest md;
+    byte[] k_xor_ipad;
+    byte[] k_xor_opad;
+
+    byte[] tmp;
+
+    int size;
+
+    public HMAC(Digest md, byte[] key, int size) throws DigestException {
+        this.md = md;
+        this.size = size;
+        tmp = new byte[md.getDigestLength()];
+        final int BLOCKSIZE = 64;
+        k_xor_ipad = new byte[BLOCKSIZE];
+        k_xor_opad = new byte[BLOCKSIZE];
+
+        if (key.length > BLOCKSIZE) {
+            md.reset();
+            md.update(key);
+            md.digest(tmp);
+            key = tmp;
+        }
+
+        System.arraycopy(key, 0, k_xor_ipad, 0, key.length);
+        System.arraycopy(key, 0, k_xor_opad, 0, key.length);
+
+        for (int i = 0; i < BLOCKSIZE; i++) {
+            k_xor_ipad[i] ^= 0x36;
+            k_xor_opad[i] ^= 0x5C;
+        }
+
+        md.update(k_xor_ipad);
+    }
+
+    public final int getDigestLength() {
+        return size;
+    }
+
+    public final void update(byte b) {
+        md.update(b);
+    }
+
+    public final void update(byte[] b) {
+        md.update(b);
+    }
+
+    public final void update(byte[] b, int off, int len) {
+        md.update(b, off, len);
+    }
+
+    public final void reset() {
+        md.reset();
+        md.update(k_xor_ipad);
+    }
+
+    public final void digest(byte[] out) throws DigestException {
+        digest(out, 0);
+    }
+
+    public final void digest(byte[] out, int off) throws DigestException {
+        md.digest(tmp);
+        md.update(k_xor_opad);
+        md.update(tmp);
+        md.digest(tmp);
+        System.arraycopy(tmp, 0, out, off, size);
+        md.update(k_xor_ipad);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/HashForSSH2Types.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.DigestException;
+
+/**
+ * HashForSSH2Types.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class HashForSSH2Types {
+    Digest md;
+
+    public HashForSSH2Types(Digest md) {
+        this.md = md;
+    }
+
+    public HashForSSH2Types(String type) {
+        if (type.equals("SHA1")) {
+            md = new SHA1();
+        }
+        else if (type.equals("SHA2")) {
+            md = new SHA256();
+        }
+        else if (type.equals("MD5")) {
+            md = new MD5();
+        }
+        else {
+            throw new IllegalArgumentException(String.format("Unknown algorithm %s", type));
+        }
+    }
+
+    public void updateByte(byte b) {
+        md.update(b);
+    }
+
+    public void updateBytes(byte[] b) {
+        md.update(b);
+    }
+
+    public void updateUINT32(int v) {
+        md.update((byte)(v >> 24));
+        md.update((byte)(v >> 16));
+        md.update((byte)(v >> 8));
+        md.update((byte)(v));
+    }
+
+    public void updateByteString(byte[] b) {
+        updateUINT32(b.length);
+        updateBytes(b);
+    }
+
+    public void updateBigInt(BigInteger b) {
+        updateByteString(b.toByteArray());
+    }
+
+    public void reset() {
+        md.reset();
+    }
+
+    public int getDigestLength() {
+        return md.getDigestLength();
+    }
+
+    public byte[] getDigest() throws IOException {
+        byte[] tmp = new byte[md.getDigestLength()];
+        getDigest(tmp);
+        return tmp;
+    }
+
+    public void getDigest(byte[] out) throws IOException {
+        try {
+            getDigest(out, 0);
+        }
+        catch (DigestException e) {
+            throw new IOException(e);
+        }
+    }
+
+    public void getDigest(byte[] out, int off) throws DigestException {
+        md.digest(out, off);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/MAC.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.io.IOException;
+import java.security.DigestException;
+
+/**
+ * MAC.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class MAC {
+    private Digest mac;
+    private int size;
+
+    public static String[] getMacList() {
+        // Higher priority first. Added SHA-2 algorithms as in RFC 6668
+        return new String[] {"hmac-sha1-96", "hmac-sha1", "hmac-md5-96", "hmac-md5", "hmac-sha2-256", "hmac-sha2-512"};
+    }
+
+    public static void checkMacList(final String[] macs) {
+        for (String m : macs) {
+            getKeyLen(m);
+        }
+    }
+
+    public static int getKeyLen(final String type) {
+        if (type.equals("hmac-sha1")) {
+            return 20;
+        }
+
+        if (type.equals("hmac-sha1-96")) {
+            return 20;
+        }
+
+        if (type.equals("hmac-md5")) {
+            return 16;
+        }
+
+        if (type.equals("hmac-md5-96")) {
+            return 16;
+        }
+
+        if (type.equals("hmac-sha2-256")) {
+            return 32;
+        }
+
+        if (type.equals("hmac-sha2-512")) {
+            return 64;
+        }
+
+        throw new IllegalArgumentException(String.format("Unknown algorithm %s", type));
+    }
+
+    public MAC(final String type, final byte[] key) throws DigestException {
+        if (type.equals("hmac-sha1")) {
+            mac = new HMAC(new SHA1(), key, 20);
+        }
+        else if (type.equals("hmac-sha1-96")) {
+            mac = new HMAC(new SHA1(), key, 12);
+        }
+        else if (type.equals("hmac-md5")) {
+            mac = new HMAC(new MD5(), key, 16);
+        }
+        else if (type.equals("hmac-md5-96")) {
+            mac = new HMAC(new MD5(), key, 12);
+        }
+        else if (type.equals("hmac-sha2-256")) {
+            mac = new HMAC(new SHA256(), key, 32);
+        }
+        else if (type.equals("hmac-sha2-512")) {
+            mac = new HMAC(new SHA512(), key, 64);
+        }
+        else {
+            throw new IllegalArgumentException(String.format("Unknown algorithm %s", type));
+        }
+
+        size = mac.getDigestLength();
+    }
+
+    public final void initMac(final int seq) {
+        mac.reset();
+        mac.update((byte)(seq >> 24));
+        mac.update((byte)(seq >> 16));
+        mac.update((byte)(seq >> 8));
+        mac.update((byte)(seq));
+    }
+
+    public final void update(byte[] packetdata, int off, int len) {
+        mac.update(packetdata, off, len);
+    }
+
+    public final void getMac(byte[] out, int off) throws IOException {
+        try {
+            mac.digest(out, off);
+        }
+        catch (DigestException e) {
+            throw new IOException(e);
+        }
+    }
+
+    public final int size() {
+        return size;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/MD5.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @version $Id: MD5.java 154 2014-04-28 11:45:02Z dkocher@sudo.ch $
+ */
+public final class MD5 implements Digest {
+
+    private MessageDigest md;
+
+    public MD5() {
+        try {
+            md = MessageDigest.getInstance("MD5");
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    public final int getDigestLength() {
+        return md.getDigestLength();
+    }
+
+    public final void reset() {
+        md.reset();
+    }
+
+    public final void update(byte b[]) {
+        this.update(b, 0, b.length);
+    }
+
+    public final void update(byte b[], int off, int len) {
+        md.update(b, off, len);
+    }
+
+    public final void update(byte b) {
+        md.update(b);
+    }
+
+    public final void digest(byte[] out) throws DigestException {
+        this.digest(out, 0);
+    }
+
+    public final void digest(byte[] out, int off) throws DigestException {
+        md.digest(out, off, out.length);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/SHA1.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @version $Id: SHA1.java 154 2014-04-28 11:45:02Z dkocher@sudo.ch $
+ */
+public final class SHA1 implements Digest {
+
+    private MessageDigest md;
+
+    public SHA1() {
+        try {
+            md = MessageDigest.getInstance("SHA-1");
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    public final int getDigestLength() {
+        return md.getDigestLength();
+    }
+
+    public final void reset() {
+        md.reset();
+    }
+
+    public final void update(byte b[]) {
+        this.update(b, 0, b.length);
+    }
+
+    public final void update(byte b[], int off, int len) {
+        md.update(b, off, len);
+    }
+
+    public final void update(byte b) {
+        md.update(b);
+    }
+
+    public final void digest(byte[] out) throws DigestException {
+        this.digest(out, 0);
+    }
+
+    public final void digest(byte[] out, int off) throws DigestException {
+        md.digest(out, off, out.length);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/SHA256.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @version $Id: SHA256.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $
+ */
+public final class SHA256 implements Digest {
+
+    private MessageDigest md;
+
+    public SHA256() {
+        try {
+            md = MessageDigest.getInstance("SHA-256");
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    public final int getDigestLength() {
+        return md.getDigestLength();
+    }
+
+    public final void reset() {
+        md.reset();
+    }
+
+    public final void update(byte b[]) {
+        this.update(b, 0, b.length);
+    }
+
+    public final void update(byte b[], int off, int len) {
+        md.update(b, off, len);
+    }
+
+    public final void update(byte b) {
+        md.update(b);
+    }
+
+    public final void digest(byte[] out) throws DigestException {
+        this.digest(out, 0);
+    }
+
+    public final void digest(byte[] out, int off) throws DigestException {
+        md.digest(out, off, out.length);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/SHA512.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @version $Id: SHA512.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $
+ */
+public final class SHA512 implements Digest {
+
+    private MessageDigest md;
+
+    public SHA512() {
+        try {
+            md = MessageDigest.getInstance("SHA-512");
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    public final int getDigestLength() {
+        return md.getDigestLength();
+    }
+
+    public final void reset() {
+        md.reset();
+    }
+
+    public final void update(byte b[]) {
+        md.update(b);
+    }
+
+    public final void update(byte b[], int off, int len) {
+        md.update(b, off, len);
+    }
+
+    public final void update(byte b) {
+        md.update(b);
+    }
+
+    public final void digest(byte[] out) {
+        md.digest(out);
+    }
+
+    public final void digest(byte[] out, int off) throws DigestException {
+        md.digest(out, off, out.length);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/log/Logger.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.log;
+
+import android.util.Log;
+
+public class Logger {
+    private static final String TAG = "ConnectBot.ssh";
+    public static boolean enabled = true;
+
+    public static Logger getLogger(Class<?> x) {
+        return new Logger();
+    }
+
+    public Logger() {
+    }
+
+    public final boolean isEnabled() {
+        return enabled;
+    }
+
+    public boolean isDebugEnabled() {
+        return enabled;
+    }
+
+    public void debug(String message) {
+        if (enabled) Log.d(TAG, message);
+    }
+
+    public boolean isInfoEnabled() {
+        return enabled;
+    }
+
+    public void info(String message) {
+        if (enabled) Log.i(TAG, message);
+    }
+
+    public boolean isWarningEnabled() {
+        return enabled;
+    }
+
+    public void warning(String message) {
+        if (enabled) Log.w(TAG, message);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelAuthAgentReq.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,30 @@
+package ch.ethz.ssh2.packets;
+
+/**
+ * PacketGlobalAuthAgent.
+ *
+ * @author Kenny Root, kenny@the-b.org
+ * @version $Id$
+ */
+public class PacketChannelAuthAgentReq {
+    byte[] payload;
+
+    public int recipientChannelID;
+
+    public PacketChannelAuthAgentReq(int recipientChannelID) {
+        this.recipientChannelID = recipientChannelID;
+    }
+
+    public byte[] getPayload() {
+        if (payload == null) {
+            TypesWriter tw = new TypesWriter();
+            tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+            tw.writeUINT32(recipientChannelID);
+            tw.writeString("auth-agent-req@openssh.com");
+            tw.writeBoolean(true); // want reply
+            payload = tw.getBytes();
+        }
+
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelFailure.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketChannelFailure.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketChannelFailure {
+    private final byte[] payload;
+
+    public PacketChannelFailure(int recipientChannelID) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_FAILURE);
+        tw.writeUINT32(recipientChannelID);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelOpenConfirmation.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketChannelOpenConfirmation.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketChannelOpenConfirmation {
+
+    private final byte[] payload;
+
+    private final int recipientChannelID;
+    private final int senderChannelID;
+    private final int initialWindowSize;
+    private final int maxPacketSize;
+
+    public PacketChannelOpenConfirmation(int recipientChannelID, int senderChannelID, int initialWindowSize,
+                                         int maxPacketSize) {
+        this.recipientChannelID = recipientChannelID;
+        this.senderChannelID = senderChannelID;
+        this.initialWindowSize = initialWindowSize;
+        this.maxPacketSize = maxPacketSize;
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+        tw.writeUINT32(recipientChannelID);
+        tw.writeUINT32(senderChannelID);
+        tw.writeUINT32(initialWindowSize);
+        tw.writeUINT32(maxPacketSize);
+        payload = tw.getBytes();
+    }
+
+    public PacketChannelOpenConfirmation(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        recipientChannelID = tr.readUINT32();
+        senderChannelID = tr.readUINT32();
+        initialWindowSize = tr.readUINT32();
+        maxPacketSize = tr.readUINT32();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public int getRecipientChannelID() {
+        return recipientChannelID;
+    }
+
+    public int getSenderChannelID() {
+        return senderChannelID;
+    }
+
+    public int getInitialWindowSize() {
+        return initialWindowSize;
+    }
+
+    public int getMaxPacketSize() {
+        return maxPacketSize;
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelOpenFailure.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketChannelOpenFailure.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketChannelOpenFailure {
+
+    private final byte[] payload;
+
+    public PacketChannelOpenFailure(int recipientChannelID, int reasonCode, String description,
+                                    String languageTag) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_FAILURE);
+        tw.writeUINT32(recipientChannelID);
+        tw.writeUINT32(reasonCode);
+        tw.writeString(description);
+        tw.writeString(languageTag);
+        payload = tw.getBytes();
+    }
+
+    public PacketChannelOpenFailure(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_FAILURE) {
+            throw new IOException(
+                "This is not a SSH_MSG_CHANNEL_OPEN_FAILURE! ("
+                + packet_type + ")"
+            );
+        }
+
+        int recipientChannelID = tr.readUINT32();
+        int reasonCode = tr.readUINT32();
+        String description = tr.readString();
+        String languageTag = tr.readString();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelSuccess.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketChannelSuccess.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketChannelSuccess {
+
+    private final byte[] payload;
+
+    public PacketChannelSuccess(int recipientChannelID) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_SUCCESS);
+        tw.writeUINT32(recipientChannelID);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelWindowAdjust.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketChannelWindowAdjust.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketChannelWindowAdjust {
+
+    private final byte[] payload;
+
+    public PacketChannelWindowAdjust(int recipientChannelID, int windowChange) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST);
+        tw.writeUINT32(recipientChannelID);
+        tw.writeUINT32(windowChange);
+        payload = tw.getBytes();
+    }
+
+    public PacketChannelWindowAdjust(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST) {
+            throw new IOException(
+                "This is not a SSH_MSG_CHANNEL_WINDOW_ADJUST! ("
+                + packet_type + ")"
+            );
+        }
+
+        int recipientChannelID = tr.readUINT32();
+        int windowChange = tr.readUINT32();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketDisconnect.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketDisconnect.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketDisconnect {
+
+    public enum Reason {
+        SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT,
+        SSH_DISCONNECT_PROTOCOL_ERROR,
+        SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+        SSH_DISCONNECT_RESERVED,
+        SSH_DISCONNECT_MAC_ERROR,
+        SSH_DISCONNECT_COMPRESSION_ERROR,
+        SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+        SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
+        SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE,
+        SSH_DISCONNECT_CONNECTION_LOST,
+        SSH_DISCONNECT_BY_APPLICATION,
+        SSH_DISCONNECT_TOO_MANY_CONNECTIONS,
+        SSH_DISCONNECT_AUTH_CANCELLED_BY_USER,
+        SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+        SSH_DISCONNECT_ILLEGAL_USER_NAME
+    }
+
+    private final byte[] payload;
+
+    private final Reason reason;
+
+    private final String message;
+
+    public PacketDisconnect(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_DISCONNECT) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        reason = PacketDisconnect.Reason.values()[tr.readUINT32()];
+        message = tr.readString();
+        String lang = tr.readString();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public PacketDisconnect(Reason reason, String desc) {
+        this.reason = reason;
+        this.message = desc;
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_DISCONNECT);
+        tw.writeUINT32(reason.ordinal());
+        tw.writeString(desc);
+        tw.writeString("");
+        payload = tw.getBytes();
+    }
+
+    public Reason getReason() {
+        return reason;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketGlobalCancelForwardRequest.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketGlobalCancelForwardRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketGlobalCancelForwardRequest {
+
+    private final byte[] payload;
+
+    public PacketGlobalCancelForwardRequest(boolean wantReply, String bindAddress, int bindPort) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
+        tw.writeString("cancel-tcpip-forward");
+        tw.writeBoolean(wantReply);
+        tw.writeString(bindAddress);
+        tw.writeUINT32(bindPort);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketGlobalForwardRequest.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketGlobalForwardRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketGlobalForwardRequest {
+
+    private final byte[] payload;
+
+    public PacketGlobalForwardRequest(boolean wantReply, String bindAddress, int bindPort) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
+        tw.writeString("tcpip-forward");
+        tw.writeBoolean(wantReply);
+        tw.writeString(bindAddress);
+        tw.writeUINT32(bindPort);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketIgnore.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketIgnore.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketIgnore {
+    private final byte[] payload;
+
+    public PacketIgnore(byte[] data) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_IGNORE);
+
+        if (data != null) {
+            tw.writeString(data, 0, data.length);
+        }
+        else {
+            tw.writeString("");
+        }
+
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDHInit.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDHInit.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDHInit {
+
+    private final byte[] payload;
+
+    private final BigInteger e;
+
+    public PacketKexDHInit(BigInteger e) {
+        this.e = e;
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_KEXDH_INIT);
+        tw.writeMPInt(e);
+        payload = tw.getBytes();
+    }
+
+    public PacketKexDHInit(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_KEXDH_INIT) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        e = tr.readMPINT();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public BigInteger getE() {
+        return e;
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDHReply.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDHReply.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDHReply {
+
+    private final byte[] payload;
+
+    private final byte[] hostKey;
+    private final BigInteger f;
+    private final byte[] signature;
+
+    public PacketKexDHReply(byte[] hostKey, BigInteger f, byte[] signature) {
+        this.hostKey = hostKey;
+        this.f = f;
+        this.signature = signature;
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_KEXDH_REPLY);
+        tw.writeString(hostKey, 0, hostKey.length);
+        tw.writeMPInt(f);
+        tw.writeString(signature, 0, signature.length);
+        payload = tw.getBytes();
+    }
+
+    public PacketKexDHReply(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_KEXDH_REPLY) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        hostKey = tr.readByteString();
+        f = tr.readMPINT();
+        signature = tr.readByteString();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+
+    public BigInteger getF() {
+        return f;
+    }
+
+    public byte[] getHostKey() {
+        return hostKey;
+    }
+
+    public byte[] getSignature() {
+        return signature;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDhGexGroup.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDhGexGroup.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDhGexGroup {
+
+    private final BigInteger p;
+    private final BigInteger g;
+
+    public PacketKexDhGexGroup(byte payload[]) throws IOException {
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_GROUP) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        p = tr.readMPINT();
+        g = tr.readMPINT();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public BigInteger getG() {
+        return g;
+    }
+
+    public BigInteger getP() {
+        return p;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDhGexInit.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.math.BigInteger;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDhGexInit.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDhGexInit {
+
+    private final byte[] payload;
+
+    public PacketKexDhGexInit(BigInteger e) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_INIT);
+        tw.writeMPInt(e);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDhGexReply.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDhGexReply.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDhGexReply {
+
+    private final byte[] hostKey;
+    private final BigInteger f;
+    private final byte[] signature;
+
+    public PacketKexDhGexReply(byte payload[]) throws IOException {
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_REPLY) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        hostKey = tr.readByteString();
+        f = tr.readMPINT();
+        signature = tr.readByteString();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public BigInteger getF() {
+        return f;
+    }
+
+    public byte[] getHostKey() {
+        return hostKey;
+    }
+
+    public byte[] getSignature() {
+        return signature;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDhGexRequest.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import ch.ethz.ssh2.DHGexParameters;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDhGexRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDhGexRequest {
+    private final byte[] payload;
+
+    public PacketKexDhGexRequest(DHGexParameters para) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST);
+        tw.writeUINT32(para.getMin_group_len());
+        tw.writeUINT32(para.getPref_group_len());
+        tw.writeUINT32(para.getMax_group_len());
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDhGexRequestOld.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import ch.ethz.ssh2.DHGexParameters;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDhGexRequestOld.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDhGexRequestOld {
+
+    private final byte[] payload;
+
+    public PacketKexDhGexRequestOld(DHGexParameters para) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST_OLD);
+        tw.writeUINT32(para.getPref_group_len());
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexInit.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.transport.KexParameters;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexInit.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexInit {
+    private final byte[] payload;
+
+    KexParameters kp = new KexParameters();
+
+    public PacketKexInit(CryptoWishList cwl, SecureRandom rnd) {
+        kp.cookie = new byte[16];
+        rnd.nextBytes(kp.cookie);
+        kp.kex_algorithms = cwl.kexAlgorithms;
+        kp.server_host_key_algorithms = cwl.serverHostKeyAlgorithms;
+        kp.encryption_algorithms_client_to_server = cwl.c2s_enc_algos;
+        kp.encryption_algorithms_server_to_client = cwl.s2c_enc_algos;
+        kp.mac_algorithms_client_to_server = cwl.c2s_mac_algos;
+        kp.mac_algorithms_server_to_client = cwl.s2c_mac_algos;
+        kp.compression_algorithms_client_to_server = cwl.c2s_comp_algos;
+        kp.compression_algorithms_server_to_client = cwl.s2c_comp_algos;
+        kp.languages_client_to_server = new String[] {""};
+        kp.languages_server_to_client = new String[] {""};
+        kp.first_kex_packet_follows = false;
+        kp.reserved_field1 = 0;
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_KEXINIT);
+        tw.writeBytes(kp.cookie, 0, 16);
+        tw.writeNameList(kp.kex_algorithms);
+        tw.writeNameList(kp.server_host_key_algorithms);
+        tw.writeNameList(kp.encryption_algorithms_client_to_server);
+        tw.writeNameList(kp.encryption_algorithms_server_to_client);
+        tw.writeNameList(kp.mac_algorithms_client_to_server);
+        tw.writeNameList(kp.mac_algorithms_server_to_client);
+        tw.writeNameList(kp.compression_algorithms_client_to_server);
+        tw.writeNameList(kp.compression_algorithms_server_to_client);
+        tw.writeNameList(kp.languages_client_to_server);
+        tw.writeNameList(kp.languages_server_to_client);
+        tw.writeBoolean(kp.first_kex_packet_follows);
+        tw.writeUINT32(kp.reserved_field1);
+        payload = tw.getBytes();
+    }
+
+    public PacketKexInit(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_KEXINIT) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        kp.cookie = tr.readBytes(16);
+        kp.kex_algorithms = tr.readNameList();
+        kp.server_host_key_algorithms = tr.readNameList();
+        kp.encryption_algorithms_client_to_server = tr.readNameList();
+        kp.encryption_algorithms_server_to_client = tr.readNameList();
+        kp.mac_algorithms_client_to_server = tr.readNameList();
+        kp.mac_algorithms_server_to_client = tr.readNameList();
+        kp.compression_algorithms_client_to_server = tr.readNameList();
+        kp.compression_algorithms_server_to_client = tr.readNameList();
+        kp.languages_client_to_server = tr.readNameList();
+        kp.languages_server_to_client = tr.readNameList();
+        kp.first_kex_packet_follows = tr.readBoolean();
+        kp.reserved_field1 = tr.readUINT32();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+
+    public KexParameters getKexParameters() {
+        return kp;
+    }
+
+    public String[] getCompression_algorithms_client_to_server() {
+        return kp.compression_algorithms_client_to_server;
+    }
+
+    public String[] getCompression_algorithms_server_to_client() {
+        return kp.compression_algorithms_server_to_client;
+    }
+
+    public byte[] getCookie() {
+        return kp.cookie;
+    }
+
+    public String[] getEncryption_algorithms_client_to_server() {
+        return kp.encryption_algorithms_client_to_server;
+    }
+
+    public String[] getEncryption_algorithms_server_to_client() {
+        return kp.encryption_algorithms_server_to_client;
+    }
+
+    public boolean isFirst_kex_packet_follows() {
+        return kp.first_kex_packet_follows;
+    }
+
+    public String[] getKex_algorithms() {
+        return kp.kex_algorithms;
+    }
+
+    public String[] getLanguages_client_to_server() {
+        return kp.languages_client_to_server;
+    }
+
+    public String[] getLanguages_server_to_client() {
+        return kp.languages_server_to_client;
+    }
+
+    public String[] getMac_algorithms_client_to_server() {
+        return kp.mac_algorithms_client_to_server;
+    }
+
+    public String[] getMac_algorithms_server_to_client() {
+        return kp.mac_algorithms_server_to_client;
+    }
+
+    public int getReserved_field1() {
+        return kp.reserved_field1;
+    }
+
+    public String[] getServer_host_key_algorithms() {
+        return kp.server_host_key_algorithms;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketNewKeys.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketNewKeys.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketNewKeys {
+    private final byte[] payload;
+
+    public PacketNewKeys() {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_NEWKEYS);
+        payload = tw.getBytes();
+    }
+
+    public PacketNewKeys(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_NEWKEYS) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketOpenDirectTCPIPChannel.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketOpenDirectTCPIPChannel.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketOpenDirectTCPIPChannel {
+    private final byte[] payload;
+
+    public PacketOpenDirectTCPIPChannel(int channelID, int initialWindowSize, int maxPacketSize,
+                                        String host_to_connect, int port_to_connect, String originator_IP_address,
+                                        int originator_port) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN);
+        tw.writeString("direct-tcpip");
+        tw.writeUINT32(channelID);
+        tw.writeUINT32(initialWindowSize);
+        tw.writeUINT32(maxPacketSize);
+        tw.writeString(host_to_connect);
+        tw.writeUINT32(port_to_connect);
+        tw.writeString(originator_IP_address);
+        tw.writeUINT32(originator_port);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketOpenSessionChannel.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketOpenSessionChannel.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketOpenSessionChannel {
+    private final byte[] payload;
+
+    public PacketOpenSessionChannel(int channelID, int initialWindowSize, int maxPacketSize) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN);
+        tw.writeString("session");
+        tw.writeUINT32(channelID);
+        tw.writeUINT32(initialWindowSize);
+        tw.writeUINT32(maxPacketSize);
+        payload = tw.getBytes();
+    }
+
+    public PacketOpenSessionChannel(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        int channelID = tr.readUINT32();
+        int initialWindowSize = tr.readUINT32();
+        int maxPacketSize = tr.readUINT32();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketServiceAccept.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketServiceAccept.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketServiceAccept {
+    private final byte[] payload;
+
+    public PacketServiceAccept(String serviceName) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_SERVICE_ACCEPT);
+        tw.writeString(serviceName);
+        payload = tw.getBytes();
+    }
+
+    public PacketServiceAccept(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_SERVICE_ACCEPT) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        if (tr.remain() != 0) {
+            String serviceName = tr.readString();
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketServiceRequest.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketServiceRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketServiceRequest {
+    private final byte[] payload;
+
+    private final String serviceName;
+
+    public PacketServiceRequest(String serviceName) {
+        this.serviceName = serviceName;
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_SERVICE_REQUEST);
+        tw.writeString(serviceName);
+        payload = tw.getBytes();
+    }
+
+    public String getServiceName() {
+        return serviceName;
+    }
+
+    public PacketServiceRequest(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_SERVICE_REQUEST) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        serviceName = tr.readString();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketSessionExecCommand.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketSessionExecCommand.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketSessionExecCommand {
+    private final byte[] payload;
+
+    public PacketSessionExecCommand(int recipientChannelID, boolean wantReply, String command, String charsetName) throws UnsupportedEncodingException {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+        tw.writeUINT32(recipientChannelID);
+        tw.writeString("exec");
+        tw.writeBoolean(wantReply);
+        tw.writeString(command, charsetName);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() throws IOException {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketSessionPtyRequest.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketSessionPtyRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketSessionPtyRequest {
+    private final byte[] payload;
+
+    public PacketSessionPtyRequest(int recipientChannelID, boolean wantReply, String term,
+                                   int character_width, int character_height, int pixel_width, int pixel_height,
+                                   byte[] terminal_modes) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+        tw.writeUINT32(recipientChannelID);
+        tw.writeString("pty-req");
+        tw.writeBoolean(wantReply);
+        tw.writeString(term);
+        tw.writeUINT32(character_width);
+        tw.writeUINT32(character_height);
+        tw.writeUINT32(pixel_width);
+        tw.writeUINT32(pixel_height);
+        tw.writeString(terminal_modes, 0, terminal_modes.length);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketSessionStartShell.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketSessionStartShell.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketSessionStartShell {
+    private final byte[] payload;
+
+    public PacketSessionStartShell(int recipientChannelID, boolean wantReply) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+        tw.writeUINT32(recipientChannelID);
+        tw.writeString("shell");
+        tw.writeBoolean(wantReply);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketSessionSubsystemRequest.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketSessionSubsystemRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketSessionSubsystemRequest {
+    private final byte[] payload;
+
+    public PacketSessionSubsystemRequest(int recipientChannelID, boolean wantReply, String subsystem) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+        tw.writeUINT32(recipientChannelID);
+        tw.writeString("subsystem");
+        tw.writeBoolean(wantReply);
+        tw.writeString(subsystem);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketSessionX11Request.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketSessionX11Request.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketSessionX11Request {
+    private final byte[] payload;
+
+    public PacketSessionX11Request(int recipientChannelID, boolean wantReply, boolean singleConnection,
+                                   String x11AuthenticationProtocol, String x11AuthenticationCookie, int x11ScreenNumber) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+        tw.writeUINT32(recipientChannelID);
+        tw.writeString("x11-req");
+        tw.writeBoolean(wantReply);
+        tw.writeBoolean(singleConnection);
+        tw.writeString(x11AuthenticationProtocol);
+        tw.writeString(x11AuthenticationCookie);
+        tw.writeUINT32(x11ScreenNumber);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthBanner.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthBanner.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthBanner {
+    private final byte[] payload;
+
+    private final String message;
+
+    public PacketUserauthBanner(String message) {
+        this.message = message;
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_USERAUTH_BANNER);
+        tw.writeString(message);
+        tw.writeString("");
+        payload = tw.getBytes();
+    }
+
+    public String getBanner() {
+        return message;
+    }
+
+    public PacketUserauthBanner(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_USERAUTH_BANNER) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        message = tr.readString("UTF-8");
+        String language = tr.readString();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthFailure.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthFailure.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthFailure {
+
+    private final byte[] payload;
+
+    private Set<String> authThatCanContinue;
+
+    private boolean partialSuccess;
+
+    public PacketUserauthFailure(Set<String> authThatCanContinue, boolean partialSuccess) {
+        this.authThatCanContinue = authThatCanContinue;
+        this.partialSuccess = partialSuccess;
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_USERAUTH_FAILURE);
+        tw.writeNameList(authThatCanContinue.toArray(new String[authThatCanContinue.size()]));
+        tw.writeBoolean(partialSuccess);
+        payload = tw.getBytes();
+    }
+
+    public PacketUserauthFailure(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_USERAUTH_FAILURE) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        authThatCanContinue = new HashSet<String>(Arrays.asList(tr.readNameList()));
+        partialSuccess = tr.readBoolean();
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+
+    public Set<String> getAuthThatCanContinue() {
+        return authThatCanContinue;
+    }
+
+    public boolean isPartialSuccess() {
+        return partialSuccess;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthInfoRequest.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthInfoRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthInfoRequest {
+
+    private final String name;
+    private final String instruction;
+    private final String languageTag;
+    private final int numPrompts;
+
+    private final String prompt[];
+    private final boolean echo[];
+
+    public PacketUserauthInfoRequest(byte payload[]) throws IOException {
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_USERAUTH_INFO_REQUEST) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        name = tr.readString();
+        instruction = tr.readString();
+        languageTag = tr.readString();
+        numPrompts = tr.readUINT32();
+        prompt = new String[numPrompts];
+        echo = new boolean[numPrompts];
+
+        for (int i = 0; i < numPrompts; i++) {
+            prompt[i] = tr.readString();
+            echo[i] = tr.readBoolean();
+        }
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public boolean[] getEcho() {
+        return echo;
+    }
+
+    public String getInstruction() {
+        return instruction;
+    }
+
+    public String getLanguageTag() {
+        return languageTag;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getNumPrompts() {
+        return numPrompts;
+    }
+
+    public String[] getPrompt() {
+        return prompt;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthInfoResponse.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthInfoResponse.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthInfoResponse {
+    private final byte[] payload;
+
+    public PacketUserauthInfoResponse(String[] responses) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_USERAUTH_INFO_RESPONSE);
+        tw.writeUINT32(responses.length);
+
+        for (String response : responses) {
+            tw.writeString(response);
+        }
+
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthRequestInteractive.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthRequestInteractive.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthRequestInteractive {
+
+    private final byte[] payload;
+
+    public PacketUserauthRequestInteractive(String serviceName, String user, String[] submethods) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+        tw.writeString(user);
+        tw.writeString(serviceName);
+        tw.writeString("keyboard-interactive");
+        tw.writeString(""); // draft-ietf-secsh-newmodes-04.txt says that
+        // the language tag should be empty.
+        tw.writeNameList(null == submethods ? new String[] {} : submethods);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthRequestNone.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthRequestNone.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthRequestNone {
+
+    private final byte[] payload;
+
+    public PacketUserauthRequestNone(String serviceName, String user) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+        tw.writeString(user);
+        tw.writeString(serviceName);
+        tw.writeString("none");
+        payload = tw.getBytes();
+    }
+
+    public PacketUserauthRequestNone(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        String userName = tr.readString();
+        String serviceName = tr.readString();
+        String method = tr.readString();
+
+        if (!method.equals("none")) {
+            throw new IOException(String.format("Unexpected method %s", method));
+        }
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthRequestPassword.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthRequestPassword.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthRequestPassword {
+
+    private final byte[] payload;
+
+    public PacketUserauthRequestPassword(String serviceName, String user, String pass) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+        tw.writeString(user);
+        tw.writeString(serviceName);
+        tw.writeString("password");
+        tw.writeBoolean(false);
+        tw.writeString(pass);
+        payload = tw.getBytes();
+    }
+
+    public PacketUserauthRequestPassword(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        String userName = tr.readString();
+        String serviceName = tr.readString();
+        String method = tr.readString();
+
+        if (!method.equals("password")) {
+            throw new IOException(String.format("Unexpected method %s", method));
+        }
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthRequestPublicKey.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthRequestPublicKey.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthRequestPublicKey {
+
+    private final byte[] payload;
+
+    public PacketUserauthRequestPublicKey(String serviceName, String user,
+                                          String pkAlgorithmName, byte[] pk, byte[] sig) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+        tw.writeString(user);
+        tw.writeString(serviceName);
+        tw.writeString("publickey");
+        tw.writeBoolean(true);
+        tw.writeString(pkAlgorithmName);
+        tw.writeString(pk, 0, pk.length);
+        tw.writeString(sig, 0, sig.length);
+        payload = tw.getBytes();
+    }
+
+    public PacketUserauthRequestPublicKey(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        String userName = tr.readString();
+        String serviceName = tr.readString();
+        String method = tr.readString();
+
+        if (!method.equals("publickey")) {
+            throw new IOException(String.format("Unexpected method %s", method));
+        }
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthSuccess.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthSuccess.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthSuccess {
+
+    private final byte[] payload;
+
+    public PacketUserauthSuccess() {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_USERAUTH_SUCCESS);
+        payload = tw.getBytes();
+    }
+
+    public PacketUserauthSuccess(byte payload[]) throws IOException {
+        this.payload = payload;
+        TypesReader tr = new TypesReader(payload);
+        int packet_type = tr.readByte();
+
+        if (packet_type != Packets.SSH_MSG_USERAUTH_SUCCESS) {
+            throw new PacketTypeException(packet_type);
+        }
+
+        if (tr.remain() != 0) {
+            throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+        }
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketWindowChange.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,30 @@
+package ch.ethz.ssh2.packets;
+
+/**
+ * Indicates that that size of the terminal (window) size has changed on the client side.
+ * <p/>
+ * See section 6.7 of RFC 4254.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public final class PacketWindowChange {
+    private final byte[] payload;
+
+    public PacketWindowChange(int recipientChannelID,
+                              int character_width, int character_height, int pixel_width, int pixel_height) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+        tw.writeUINT32(recipientChannelID);
+        tw.writeString("window-change");
+        tw.writeBoolean(false);
+        tw.writeUINT32(character_width);
+        tw.writeUINT32(character_height);
+        tw.writeUINT32(pixel_width);
+        tw.writeUINT32(pixel_height);
+        payload = tw.getBytes();
+    }
+
+    public byte[] getPayload() {
+        return payload;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/Packets.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * Packets.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class Packets {
+    public static final int SSH_MSG_DISCONNECT = 1;
+    public static final int SSH_MSG_IGNORE = 2;
+    public static final int SSH_MSG_UNIMPLEMENTED = 3;
+    public static final int SSH_MSG_DEBUG = 4;
+    public static final int SSH_MSG_SERVICE_REQUEST = 5;
+    public static final int SSH_MSG_SERVICE_ACCEPT = 6;
+
+    public static final int SSH_MSG_KEXINIT = 20;
+    public static final int SSH_MSG_NEWKEYS = 21;
+
+    public static final int SSH_MSG_KEXDH_INIT = 30;
+    public static final int SSH_MSG_KEXDH_REPLY = 31;
+
+    public static final int SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30;
+    public static final int SSH_MSG_KEX_DH_GEX_REQUEST = 34;
+    public static final int SSH_MSG_KEX_DH_GEX_GROUP = 31;
+    public static final int SSH_MSG_KEX_DH_GEX_INIT = 32;
+    public static final int SSH_MSG_KEX_DH_GEX_REPLY = 33;
+
+    public static final int SSH_MSG_USERAUTH_REQUEST = 50;
+    public static final int SSH_MSG_USERAUTH_FAILURE = 51;
+    public static final int SSH_MSG_USERAUTH_SUCCESS = 52;
+    public static final int SSH_MSG_USERAUTH_BANNER = 53;
+    public static final int SSH_MSG_USERAUTH_INFO_REQUEST = 60;
+    public static final int SSH_MSG_USERAUTH_INFO_RESPONSE = 61;
+
+    public static final int SSH_MSG_GLOBAL_REQUEST = 80;
+    public static final int SSH_MSG_REQUEST_SUCCESS = 81;
+    public static final int SSH_MSG_REQUEST_FAILURE = 82;
+
+    public static final int SSH_MSG_CHANNEL_OPEN = 90;
+    public static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91;
+    public static final int SSH_MSG_CHANNEL_OPEN_FAILURE = 92;
+    public static final int SSH_MSG_CHANNEL_WINDOW_ADJUST = 93;
+    public static final int SSH_MSG_CHANNEL_DATA = 94;
+    public static final int SSH_MSG_CHANNEL_EXTENDED_DATA = 95;
+    public static final int SSH_MSG_CHANNEL_EOF = 96;
+    public static final int SSH_MSG_CHANNEL_CLOSE = 97;
+    public static final int SSH_MSG_CHANNEL_REQUEST = 98;
+    public static final int SSH_MSG_CHANNEL_SUCCESS = 99;
+    public static final int SSH_MSG_CHANNEL_FAILURE = 100;
+
+    public static final int SSH_EXTENDED_DATA_STDERR = 1;
+
+    public static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1;
+    public static final int SSH_OPEN_CONNECT_FAILED = 2;
+    public static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3;
+    public static final int SSH_OPEN_RESOURCE_SHORTAGE = 4;
+
+    private static final String[] reverseNames = new String[101];
+
+    static {
+        reverseNames[1] = "SSH_MSG_DISCONNECT";
+        reverseNames[2] = "SSH_MSG_IGNORE";
+        reverseNames[3] = "SSH_MSG_UNIMPLEMENTED";
+        reverseNames[4] = "SSH_MSG_DEBUG";
+        reverseNames[5] = "SSH_MSG_SERVICE_REQUEST";
+        reverseNames[6] = "SSH_MSG_SERVICE_ACCEPT";
+        reverseNames[20] = "SSH_MSG_KEXINIT";
+        reverseNames[21] = "SSH_MSG_NEWKEYS";
+        reverseNames[30] = "SSH_MSG_KEXDH_INIT";
+        reverseNames[31] = "SSH_MSG_KEXDH_REPLY/SSH_MSG_KEX_DH_GEX_GROUP";
+        reverseNames[32] = "SSH_MSG_KEX_DH_GEX_INIT";
+        reverseNames[33] = "SSH_MSG_KEX_DH_GEX_REPLY";
+        reverseNames[34] = "SSH_MSG_KEX_DH_GEX_REQUEST";
+        reverseNames[50] = "SSH_MSG_USERAUTH_REQUEST";
+        reverseNames[51] = "SSH_MSG_USERAUTH_FAILURE";
+        reverseNames[52] = "SSH_MSG_USERAUTH_SUCCESS";
+        reverseNames[53] = "SSH_MSG_USERAUTH_BANNER";
+        reverseNames[60] = "SSH_MSG_USERAUTH_INFO_REQUEST";
+        reverseNames[61] = "SSH_MSG_USERAUTH_INFO_RESPONSE";
+        reverseNames[80] = "SSH_MSG_GLOBAL_REQUEST";
+        reverseNames[81] = "SSH_MSG_REQUEST_SUCCESS";
+        reverseNames[82] = "SSH_MSG_REQUEST_FAILURE";
+        reverseNames[90] = "SSH_MSG_CHANNEL_OPEN";
+        reverseNames[91] = "SSH_MSG_CHANNEL_OPEN_CONFIRMATION";
+        reverseNames[92] = "SSH_MSG_CHANNEL_OPEN_FAILURE";
+        reverseNames[93] = "SSH_MSG_CHANNEL_WINDOW_ADJUST";
+        reverseNames[94] = "SSH_MSG_CHANNEL_DATA";
+        reverseNames[95] = "SSH_MSG_CHANNEL_EXTENDED_DATA";
+        reverseNames[96] = "SSH_MSG_CHANNEL_EOF";
+        reverseNames[97] = "SSH_MSG_CHANNEL_CLOSE";
+        reverseNames[98] = "SSH_MSG_CHANNEL_REQUEST";
+        reverseNames[99] = "SSH_MSG_CHANNEL_SUCCESS";
+        reverseNames[100] = "SSH_MSG_CHANNEL_FAILURE";
+    }
+
+    public static final String getMessageName(int type) {
+        String res = null;
+
+        if ((type >= 0) && (type < reverseNames.length)) {
+            res = reverseNames[type];
+        }
+
+        return (res == null) ? ("UNKNOWN MSG " + type) : res;
+    }
+
+    //  public static final void debug(String tag, byte[] msg)
+    //  {
+    //      System.err.println(tag + " Type: " + msg[0] + ", LEN: " + msg.length);
+    //
+    //      for (int i = 0; i < msg.length; i++)
+    //      {
+    //          if (((msg[i] >= 'a') && (msg[i] <= 'z')) || ((msg[i] >= 'A') && (msg[i] <= 'Z'))
+    //                  || ((msg[i] >= '0') && (msg[i] <= '9')) || (msg[i] == ' '))
+    //              System.err.print((char) msg[i]);
+    //          else
+    //              System.err.print(".");
+    //      }
+    //      System.err.println();
+    //      System.err.flush();
+    //  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/TypesReader.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: TypesReader.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class TypesReader {
+    byte[] arr;
+    int pos = 0;
+    int max = 0;
+
+    public TypesReader(byte[] arr) {
+        this.arr = arr;
+        pos = 0;
+        max = arr.length;
+    }
+
+    public TypesReader(byte[] arr, int off) {
+        this.arr = arr;
+        this.pos = off;
+        this.max = arr.length;
+
+        if ((pos < 0) || (pos > arr.length)) {
+            throw new IllegalArgumentException("Illegal offset.");
+        }
+    }
+
+    public TypesReader(byte[] arr, int off, int len) {
+        this.arr = arr;
+        this.pos = off;
+        this.max = off + len;
+
+        if ((pos < 0) || (pos > arr.length)) {
+            throw new IllegalArgumentException("Illegal offset.");
+        }
+
+        if ((max < 0) || (max > arr.length)) {
+            throw new IllegalArgumentException("Illegal length.");
+        }
+    }
+
+    public int readByte() throws IOException {
+        if (pos >= max) {
+            throw new PacketFormatException("Packet too short.");
+        }
+
+        return (arr[pos++] & 0xff);
+    }
+
+    public byte[] readBytes(int len) throws IOException {
+        if ((pos + len) > max) {
+            throw new PacketFormatException("Packet too short.");
+        }
+
+        byte[] res = new byte[len];
+        System.arraycopy(arr, pos, res, 0, len);
+        pos += len;
+        return res;
+    }
+
+    public void readBytes(byte[] dst, int off, int len) throws IOException {
+        if ((pos + len) > max) {
+            throw new PacketFormatException("Packet too short.");
+        }
+
+        System.arraycopy(arr, pos, dst, off, len);
+        pos += len;
+    }
+
+    public boolean readBoolean() throws IOException {
+        if (pos >= max) {
+            throw new PacketFormatException("Packet too short.");
+        }
+
+        return (arr[pos++] != 0);
+    }
+
+    public int readUINT32() throws IOException {
+        if ((pos + 4) > max) {
+            throw new PacketFormatException("Packet too short.");
+        }
+
+        return ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
+               | (arr[pos++] & 0xff);
+    }
+
+    public long readUINT64() throws IOException {
+        if ((pos + 8) > max) {
+            throw new PacketFormatException("Packet too short.");
+        }
+
+        long high = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
+                    | (arr[pos++] & 0xff); /* sign extension may take place - will be shifted away =) */
+        long low = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
+                   | (arr[pos++] & 0xff); /* sign extension may take place - handle below */
+        return (high << 32) | (low & 0xffffffffl); /* see Java language spec (15.22.1, 5.6.2) */
+    }
+
+    public BigInteger readMPINT() throws IOException {
+        BigInteger b;
+        byte raw[] = readByteString();
+
+        if (raw.length == 0) {
+            b = BigInteger.ZERO;
+        }
+        else {
+            b = new BigInteger(raw);
+        }
+
+        return b;
+    }
+
+    public byte[] readByteString() throws IOException {
+        int len = readUINT32();
+
+        if ((len + pos) > max) {
+            throw new PacketFormatException("Malformed SSH byte string.");
+        }
+
+        byte[] res = new byte[len];
+        System.arraycopy(arr, pos, res, 0, len);
+        pos += len;
+        return res;
+    }
+
+    public String readString(String charsetName) throws IOException {
+        int len = readUINT32();
+
+        if ((len + pos) > max) {
+            throw new PacketFormatException("Malformed SSH string.");
+        }
+
+        String res = (charsetName == null) ? new String(arr, pos, len) : new String(arr, pos, len, charsetName);
+        pos += len;
+        return res;
+    }
+
+    public String readString() throws IOException {
+        int len = readUINT32();
+
+        if ((len + pos) > max) {
+            throw new PacketFormatException("Malformed SSH string.");
+        }
+
+        String res = StringEncoder.GetString(arr, pos, len);
+        pos += len;
+        return res;
+    }
+
+    public String[] readNameList() throws IOException {
+        return readString().split(",");
+    }
+
+    public int remain() {
+        return max - pos;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/TypesWriter.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: TypesWriter.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class TypesWriter {
+    byte arr[];
+    int pos;
+
+    public TypesWriter() {
+        arr = new byte[256];
+        pos = 0;
+    }
+
+    private void resize(int len) {
+        byte new_arr[] = new byte[len];
+        System.arraycopy(arr, 0, new_arr, 0, arr.length);
+        arr = new_arr;
+    }
+
+    public int length() {
+        return pos;
+    }
+
+    public byte[] getBytes() {
+        byte[] dst = new byte[pos];
+        System.arraycopy(arr, 0, dst, 0, pos);
+        return dst;
+    }
+
+    public void getBytes(byte dst[]) {
+        System.arraycopy(arr, 0, dst, 0, pos);
+    }
+
+    public void writeUINT32(int val, int off) {
+        if ((off + 4) > arr.length) {
+            resize(off + 32);
+        }
+
+        arr[off++] = (byte)(val >> 24);
+        arr[off++] = (byte)(val >> 16);
+        arr[off++] = (byte)(val >> 8);
+        arr[off++] = (byte) val;
+    }
+
+    public void writeUINT32(int val) {
+        writeUINT32(val, pos);
+        pos += 4;
+    }
+
+    public void writeUINT64(long val) {
+        if ((pos + 8) > arr.length) {
+            resize(arr.length + 32);
+        }
+
+        arr[pos++] = (byte)(val >> 56);
+        arr[pos++] = (byte)(val >> 48);
+        arr[pos++] = (byte)(val >> 40);
+        arr[pos++] = (byte)(val >> 32);
+        arr[pos++] = (byte)(val >> 24);
+        arr[pos++] = (byte)(val >> 16);
+        arr[pos++] = (byte)(val >> 8);
+        arr[pos++] = (byte) val;
+    }
+
+    public void writeBoolean(boolean v) {
+        if ((pos + 1) > arr.length) {
+            resize(arr.length + 32);
+        }
+
+        arr[pos++] = v ? (byte) 1 : (byte) 0;
+    }
+
+    public void writeByte(int v, int off) {
+        if ((off + 1) > arr.length) {
+            resize(off + 32);
+        }
+
+        arr[off] = (byte) v;
+    }
+
+    public void writeByte(int v) {
+        writeByte(v, pos);
+        pos++;
+    }
+
+    public void writeMPInt(BigInteger b) {
+        byte raw[] = b.toByteArray();
+
+        if ((raw.length == 1) && (raw[0] == 0)) {
+            writeUINT32(0); /* String with zero bytes of data */
+        }
+        else {
+            writeString(raw, 0, raw.length);
+        }
+    }
+
+    public void writeBytes(byte[] buff) {
+        writeBytes(buff, 0, buff.length);
+    }
+
+    public void writeBytes(byte[] buff, int off, int len) {
+        if ((pos + len) > arr.length) {
+            resize(arr.length + len + 32);
+        }
+
+        System.arraycopy(buff, off, arr, pos, len);
+        pos += len;
+    }
+
+    public void writeString(byte[] buff, int off, int len) {
+        writeUINT32(len);
+        writeBytes(buff, off, len);
+    }
+
+    public void writeString(String v) {
+        byte[] b = StringEncoder.GetBytes(v);
+        writeUINT32(b.length);
+        writeBytes(b, 0, b.length);
+    }
+
+    public void writeString(String v, String charsetName) throws UnsupportedEncodingException {
+        byte[] b = (charsetName == null) ? StringEncoder.GetBytes(v) : v.getBytes(charsetName);
+        writeUINT32(b.length);
+        writeBytes(b, 0, b.length);
+    }
+
+    public void writeNameList(String v[]) {
+        StringBuilder sb = new StringBuilder();
+
+        for (int i = 0; i < v.length; i++) {
+            if (i > 0) {
+                sb.append(',');
+            }
+
+            sb.append(v[i]);
+        }
+
+        writeString(sb.toString());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/server/ServerConnectionState.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.server;
+
+import java.net.Socket;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.ServerAuthenticationCallback;
+import ch.ethz.ssh2.ServerConnection;
+import ch.ethz.ssh2.ServerConnectionCallback;
+import ch.ethz.ssh2.auth.ServerAuthenticationManager;
+import ch.ethz.ssh2.channel.ChannelManager;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import ch.ethz.ssh2.transport.ClientServerHello;
+import ch.ethz.ssh2.transport.ServerTransportManager;
+
+public class ServerConnectionState {
+    public ServerConnection conn;
+
+    public SecureRandom generator = new SecureRandom();
+
+    public String softwareversion;
+
+    //public String auth_banner = null;
+    public ServerConnectionCallback cb_conn;
+    public ServerAuthenticationCallback cb_auth;
+
+    /* Settings for the next key exchange */
+    public CryptoWishList next_cryptoWishList = CryptoWishList.forServer();
+    public KeyPair next_dsa_key;
+    public KeyPair next_ec_key;
+    public KeyPair next_rsa_key;
+
+    public Socket s;
+
+    public ClientServerHello csh;
+    public ServerTransportManager tm;
+    public ServerAuthenticationManager am;
+    public ChannelManager cm;
+
+    public boolean flag_auth_serviceRequested = false;
+    public boolean flag_auth_completed = false;
+
+    public ServerConnectionState(ServerConnection conn) {
+        this.conn = conn;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AceFlags.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,17 @@
+package ch.ethz.ssh2.sftp;
+
+/**
+ * @version $Id: AceFlags.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public final class AceFlags {
+    private AceFlags() {
+    }
+
+    public static final int ACE4_FILE_INHERIT_ACE = 0x00000001;
+    public static final int ACE4_DIRECTORY_INHERIT_ACE = 0x00000002;
+    public static final int ACE4_NO_PROPAGATE_INHERIT_ACE = 0x00000004;
+    public static final int ACE4_INHERIT_ONLY_ACE = 0x00000008;
+    public static final int ACE4_SUCCESSFUL_ACCESS_ACE_FLAG = 0x00000010;
+    public static final int ACE4_FAILED_ACCESS_ACE_FLAG = 0x00000020;
+    public static final int ACE4_IDENTIFIER_GROUP = 0x00000040;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AceMask.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,28 @@
+package ch.ethz.ssh2.sftp;
+
+/**
+ * @version $Id: AceMask.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public final class AceMask {
+    private AceMask() {
+    }
+
+    public static final int ACE4_READ_DATA = 0x00000001;
+    public static final int ACE4_LIST_DIRECTORY = 0x00000001;
+    public static final int ACE4_WRITE_DATA = 0x00000002;
+    public static final int ACE4_ADD_FILE = 0x00000002;
+    public static final int ACE4_APPEND_DATA = 0x00000004;
+    public static final int ACE4_ADD_SUBDIRECTORY = 0x00000004;
+    public static final int ACE4_READ_NAMED_ATTRS = 0x00000008;
+    public static final int ACE4_WRITE_NAMED_ATTRS = 0x00000010;
+    public static final int ACE4_EXECUTE = 0x00000020;
+    public static final int ACE4_DELETE_CHILD = 0x00000040;
+    public static final int ACE4_READ_ATTRIBUTES = 0x00000080;
+    public static final int ACE4_WRITE_ATTRIBUTES = 0x00000100;
+    public static final int ACE4_DELETE = 0x00010000;
+    public static final int ACE4_READ_ACL = 0x00020000;
+    public static final int ACE4_WRITE_ACL = 0x00040000;
+    public static final int ACE4_WRITE_OWNER = 0x00080000;
+    public static final int ACE4_SYNCHRONIZE = 0x00100000;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AceType.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,16 @@
+package ch.ethz.ssh2.sftp;
+
+/**
+ * @version $Id: AceType.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public final class AceType {
+
+    private AceType() {
+    }
+
+    private static final int ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000;
+    private static final int ACE4_ACCESS_DENIED_ACE_TYPE = 0x00000001;
+    private static final int ACE4_SYSTEM_AUDIT_ACE_TYPE = 0x00000002;
+    private static final int ACE4_SYSTEM_ALARM_ACE_TYPE = 0x00000003;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AclFlags.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,45 @@
+package ch.ethz.ssh2.sftp;
+
+/**
+ * @version $Id: AclFlags.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public final class AclFlags {
+
+    /**
+     * If INCLUDED is set during a setstat operation, then the client
+     * intends to modify the ALLOWED/DENIED entries of the ACL.
+     * Otherwise, the client intends for these entries to be
+     * preserved.
+     */
+    public static final int SFX_ACL_CONTROL_INCLUDED = 0x00000001;
+    /**
+     * If the PRESENT bit is not set, then the client wishes to remove
+     * control entries.  If the server doesn't support separate
+     * control and audit information, the client MUST not clear this
+     * bit without also clearing the AUDIT_ALARM_PRESENT bit.
+     */
+    public static final int SFX_ACL_CONTROL_PRESENT = 0x00000002;
+    /**
+     * If INHERITED is set, then ALLOW/DENY ACEs MAY be inherited from
+     * the parent directory.  If it is off, then they MUST not be
+     * INHERITED.  If the server does not support controlling
+     * inheritance, then the client MUST clear this bit; in this case
+     * the inheritance properties of the server are undefined.
+     */
+    public static final int SFX_ACL_CONTROL_INHERITED = 0x00000004;
+    /**
+     * If INCLUDE is set during a setstat operation, then the client
+     * intends to modify the AUDIT/ALARM entries of the ACL.
+     * Otherwise, the client intends for these entries to be
+     * preserved.
+     */
+    public static final int SFX_ACL_AUDIT_ALARM_INCLUDED = 0x00000010;
+    /**
+     * If INHERITED is set, then AUDIT/ALARM ACEs MAY be inherited
+     * from the parent directory.  If it is off, then they MUST not be
+     * INHERITED.  If the server does not support controlling
+     * inheritance, then the client MUST clear this bit; in this case
+     * the inheritance properties of the server are undefined.
+     */
+    public static final int SFX_ACL_AUDIT_ALARM_INHERITED = 0x00000020;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AttrTextHints.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ *
+ * Values for the 'text-hint' field in the SFTP ATTRS data type.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ *
+ */
+public class AttrTextHints {
+    /**
+     * The server knows the file is a text file, and should be opened
+     * using the SSH_FXF_ACCESS_TEXT_MODE flag.
+     */
+    public static final int SSH_FILEXFER_ATTR_KNOWN_TEXT = 0x00;
+
+    /**
+     * The server has applied a heuristic or other mechanism and
+     * believes that the file should be opened with the
+     * SSH_FXF_ACCESS_TEXT_MODE flag.
+     */
+    public static final int SSH_FILEXFER_ATTR_GUESSED_TEXT = 0x01;
+
+    /**
+     * The server knows the file has binary content.
+     */
+    public static final int SSH_FILEXFER_ATTR_KNOWN_BINARY = 0x02;
+
+    /**
+     * The server has applied a heuristic or other mechanism and
+     * believes has binary content, and should not be opened with the
+     * SSH_FXF_ACCESS_TEXT_MODE flag.
+     */
+    public static final int SSH_FILEXFER_ATTR_GUESSED_BINARY = 0x03;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AttribBits.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * SFTP Attribute Bits for the "attrib-bits" and "attrib-bits-valid" fields
+ * of the SFTP ATTR data type.
+ * <p/>
+ * Yes, these are the "attrib-bits", even though they have "_FLAGS_" in
+ * their name. Don't ask - I did not invent it.
+ * <p/>
+ * "<i>These fields, taken together, reflect various attributes of the file
+ * or directory, on the server. Bits not set in 'attrib-bits-valid' MUST be
+ * ignored in the 'attrib-bits' field.  This allows both the server and the
+ * client to communicate only the bits it knows about without inadvertently
+ * twiddling bits they don't understand.</i>"
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class AttribBits {
+
+    private AttribBits() {
+    }
+
+    /**
+     * Advisory, read-only bit. This bit is not part of the access
+     * control information on the file, but is rather an advisory field
+     * indicating that the file should not be written.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_READONLY = 0x00000001;
+
+    /**
+     * The file is part of the operating system.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_SYSTEM = 0x00000002;
+
+    /**
+     * File SHOULD NOT be shown to user unless specifically requested.
+     * For example, most UNIX systems SHOULD set this bit if the filename
+     * begins with a 'period'. This bit may be read-only (see section 5.4 of
+     * the SFTP standard draft). Most UNIX systems will not allow this to be
+     * changed.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_HIDDEN = 0x00000004;
+
+    /**
+     * This attribute applies only to directories. This attribute is
+     * always read-only, and cannot be modified. This attribute means
+     * that files and directory names in this directory should be compared
+     * without regard to case.
+     * <p/>
+     * It is recommended that where possible, the server's filesystem be
+     * allowed to do comparisons. For example, if a client wished to prompt
+     * a user before overwriting a file, it should not compare the new name
+     * with the previously retrieved list of names in the directory. Rather,
+     * it should first try to create the new file by specifying
+     * SSH_FXF_CREATE_NEW flag. Then, if this fails and returns
+     * SSH_FX_FILE_ALREADY_EXISTS, it should prompt the user and then retry
+     * the create specifying SSH_FXF_CREATE_TRUNCATE.
+     * <p/>
+     * Unless otherwise specified, filenames are assumed to be case sensitive.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE = 0x00000008;
+
+    /**
+     * The file should be included in backup / archive operations.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_ARCHIVE = 0x00000010;
+
+    /**
+     * The file is stored on disk using file-system level transparent
+     * encryption. This flag does not affect the file data on the wire
+     * (for either READ or WRITE requests.)
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED = 0x00000020;
+
+    /**
+     * The file is stored on disk using file-system level transparent
+     * compression. This flag does not affect the file data on the wire.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_COMPRESSED = 0x00000040;
+
+    /**
+     * The file is a sparse file; this means that file blocks that have
+     * not been explicitly written are not stored on disk. For example, if
+     * a client writes a buffer at 10 M from the beginning of the file,
+     * the blocks between the previous EOF marker and the 10 M offset would
+     * not consume physical disk space.
+     * <p/>
+     * Some servers may store all files as sparse files, in which case
+     * this bit will be unconditionally set. Other servers may not have
+     * a mechanism for determining if the file is sparse, and so the file
+     * MAY be stored sparse even if this flag is not set.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_SPARSE = 0x00000080;
+
+    /**
+     * Opening the file without either the SSH_FXF_ACCESS_APPEND_DATA or
+     * the SSH_FXF_ACCESS_APPEND_DATA_ATOMIC flag (see section 8.1.1.3
+     * of the SFTP standard draft) MUST result in an
+     * SSH_FX_INVALID_PARAMETER error.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_APPEND_ONLY = 0x00000100;
+
+    /**
+     * The file cannot be deleted or renamed, no hard link can be created
+     * to this file, and no data can be written to the file.
+     * <p/>
+     * This bit implies a stronger level of protection than
+     * SSH_FILEXFER_ATTR_FLAGS_READONLY, the file permission mask or ACLs.
+     * Typically even the superuser cannot write to immutable files, and
+     * only the superuser can set or remove the bit.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_IMMUTABLE = 0x00000200;
+
+    /**
+     * When the file is modified, the changes are written synchronously
+     * to the disk.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_SYNC = 0x00000400;
+
+    /**
+     * The server MAY include this bit in a directory listing or realpath
+     * response. It indicates there was a failure in the translation to UTF-8.
+     * If this flag is included, the server SHOULD also include the
+     * UNTRANSLATED_NAME attribute.
+     */
+    public static final int SSH_FILEXFER_ATTR_FLAGS_TRANSLATION_ERR = 0x00000800;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AttribFlags.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * Attribute Flags. The 'valid-attribute-flags' field in
+ * the SFTP ATTRS data type specifies which of the fields are actually present.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class AttribFlags {
+
+    private AttribFlags() {
+    }
+
+    /**
+     * Indicates that the 'allocation-size' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
+
+    /**
+     * Protocol version 6:
+     * 0x00000002 was used in a previous version of this protocol.
+     * It is now a reserved value and MUST NOT appear in the mask.
+     * Some future version of this protocol may reuse this value.
+     */
+    public static final int SSH_FILEXFER_ATTR_V3_UIDGID = 0x00000002;
+
+    /**
+     * Indicates that the 'permissions' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
+
+    /**
+     * Indicates that the 'atime' and 'mtime' field are present
+     * (protocol v3).
+     */
+    public static final int SSH_FILEXFER_ATTR_V3_ACMODTIME = 0x00000008;
+
+    /**
+     * Indicates that the 'atime' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008;
+
+    /**
+     * Indicates that the 'createtime' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010;
+
+    /**
+     * Indicates that the 'mtime' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020;
+
+    /**
+     * Indicates that the 'acl' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040;
+
+    /**
+     * Indicates that the 'owner' and 'group' fields are present.
+     */
+    public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080;
+
+    /**
+     * Indicates that additionally to the 'atime', 'createtime',
+     * 'mtime' and 'ctime' fields (if present), there is also
+     * 'atime-nseconds', 'createtime-nseconds',  'mtime-nseconds'
+     * and 'ctime-nseconds'.
+     */
+    public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100;
+
+    /**
+     * Indicates that the 'attrib-bits' and 'attrib-bits-valid'
+     * fields are present.
+     */
+    public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200;
+
+    /**
+     * Indicates that the 'allocation-size' field is present. Field specifies the number of bytes that the
+     * file consumes on disk.
+     */
+    public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400;
+
+    /**
+     * Indicates that the 'text-hint' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800;
+
+    /**
+     * Indicates that the 'mime-type' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000;
+
+    /**
+     * Indicates that the 'link-count' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000;
+
+    /**
+     * Indicates that the 'untranslated-name' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000;
+
+    /**
+     * Indicates that the 'ctime' field is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000;
+
+    /**
+     * Indicates that the 'extended-count' field (and probablby some
+     * 'extensions') is present.
+     */
+    public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AttribPermissions.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * Permissions for the 'permissions' field in the SFTP ATTRS data type.
+ * <p/>
+ * "<i>The 'permissions' field contains a bit mask specifying file permissions.
+ * These permissions correspond to the st_mode field of the stat structure
+ * defined by POSIX [IEEE.1003-1.1996].</i>"
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class AttribPermissions {
+    private AttribPermissions() {
+    }
+
+    /* Octal values! */
+
+    public static final int S_IRUSR = 0400;
+    public static final int S_IWUSR = 0200;
+    public static final int S_IXUSR = 0100;
+    public static final int S_IRGRP = 0040;
+    public static final int S_IWGRP = 0020;
+    public static final int S_IXGRP = 0010;
+    public static final int S_IROTH = 0004;
+    public static final int S_IWOTH = 0002;
+    public static final int S_IXOTH = 0001;
+    public static final int S_ISUID = 04000;
+    public static final int S_ISGID = 02000;
+    public static final int S_ISVTX = 01000;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AttribTypes.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * Types for the 'type' field in the SFTP ATTRS data type.
+ * <p/>
+ * "<i>On a POSIX system, these values would be derived from the mode field
+ * of the stat structure.  SPECIAL should be used for files that are of
+ * a known type which cannot be expressed in the protocol. UNKNOWN
+ * should be used if the type is not known.</i>"
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class AttribTypes {
+    private AttribTypes() {
+    }
+
+    public static final int SSH_FILEXFER_TYPE_REGULAR = 1;
+    public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2;
+    public static final int SSH_FILEXFER_TYPE_SYMLINK = 3;
+    public static final int SSH_FILEXFER_TYPE_SPECIAL = 4;
+    /**
+     * Should be used if the type is not known.
+     */
+    public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5;
+    public static final int SSH_FILEXFER_TYPE_SOCKET = 6;
+    public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7;
+    public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8;
+    public static final int SSH_FILEXFER_TYPE_FIFO = 9;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/ErrorCodes.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * SFTP Error Codes
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class ErrorCodes {
+    private ErrorCodes() {
+    }
+
+    public static final int SSH_FX_OK = 0;
+    public static final int SSH_FX_EOF = 1;
+    public static final int SSH_FX_NO_SUCH_FILE = 2;
+    public static final int SSH_FX_PERMISSION_DENIED = 3;
+    public static final int SSH_FX_FAILURE = 4;
+    public static final int SSH_FX_BAD_MESSAGE = 5;
+    public static final int SSH_FX_NO_CONNECTION = 6;
+    public static final int SSH_FX_CONNECTION_LOST = 7;
+    /**
+     * The server MUST respond with SSH_FXP_STATUS(SSH_FX_OP_UNSUPPORTED) if
+     * it receives a packet it does not recognize.
+     */
+    public static final int SSH_FX_OP_UNSUPPORTED = 8;
+    public static final int SSH_FX_INVALID_HANDLE = 9;
+    public static final int SSH_FX_NO_SUCH_PATH = 10;
+    public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
+    public static final int SSH_FX_WRITE_PROTECT = 12;
+    public static final int SSH_FX_NO_MEDIA = 13;
+    public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14;
+    public static final int SSH_FX_QUOTA_EXCEEDED = 15;
+    public static final int SSH_FX_UNKNOWN_PRINCIPAL = 16;
+    public static final int SSH_FX_LOCK_CONFLICT = 17;
+    public static final int SSH_FX_DIR_NOT_EMPTY = 18;
+    public static final int SSH_FX_NOT_A_DIRECTORY = 19;
+    public static final int SSH_FX_INVALID_FILENAME = 20;
+    public static final int SSH_FX_LINK_LOOP = 21;
+    public static final int SSH_FX_CANNOT_DELETE = 22;
+    public static final int SSH_FX_INVALID_PARAMETER = 23;
+    public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24;
+    public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25;
+    public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26;
+    public static final int SSH_FX_DELETE_PENDING = 27;
+    public static final int SSH_FX_FILE_CORRUPT = 28;
+    public static final int SSH_FX_OWNER_INVALID = 29;
+    public static final int SSH_FX_GROUP_INVALID = 30;
+    public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31;
+
+    private static final String[][] messages = {
+
+        {"SSH_FX_OK", "Indicates successful completion of the operation."},
+        {
+            "SSH_FX_EOF",
+            "An attempt to read past the end-of-file was made; or, there are no more directory entries to return."
+        },
+        {"SSH_FX_NO_SUCH_FILE", "A reference was made to a file which does not exist."},
+        {"SSH_FX_PERMISSION_DENIED", "The user does not have sufficient permissions to perform the operation."},
+        {"SSH_FX_FAILURE", "An error occurred, but no specific error code exists to describe the failure."},
+        {"SSH_FX_BAD_MESSAGE", "A badly formatted packet or other SFTP protocol incompatibility was detected."},
+        {"SSH_FX_NO_CONNECTION", "There is no connection to the server."},
+        {"SSH_FX_CONNECTION_LOST", "The connection to the server was lost."},
+        {
+            "SSH_FX_OP_UNSUPPORTED",
+            "An attempted operation could not be completed by the server because the server does not support the operation."
+        },
+        {"SSH_FX_INVALID_HANDLE", "The handle value was invalid."},
+        {"SSH_FX_NO_SUCH_PATH", "The file path does not exist or is invalid."},
+        {"SSH_FX_FILE_ALREADY_EXISTS", "The file already exists."},
+        {"SSH_FX_WRITE_PROTECT", "The file is on read-only media, or the media is write protected."},
+        {
+            "SSH_FX_NO_MEDIA",
+            "The requested operation cannot be completed because there is no media available in the drive."
+        },
+        {
+            "SSH_FX_NO_SPACE_ON_FILESYSTEM",
+            "The requested operation cannot be completed because there is insufficient free space on the filesystem."
+        },
+        {
+            "SSH_FX_QUOTA_EXCEEDED",
+            "The operation cannot be completed because it would exceed the user's storage quota."
+        },
+        {
+            "SSH_FX_UNKNOWN_PRINCIPAL",
+            "A principal referenced by the request (either the 'owner', 'group', or 'who' field of an ACL), was unknown. The error specific data contains the problematic names."
+        },
+        {"SSH_FX_LOCK_CONFLICT", "The file could not be opened because it is locked by another process."},
+        {"SSH_FX_DIR_NOT_EMPTY", "The directory is not empty."},
+        {"SSH_FX_NOT_A_DIRECTORY", "The specified file is not a directory."},
+        {"SSH_FX_INVALID_FILENAME", "The filename is not valid."},
+        {
+            "SSH_FX_LINK_LOOP",
+            "Too many symbolic links encountered or, an SSH_FXF_NOFOLLOW open encountered a symbolic link as the final component."
+        },
+        {
+            "SSH_FX_CANNOT_DELETE",
+            "The file cannot be deleted. One possible reason is that the advisory READONLY attribute-bit is set."
+        },
+        {
+            "SSH_FX_INVALID_PARAMETER",
+            "One of the parameters was out of range, or the parameters specified cannot be used together."
+        },
+        {
+            "SSH_FX_FILE_IS_A_DIRECTORY",
+            "The specified file was a directory in a context where a directory cannot be used."
+        },
+        {
+            "SSH_FX_BYTE_RANGE_LOCK_CONFLICT",
+            " A read or write operation failed because another process's mandatory byte-range lock overlaps with the request."
+        },
+        {"SSH_FX_BYTE_RANGE_LOCK_REFUSED", "A request for a byte range lock was refused."},
+        {"SSH_FX_DELETE_PENDING", "An operation was attempted on a file for which a delete operation is pending."},
+        {"SSH_FX_FILE_CORRUPT", "The file is corrupt; an filesystem integrity check should be run."},
+        {"SSH_FX_OWNER_INVALID", "The principal specified can not be assigned as an owner of a file."},
+        {"SSH_FX_GROUP_INVALID", "The principal specified can not be assigned as the primary group of a file."},
+        {
+            "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK",
+            "The requested operation could not be completed because the	specifed byte range lock has not been granted."
+        },
+
+    };
+
+    public static final String[] getDescription(int errorCode) {
+        if ((errorCode < 0) || (errorCode >= messages.length)) {
+            return null;
+        }
+
+        return messages[errorCode];
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/OpenFlags.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * SFTP Open Flags.
+ * <p/>
+ * The following table is provided to assist in mapping POSIX semantics
+ * to equivalent SFTP file open parameters:
+ * <p/>
+ * <ul>
+ * <li>O_RDONLY
+ * <ul><li>desired-access = READ_DATA | READ_ATTRIBUTES</li></ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_WRONLY
+ * <ul><li>desired-access = WRITE_DATA | WRITE_ATTRIBUTES</li></ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_RDWR
+ * <ul><li>desired-access = READ_DATA | READ_ATTRIBUTES | WRITE_DATA | WRITE_ATTRIBUTES</li></ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_APPEND
+ * <ul>
+ * <li>desired-access = WRITE_DATA | WRITE_ATTRIBUTES | APPEND_DATA</li>
+ * <li>flags = SSH_FXF_ACCESS_APPEND_DATA and or SSH_FXF_ACCESS_APPEND_DATA_ATOMIC</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_CREAT
+ * <ul>
+ * <li>flags = SSH_FXF_OPEN_OR_CREATE</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_TRUNC
+ * <ul>
+ * <li>flags = SSH_FXF_TRUNCATE_EXISTING</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_TRUNC|O_CREATE
+ * <ul>
+ * <li>flags = SSH_FXF_CREATE_TRUNCATE</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class OpenFlags {
+    private OpenFlags() {
+    }
+
+    /**
+     * Disposition is a 3 bit field that controls how the file is opened.
+     * The server MUST support these bits (possible enumaration values:
+     * SSH_FXF_CREATE_NEW, SSH_FXF_CREATE_TRUNCATE, SSH_FXF_OPEN_EXISTING,
+     * SSH_FXF_OPEN_OR_CREATE or SSH_FXF_TRUNCATE_EXISTING).
+     */
+    public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007;
+
+    /**
+     * A new file is created; if the file already exists, the server
+     * MUST return status SSH_FX_FILE_ALREADY_EXISTS.
+     */
+    public static final int SSH_FXF_CREATE_NEW = 0x00000000;
+
+    /**
+     * A new file is created; if the file already exists, it is opened
+     * and truncated.
+     */
+    public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001;
+
+    /**
+     * An existing file is opened.  If the file does not exist, the
+     * server MUST return SSH_FX_NO_SUCH_FILE. If a directory in the
+     * path does not exist, the server SHOULD return
+     * SSH_FX_NO_SUCH_PATH. It is also acceptable if the server
+     * returns SSH_FX_NO_SUCH_FILE in this case.
+     */
+    public static final int SSH_FXF_OPEN_EXISTING = 0x00000002;
+
+    /**
+     * If the file exists, it is opened. If the file does not exist,
+     * it is created.
+     */
+    public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003;
+
+    /**
+     * An existing file is opened and truncated. If the file does not
+     * exist, the server MUST return the same error codes as defined
+     * for SSH_FXF_OPEN_EXISTING.
+     */
+    public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004;
+
+    /**
+     * Data is always written at the end of the file. The offset field
+     * of the SSH_FXP_WRITE requests are ignored.
+     * <p/>
+     * Data is not required to be appended atomically. This means that
+     * if multiple writers attempt to append data simultaneously, data
+     * from the first may be lost. However, data MAY be appended
+     * atomically.
+     */
+    public static final int SSH_FXF_ACCESS_APPEND_DATA = 0x00000008;
+
+    /**
+     * Data is always written at the end of the file. The offset field
+     * of the SSH_FXP_WRITE requests are ignored.
+     * <p/>
+     * Data MUST be written atomically so that there is no chance that
+     * multiple appenders can collide and result in data being lost.
+     * <p/>
+     * If both append flags are specified, the server SHOULD use atomic
+     * append if it is available, but SHOULD use non-atomic appends
+     * otherwise. The server SHOULD NOT fail the request in this case.
+     */
+    public static final int SSH_FXF_ACCESS_APPEND_DATA_ATOMIC = 0x00000010;
+
+    /**
+     * Indicates that the server should treat the file as text and
+     * convert it to the canonical newline convention in use.
+     * (See Determining Server Newline Convention in section 5.3 in the
+     * SFTP standard draft).
+     * <p/>
+     * When a file is opened with this flag, the offset field in the read
+     * and write functions is ignored.
+     * <p/>
+     * Servers MUST process multiple, parallel reads and writes correctly
+     * in this mode.  Naturally, it is permissible for them to do this by
+     * serializing the requests.
+     * <p/>
+     * Clients SHOULD use the SSH_FXF_ACCESS_APPEND_DATA flag to append
+     * data to a text file rather then using write with a calculated offset.
+     */
+    public static final int SSH_FXF_ACCESS_TEXT_MODE = 0x00000020;
+
+    /**
+     * The server MUST guarantee that no other handle has been opened
+     * with ACE4_READ_DATA access, and that no other handle will be
+     * opened with ACE4_READ_DATA access until the client closes the
+     * handle. (This MUST apply both to other clients and to other
+     * processes on the server.)
+     * <p/>
+     * If there is a conflicting lock the server MUST return
+     * SSH_FX_LOCK_CONFLICT.  If the server cannot make the locking
+     * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
+     * <p/>
+     * Other handles MAY be opened for ACE4_WRITE_DATA or any other
+     * combination of accesses, as long as ACE4_READ_DATA is not included
+     * in the mask.
+     */
+    public static final int SSH_FXF_ACCESS_BLOCK_READ = 0x00000040;
+
+    /**
+     * The server MUST guarantee that no other handle has been opened
+     * with ACE4_WRITE_DATA or ACE4_APPEND_DATA access, and that no other
+     * handle will be opened with ACE4_WRITE_DATA or ACE4_APPEND_DATA
+     * access until the client closes the handle. (This MUST apply both
+     * to other clients and to other processes on the server.)
+     * <p/>
+     * If there is a conflicting lock the server MUST return
+     * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
+     * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
+     * <p/>
+     * Other handles MAY be opened for ACE4_READ_DATA or any other
+     * combination of accesses, as long as neither ACE4_WRITE_DATA nor
+     * ACE4_APPEND_DATA are included in the mask.
+     */
+    public static final int SSH_FXF_ACCESS_BLOCK_WRITE = 0x00000080;
+
+    /**
+     * The server MUST guarantee that no other handle has been opened
+     * with ACE4_DELETE access, opened with the
+     * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that no other handle
+     * will be opened with ACE4_DELETE access or with the
+     * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that the file itself
+     * is not deleted in any other way until the client closes the handle.
+     * <p/>
+     * If there is a conflicting lock the server MUST return
+     * SSH_FX_LOCK_CONFLICT.  If the server cannot make the locking
+     * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
+     */
+    public static final int SSH_FXF_ACCESS_BLOCK_DELETE = 0x00000100;
+
+    /**
+     * If this bit is set, the above BLOCK modes are advisory. In advisory
+     * mode, only other accesses that specify a BLOCK mode need be
+     * considered when determining whether the BLOCK can be granted,
+     * and the server need not prevent I/O operations that violate the
+     * block mode.
+     * <p/>
+     * The server MAY perform mandatory locking even if the BLOCK_ADVISORY
+     * bit is set.
+     */
+    public static final int SSH_FXF_ACCESS_BLOCK_ADVISORY = 0x00000200;
+
+    /**
+     * If the final component of the path is a symlink, then the open
+     * MUST fail, and the error SSH_FX_LINK_LOOP MUST be returned.
+     */
+    public static final int SSH_FXF_ACCESS_NOFOLLOW = 0x00000400;
+
+    /**
+     * The file should be deleted when the last handle to it is closed.
+     * (The last handle may not be an sftp-handle.)  This MAY be emulated
+     * by a server if the OS doesn't support it by deleting the file when
+     * this handle is closed.
+     * <p/>
+     * It is implementation specific whether the directory entry is
+     * removed immediately or when the handle is closed.
+     */
+    public static final int SSH_FXF_ACCESS_DELETE_ON_CLOSE = 0x00000800;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/Packet.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * SFTP Paket Types
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class Packet {
+    public static final int SSH_FXP_INIT = 1;
+    public static final int SSH_FXP_VERSION = 2;
+    public static final int SSH_FXP_OPEN = 3;
+    public static final int SSH_FXP_CLOSE = 4;
+    public static final int SSH_FXP_READ = 5;
+    public static final int SSH_FXP_WRITE = 6;
+    public static final int SSH_FXP_LSTAT = 7;
+    public static final int SSH_FXP_FSTAT = 8;
+    public static final int SSH_FXP_SETSTAT = 9;
+    public static final int SSH_FXP_FSETSTAT = 10;
+    public static final int SSH_FXP_OPENDIR = 11;
+    public static final int SSH_FXP_READDIR = 12;
+    public static final int SSH_FXP_REMOVE = 13;
+    public static final int SSH_FXP_MKDIR = 14;
+    public static final int SSH_FXP_RMDIR = 15;
+    public static final int SSH_FXP_REALPATH = 16;
+    public static final int SSH_FXP_STAT = 17;
+    public static final int SSH_FXP_RENAME = 18;
+    public static final int SSH_FXP_READLINK = 19;
+    public static final int SSH_FXP_SYMLINK = 20;
+    public static final int SSH_FXP_LINK = 21;
+
+    public static final int SSH_FXP_STATUS = 101;
+    public static final int SSH_FXP_HANDLE = 102;
+    public static final int SSH_FXP_DATA = 103;
+    public static final int SSH_FXP_NAME = 104;
+    public static final int SSH_FXP_ATTRS = 105;
+
+    /**
+     * SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY packets can be used to
+     * implement extensions, which can be vendor specific.
+     */
+    public static final int SSH_FXP_EXTENDED = 200;
+    /**
+     * SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY packets can be used to
+     * implement extensions, which can be vendor specific.
+     */
+    public static final int SSH_FXP_EXTENDED_REPLY = 201;
+
+    public static String forName(int type) {
+        switch (type) {
+            case SSH_FXP_INIT:
+                return "SSH_FXP_INIT";
+
+            case SSH_FXP_VERSION:
+                return "SSH_FXP_VERSION";
+
+            case SSH_FXP_OPEN:
+                return "SSH_FXP_OPEN";
+
+            case SSH_FXP_CLOSE:
+                return "SSH_FXP_CLOSE";
+
+            case SSH_FXP_READ:
+                return "SSH_FXP_READ";
+
+            case SSH_FXP_WRITE:
+                return "SSH_FXP_WRITE";
+
+            case SSH_FXP_LSTAT:
+                return "SSH_FXP_LSTAT";
+
+            case SSH_FXP_FSTAT:
+                return "SSH_FXP_FSTAT";
+
+            case SSH_FXP_SETSTAT:
+                return "SSH_FXP_SETSTAT";
+
+            case SSH_FXP_FSETSTAT:
+                return "SSH_FXP_FSETSTAT";
+
+            case SSH_FXP_OPENDIR:
+                return "SSH_FXP_OPENDIR";
+
+            case SSH_FXP_READDIR:
+                return "SSH_FXP_READDIR";
+
+            case SSH_FXP_REMOVE:
+                return "SSH_FXP_REMOVE";
+
+            case SSH_FXP_MKDIR:
+                return "SSH_FXP_MKDIR";
+
+            case SSH_FXP_RMDIR:
+                return "SSH_FXP_RMDIR";
+
+            case SSH_FXP_REALPATH:
+                return "SSH_FXP_REALPATH";
+
+            case SSH_FXP_STAT:
+                return "SSH_FXP_STAT";
+
+            case SSH_FXP_RENAME:
+                return "SSH_FXP_RENAME";
+
+            case SSH_FXP_READLINK:
+                return "SSH_FXP_READLINK";
+
+            case SSH_FXP_SYMLINK:
+                return "SSH_FXP_SYMLINK";
+
+            case SSH_FXP_STATUS:
+                return "SSH_FXP_STATUS";
+
+            case SSH_FXP_HANDLE:
+                return "SSH_FXP_HANDLE";
+
+            case SSH_FXP_DATA:
+                return "SSH_FXP_DATA";
+
+            case SSH_FXP_NAME:
+                return "SSH_FXP_NAME";
+
+            case SSH_FXP_ATTRS:
+                return "SSH_FXP_ATTRS";
+
+            case SSH_FXP_EXTENDED:
+                return "SSH_FXP_EXTENDED";
+
+            case SSH_FXP_EXTENDED_REPLY:
+                return "SSH_FXP_EXTENDED_REPLY";
+        }
+
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/DSASHA1Verify.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,230 @@
+
+package ch.ethz.ssh2.signature;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+
+
+/**
+ * DSASHA1Verify.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: DSASHA1Verify.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class DSASHA1Verify {
+    private static final Logger log = Logger.getLogger(DSASHA1Verify.class);
+
+    public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException {
+        TypesReader tr = new TypesReader(key);
+        String key_format = tr.readString();
+
+        if (key_format.equals("ssh-dss") == false)
+            throw new IllegalArgumentException("This is not a ssh-dss public key!");
+
+        BigInteger p = tr.readMPINT();
+        BigInteger q = tr.readMPINT();
+        BigInteger g = tr.readMPINT();
+        BigInteger y = tr.readMPINT();
+
+        if (tr.remain() != 0)
+            throw new IOException("Padding in DSA public key!");
+
+        try {
+            KeyFactory kf = KeyFactory.getInstance("DSA");
+            KeySpec ks = new DSAPublicKeySpec(y, p, q, g);
+            return (DSAPublicKey) kf.generatePublic(ks);
+        }
+        catch (NoSuchAlgorithmException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (InvalidKeySpecException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+    public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk) throws IOException {
+        TypesWriter tw = new TypesWriter();
+        tw.writeString("ssh-dss");
+        DSAParams params = pk.getParams();
+        tw.writeMPInt(params.getP());
+        tw.writeMPInt(params.getQ());
+        tw.writeMPInt(params.getG());
+        tw.writeMPInt(pk.getY());
+        return tw.getBytes();
+    }
+
+    /**
+     * Convert from Java's signature ASN.1 encoding to the SSH spec.
+     * <p>
+     * Java ASN.1 encoding:
+     * <pre>
+     * SEQUENCE ::= {
+     *    r INTEGER,
+     *    s INTEGER
+     * }
+     * </pre>
+     */
+    public static byte[] encodeSSHDSASignature(byte[] ds) {
+        TypesWriter tw = new TypesWriter();
+        tw.writeString("ssh-dss");
+        int len, index;
+        index = 3;
+        len = ds[index++] & 0xff;
+        byte[] r = new byte[len];
+        System.arraycopy(ds, index, r, 0, r.length);
+        index = index + len + 1;
+        len = ds[index++] & 0xff;
+        byte[] s = new byte[len];
+        System.arraycopy(ds, index, s, 0, s.length);
+        byte[] a40 = new byte[40];
+        /* Patch (unsigned) r and s into the target array. */
+        int r_copylen = (r.length < 20) ? r.length : 20;
+        int s_copylen = (s.length < 20) ? s.length : 20;
+        System.arraycopy(r, r.length - r_copylen, a40, 20 - r_copylen, r_copylen);
+        System.arraycopy(s, s.length - s_copylen, a40, 40 - s_copylen, s_copylen);
+        tw.writeString(a40, 0, 40);
+        return tw.getBytes();
+    }
+
+    public static byte[] decodeSSHDSASignature(byte[] sig) throws IOException {
+        byte[] rsArray = null;
+
+        if (sig.length == 40) {
+            /* OK, another broken SSH server. */
+            rsArray = sig;
+        }
+        else {
+            /* Hopefully a server obeying the standard... */
+            TypesReader tr = new TypesReader(sig);
+            String sig_format = tr.readString();
+
+            if (sig_format.equals("ssh-dss") == false)
+                throw new IOException("Peer sent wrong signature format");
+
+            rsArray = tr.readByteString();
+
+            if (rsArray.length != 40)
+                throw new IOException("Peer sent corrupt signature");
+
+            if (tr.remain() != 0)
+                throw new IOException("Padding in DSA signature!");
+        }
+
+        int i = 0;
+        int j = 0;
+        byte[] tmp;
+
+        if (rsArray[0] == 0 && rsArray[1] == 0 && rsArray[2] == 0) {
+            j = ((rsArray[i++] << 24) & 0xff000000) | ((rsArray[i++] << 16) & 0x00ff0000)
+                | ((rsArray[i++] << 8) & 0x0000ff00) | ((rsArray[i++]) & 0x000000ff);
+            i += j;
+            j = ((rsArray[i++] << 24) & 0xff000000) | ((rsArray[i++] << 16) & 0x00ff0000)
+                | ((rsArray[i++] << 8) & 0x0000ff00) | ((rsArray[i++]) & 0x000000ff);
+            tmp = new byte[j];
+            System.arraycopy(rsArray, i, tmp, 0, j);
+            rsArray = tmp;
+        }
+
+        /* ASN.1 */
+        int frst = ((rsArray[0] & 0x80) != 0 ? 1 : 0);
+        int scnd = ((rsArray[20] & 0x80) != 0 ? 1 : 0);
+        /* Calculate output length */
+        int length = rsArray.length + 6 + frst + scnd;
+        tmp = new byte[length];
+        /* DER-encoding to match Java */
+        tmp[0] = (byte) 0x30;
+
+        if (rsArray.length != 40)
+            throw new IOException("Peer sent corrupt signature");
+
+        /* Calculate length */
+        tmp[1] = (byte) 0x2c;
+        tmp[1] += frst;
+        tmp[1] += scnd;
+        /* First item */
+        tmp[2] = (byte) 0x02;
+        /* First item length */
+        tmp[3] = (byte) 0x14;
+        tmp[3] += frst;
+        /* Copy in the data for first item */
+        System.arraycopy(rsArray, 0, tmp, 4 + frst, 20);
+        /* Second item */
+        tmp[4 + tmp[3]] = (byte) 0x02;
+        /* Second item length */
+        tmp[5 + tmp[3]] = (byte) 0x14;
+        tmp[5 + tmp[3]] += scnd;
+        /* Copy in the data for the second item */
+        System.arraycopy(rsArray, 20, tmp, 6 + tmp[3] + scnd, 20);
+        /* Swap buffers */
+        rsArray = tmp;
+        return rsArray;
+    }
+
+    public static boolean verifySignature(byte[] message, byte[] ds, DSAPublicKey dpk) throws IOException {
+        try {
+            Signature s = Signature.getInstance("SHA1withDSA");
+            s.initVerify(dpk);
+            s.update(message);
+            return s.verify(ds);
+        }
+        catch (NoSuchAlgorithmException e) {
+            IOException ex = new IOException("No such algorithm");
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (InvalidKeyException e) {
+            IOException ex = new IOException("No such algorithm");
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (SignatureException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+    public static byte[] generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd) throws IOException {
+        try {
+            Signature s = Signature.getInstance("SHA1withDSA");
+            s.initSign(pk);
+            s.update(message);
+            return s.sign();
+        }
+        catch (NoSuchAlgorithmException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (InvalidKeyException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (SignatureException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/ECDSASHA2Verify.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,488 @@
+/**
+ *
+ */
+package ch.ethz.ssh2.signature;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.EllipticCurve;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Map;
+import java.util.TreeMap;
+
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+
+/**
+ * @author Kenny Root
+ *
+ */
+public class ECDSASHA2Verify {
+    private static final Logger log = Logger.getLogger(ECDSASHA2Verify.class);
+
+    public static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-";
+
+    private static final String NISTP256 = "nistp256";
+    private static final String NISTP256_OID = "1.2.840.10045.3.1.7";
+    private static final String NISTP384 = "nistp384";
+    private static final String NISTP384_OID = "1.3.132.0.34";
+    private static final String NISTP521 = "nistp521";
+    private static final String NISTP521_OID = "1.3.132.0.35";
+
+    private static final Map<String, ECParameterSpec> CURVES = new TreeMap<String, ECParameterSpec>();
+    static {
+        CURVES.put(NISTP256, EllipticCurves.nistp256);
+        CURVES.put(NISTP384, EllipticCurves.nistp384);
+        CURVES.put(NISTP521, EllipticCurves.nistp521);
+    }
+
+    private static final Map<Integer, String> CURVE_SIZES = new TreeMap<Integer, String>();
+    static {
+        CURVE_SIZES.put(256, NISTP256);
+        CURVE_SIZES.put(384, NISTP384);
+        CURVE_SIZES.put(521, NISTP521);
+    }
+
+    private static final Map<String, String> CURVE_OIDS = new TreeMap<String, String>();
+    static {
+        CURVE_OIDS.put(NISTP256_OID, NISTP256);
+        CURVE_OIDS.put(NISTP384_OID, NISTP256);
+        CURVE_OIDS.put(NISTP521_OID, NISTP256);
+    }
+
+    public static int[] getCurveSizes() {
+        int[] keys = new int[CURVE_SIZES.size()];
+        int i = 0;
+
+        for (Integer n : CURVE_SIZES.keySet().toArray(new Integer[keys.length])) {
+            keys[i++] = n;
+        }
+
+        return keys;
+    }
+
+    public static ECParameterSpec getCurveForSize(int size) {
+        final String name = CURVE_SIZES.get(size);
+
+        if (name == null) {
+            return null;
+        }
+
+        return CURVES.get(name);
+    }
+
+    public static ECPublicKey decodeSSHECDSAPublicKey(byte[] key) throws IOException {
+        TypesReader tr = new TypesReader(key);
+        String key_format = tr.readString();
+
+        if (key_format.startsWith(ECDSA_SHA2_PREFIX) == false)
+            throw new IllegalArgumentException("This is not an ECDSA public key");
+
+        String curveName = tr.readString();
+        byte[] groupBytes = tr.readByteString();
+
+        if (tr.remain() != 0)
+            throw new IOException("Padding in ECDSA public key!");
+
+        if (key_format.equals(ECDSA_SHA2_PREFIX + curveName) == false) {
+            throw new IOException("Key format is inconsistent with curve name: " + key_format
+                                  + " != " + curveName);
+        }
+
+        ECParameterSpec params = CURVES.get(curveName);
+
+        if (params == null) {
+            throw new IOException("Curve is not supported: " + curveName);
+        }
+
+        ECPoint group = ECDSASHA2Verify.decodeECPoint(groupBytes, params.getCurve());
+
+        if (group == null) {
+            throw new IOException("Invalid ECDSA group");
+        }
+
+        KeySpec keySpec = new ECPublicKeySpec(group, params);
+
+        try {
+            KeyFactory kf = KeyFactory.getInstance("EC");
+            return (ECPublicKey) kf.generatePublic(keySpec);
+        }
+        catch (NoSuchAlgorithmException nsae) {
+            IOException ioe = new IOException("No EC KeyFactory available");
+            ioe.initCause(nsae);
+            throw ioe;
+        }
+        catch (InvalidKeySpecException ikse) {
+            IOException ioe = new IOException("No EC KeyFactory available");
+            ioe.initCause(ikse);
+            throw ioe;
+        }
+    }
+
+    public static byte[] encodeSSHECDSAPublicKey(ECPublicKey key) throws IOException {
+        TypesWriter tw = new TypesWriter();
+        String curveName = getCurveName(key.getParams());
+        String keyFormat = ECDSA_SHA2_PREFIX + curveName;
+        tw.writeString(keyFormat);
+        tw.writeString(curveName);
+        byte[] encoded = encodeECPoint(key.getW(), key.getParams().getCurve());
+        tw.writeString(encoded, 0, encoded.length);
+        return tw.getBytes();
+    }
+
+    public static String getCurveName(ECParameterSpec params) throws IOException {
+        int fieldSize = getCurveSize(params);
+        final String curveName = getCurveName(fieldSize);
+
+        if (curveName == null) {
+            throw new IOException("invalid curve size " + fieldSize);
+        }
+
+        return curveName;
+    }
+
+    public static String getCurveName(int fieldSize) {
+        String curveName = CURVE_SIZES.get(fieldSize);
+
+        if (curveName == null) {
+            return null;
+        }
+
+        return curveName;
+    }
+
+    public static int getCurveSize(ECParameterSpec params) {
+        return params.getCurve().getField().getFieldSize();
+    }
+
+    public static ECParameterSpec getCurveForOID(String oid) {
+        String name = CURVE_OIDS.get(oid);
+
+        if (name == null)
+            return null;
+
+        return CURVES.get(name);
+    }
+
+    public static byte[] decodeSSHECDSASignature(byte[] sig) throws IOException {
+        byte[] rsArray = null;
+        TypesReader tr = new TypesReader(sig);
+        String sig_format = tr.readString();
+
+        if (sig_format.startsWith(ECDSA_SHA2_PREFIX) == false)
+            throw new IOException("Peer sent wrong signature format");
+
+        String curveName = sig_format.substring(ECDSA_SHA2_PREFIX.length());
+
+        if (CURVES.containsKey(curveName) == false) {
+            throw new IOException("Unsupported curve: " + curveName);
+        }
+
+        rsArray = tr.readByteString();
+
+        if (tr.remain() != 0)
+            throw new IOException("Padding in ECDSA signature!");
+
+        byte[] rArray;
+        byte[] sArray;
+        {
+            TypesReader rsReader = new TypesReader(rsArray);
+            rArray = rsReader.readMPINT().toByteArray();
+            sArray = rsReader.readMPINT().toByteArray();
+        }
+        int first = rArray.length;
+        int second = sArray.length;
+
+        /* We can't have the high bit set, so add an extra zero at the beginning if so. */
+        if ((rArray[0] & 0x80) != 0) {
+            first++;
+        }
+
+        if ((sArray[0] & 0x80) != 0) {
+            second++;
+        }
+
+        /* Calculate total output length */
+        ByteArrayOutputStream os = new ByteArrayOutputStream(6 + first + second);
+        /* ASN.1 SEQUENCE tag */
+        os.write(0x30);
+        /* Size of SEQUENCE */
+        writeLength(4 + first + second, os);
+        /* ASN.1 INTEGER tag */
+        os.write(0x02);
+        /* "r" INTEGER length */
+        writeLength(first, os);
+
+        /* Copy in the "r" INTEGER */
+        if (first != rArray.length) {
+            os.write(0x00);
+        }
+
+        os.write(rArray);
+        /* ASN.1 INTEGER tag */
+        os.write(0x02);
+        /* "s" INTEGER length */
+        writeLength(second, os);
+
+        /* Copy in the "s" INTEGER */
+        if (second != sArray.length) {
+            os.write(0x00);
+        }
+
+        os.write(sArray);
+        return os.toByteArray();
+    }
+
+    private static final void writeLength(int length, OutputStream os) throws IOException {
+        if (length <= 0x7F) {
+            os.write(length);
+            return;
+        }
+
+        int numOctets = 0;
+        int lenCopy = length;
+
+        while (lenCopy != 0) {
+            lenCopy >>>= 8;
+            numOctets++;
+        }
+
+        os.write(0x80 | numOctets);
+
+        for (int i = (numOctets - 1) * 8; i >= 0; i -= 8) {
+            os.write((byte)(length >> i));
+        }
+    }
+
+    public static byte[] encodeSSHECDSASignature(byte[] sig, ECParameterSpec params) throws IOException {
+        TypesWriter tw = new TypesWriter();
+        String curveName = getCurveName(params);
+        tw.writeString(ECDSA_SHA2_PREFIX + curveName);
+
+        if ((sig[0] != 0x30) || (sig[1] != sig.length - 2) || (sig[2] != 0x02)) {
+            throw new IOException("Invalid signature format");
+        }
+
+        int rLength = sig[3];
+
+        if ((rLength + 6 > sig.length) || (sig[4 + rLength] != 0x02)) {
+            throw new IOException("Invalid signature format");
+        }
+
+        int sLength = sig[5 + rLength];
+
+        if (6 + rLength + sLength > sig.length) {
+            throw new IOException("Invalid signature format");
+        }
+
+        byte[] rArray = new byte[rLength];
+        byte[] sArray = new byte[sLength];
+        System.arraycopy(sig, 4, rArray, 0, rLength);
+        System.arraycopy(sig, 6 + rLength, sArray, 0, sLength);
+        BigInteger r = new BigInteger(rArray);
+        BigInteger s = new BigInteger(sArray);
+        // Write the <r,s> to its own types writer.
+        TypesWriter rsWriter = new TypesWriter();
+        rsWriter.writeMPInt(r);
+        rsWriter.writeMPInt(s);
+        byte[] encoded = rsWriter.getBytes();
+        tw.writeString(encoded, 0, encoded.length);
+        return tw.getBytes();
+    }
+
+    public static byte[] generateSignature(byte[] message, ECPrivateKey pk) throws IOException {
+        final String algo = getSignatureAlgorithmForParams(pk.getParams());
+
+        try {
+            Signature s = Signature.getInstance(algo);
+            s.initSign(pk);
+            s.update(message);
+            return s.sign();
+        }
+        catch (NoSuchAlgorithmException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (InvalidKeyException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (SignatureException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+    public static boolean verifySignature(byte[] message, byte[] ds, ECPublicKey dpk) throws IOException {
+        final String algo = getSignatureAlgorithmForParams(dpk.getParams());
+
+        try {
+            Signature s = Signature.getInstance(algo);
+            s.initVerify(dpk);
+            s.update(message);
+            return s.verify(ds);
+        }
+        catch (NoSuchAlgorithmException e) {
+            IOException ex = new IOException("No such algorithm");
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (InvalidKeyException e) {
+            IOException ex = new IOException("No such algorithm");
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (SignatureException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+    private static String getSignatureAlgorithmForParams(ECParameterSpec params) {
+        int size = getCurveSize(params);
+
+        if (size <= 256) {
+            return "SHA256withECDSA";
+        }
+        else if (size <= 384) {
+            return "SHA384withECDSA";
+        }
+        else {
+            return "SHA512withECDSA";
+        }
+    }
+
+    public static String getDigestAlgorithmForParams(ECParameterSpec params) {
+        int size = getCurveSize(params);
+
+        if (size <= 256) {
+            return "SHA256";
+        }
+        else if (size <= 384) {
+            return "SHA384";
+        }
+        else {
+            return "SHA512";
+        }
+    }
+
+    /**
+     * Decode an OctetString to EllipticCurvePoint according to SECG 2.3.4
+     */
+    public static ECPoint decodeECPoint(byte[] M, EllipticCurve curve) {
+        if (M.length == 0) {
+            return null;
+        }
+
+        // M has len 2 ceil(log_2(q)/8) + 1 ?
+        int elementSize = (curve.getField().getFieldSize() + 7) / 8;
+
+        if (M.length != 2 * elementSize + 1) {
+            return null;
+        }
+
+        // step 3.2
+        if (M[0] != 0x04) {
+            return null;
+        }
+
+        // Step 3.3
+        byte[] xp = new byte[elementSize];
+        System.arraycopy(M, 1, xp, 0, elementSize);
+        // Step 3.4
+        byte[] yp = new byte[elementSize];
+        System.arraycopy(M, 1 + elementSize, yp, 0, elementSize);
+        ECPoint P = new ECPoint(new BigInteger(1, xp), new BigInteger(1, yp));
+        // TODO check point 3.5
+        // Step 3.6
+        return P;
+    }
+
+    /**
+     * Encode EllipticCurvePoint to an OctetString
+     */
+    public static byte[] encodeECPoint(ECPoint group, EllipticCurve curve) {
+        // M has len 2 ceil(log_2(q)/8) + 1 ?
+        int elementSize = (curve.getField().getFieldSize() + 7) / 8;
+        byte[] M = new byte[2 * elementSize + 1];
+        // Uncompressed format
+        M[0] = 0x04;
+        {
+            byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray());
+            System.arraycopy(affineX, 0, M, 1 + elementSize - affineX.length, affineX.length);
+        }
+        {
+            byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray());
+            System.arraycopy(affineY, 0, M, 1 + elementSize + elementSize - affineY.length,
+                             affineY.length);
+        }
+        return M;
+    }
+
+    private static byte[] removeLeadingZeroes(byte[] input) {
+        if (input[0] != 0x00) {
+            return input;
+        }
+
+        int pos = 1;
+
+        while (pos < input.length - 1 && input[pos] == 0x00) {
+            pos++;
+        }
+
+        byte[] output = new byte[input.length - pos];
+        System.arraycopy(input, pos, output, 0, output.length);
+        return output;
+    }
+
+    public static class EllipticCurves {
+        public static ECParameterSpec nistp256 = new ECParameterSpec(
+            new EllipticCurve(
+                new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
+                new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16),
+                new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)),
+            new ECPoint(new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
+                        new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
+            new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
+            1);
+
+        public static ECParameterSpec nistp384 = new ECParameterSpec(
+            new EllipticCurve(
+                new ECFieldFp(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16)),
+                new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16),
+                new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16)),
+            new ECPoint(new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16),
+                        new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16)),
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
+            1);
+
+        public static ECParameterSpec nistp521 = new ECParameterSpec(
+            new EllipticCurve(
+                new ECFieldFp(new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)),
+                new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),
+                new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16)),
+            new ECPoint(new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", 16),
+                        new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", 16)),
+            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16),
+            1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/RSASHA1Verify.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,174 @@
+
+package ch.ethz.ssh2.signature;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.RSAPublicKeySpec;
+
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+
+
+/**
+ * RSASHA1Verify.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: RSASHA1Verify.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class RSASHA1Verify {
+    private static final Logger log = Logger.getLogger(RSASHA1Verify.class);
+
+    public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException {
+        TypesReader tr = new TypesReader(key);
+        String key_format = tr.readString();
+
+        if (key_format.equals("ssh-rsa") == false)
+            throw new IllegalArgumentException("This is not a ssh-rsa public key");
+
+        BigInteger e = tr.readMPINT();
+        BigInteger n = tr.readMPINT();
+
+        if (tr.remain() != 0)
+            throw new IOException("Padding in RSA public key!");
+
+        KeySpec keySpec = new RSAPublicKeySpec(n, e);
+
+        try {
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            return (RSAPublicKey) kf.generatePublic(keySpec);
+        }
+        catch (NoSuchAlgorithmException nsae) {
+            IOException ioe = new IOException("No RSA KeyFactory available");
+            ioe.initCause(nsae);
+            throw ioe;
+        }
+        catch (InvalidKeySpecException ikse) {
+            IOException ioe = new IOException("No RSA KeyFactory available");
+            ioe.initCause(ikse);
+            throw ioe;
+        }
+    }
+
+    public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException {
+        TypesWriter tw = new TypesWriter();
+        tw.writeString("ssh-rsa");
+        tw.writeMPInt(pk.getPublicExponent());
+        tw.writeMPInt(pk.getModulus());
+        return tw.getBytes();
+    }
+
+    public static byte[] decodeSSHRSASignature(byte[] sig) throws IOException {
+        TypesReader tr = new TypesReader(sig);
+        String sig_format = tr.readString();
+
+        if (sig_format.equals("ssh-rsa") == false)
+            throw new IOException("Peer sent wrong signature format");
+
+        /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
+         * containing s (which is an integer, without lengths or padding, unsigned and in
+         * network byte order)." See also below.
+         */
+        byte[] s = tr.readByteString();
+
+        if (s.length == 0)
+            throw new IOException("Error in RSA signature, S is empty.");
+
+        if (log.isEnabled()) {
+            log.info("Decoding ssh-rsa signature string (length: " + s.length + ")");
+        }
+
+        if (tr.remain() != 0)
+            throw new IOException("Padding in RSA signature!");
+
+        if (s[0] == 0 && s[1] == 0 && s[2] == 0) {
+            int i = 0;
+            int j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000)
+                    | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff);
+            i += j;
+            j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000)
+                | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff);
+            byte[] tmp = new byte[j];
+            System.arraycopy(s, i, tmp, 0, j);
+            sig = tmp;
+        }
+
+        return s;
+    }
+
+    public static byte[] encodeSSHRSASignature(byte[] s) throws IOException {
+        TypesWriter tw = new TypesWriter();
+        tw.writeString("ssh-rsa");
+
+        /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
+         * containing s (which is an integer, without lengths or padding, unsigned and in
+         * network byte order)."
+         */
+
+        /* Remove first zero sign byte, if present */
+
+        if ((s.length > 1) && (s[0] == 0x00))
+            tw.writeString(s, 1, s.length - 1);
+        else
+            tw.writeString(s, 0, s.length);
+
+        return tw.getBytes();
+    }
+
+    public static byte[] generateSignature(byte[] message, RSAPrivateKey pk) throws IOException {
+        try {
+            Signature s = Signature.getInstance("SHA1withRSA");
+            s.initSign(pk);
+            s.update(message);
+            return s.sign();
+        }
+        catch (NoSuchAlgorithmException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (InvalidKeyException e) {
+            IOException ex =  new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (SignatureException e) {
+            IOException ex =  new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+    public static boolean verifySignature(byte[] message, byte[] ds, RSAPublicKey dpk) throws IOException {
+        try {
+            Signature s = Signature.getInstance("SHA1withRSA");
+            s.initVerify(dpk);
+            s.update(message);
+            return s.verify(ds);
+        }
+        catch (NoSuchAlgorithmException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (InvalidKeyException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+        catch (SignatureException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/ClientKexManager.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.security.DigestException;
+import java.security.SecureRandom;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+
+import ch.ethz.ssh2.ConnectionInfo;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.ServerHostKeyVerifier;
+import ch.ethz.ssh2.compression.CompressionFactory;
+import ch.ethz.ssh2.compression.Compressor;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
+import ch.ethz.ssh2.crypto.dh.GenericDhExchange;
+import ch.ethz.ssh2.crypto.dh.DhGroupExchange;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.packets.PacketKexDHInit;
+import ch.ethz.ssh2.packets.PacketKexDHReply;
+import ch.ethz.ssh2.packets.PacketKexDhGexGroup;
+import ch.ethz.ssh2.packets.PacketKexDhGexInit;
+import ch.ethz.ssh2.packets.PacketKexDhGexReply;
+import ch.ethz.ssh2.packets.PacketKexDhGexRequest;
+import ch.ethz.ssh2.packets.PacketKexDhGexRequestOld;
+import ch.ethz.ssh2.packets.PacketKexInit;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
+
+/**
+ * @version $Id: ClientKexManager.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public class ClientKexManager extends KexManager {
+
+    private final ServerHostKeyVerifier verifier;
+
+    private final String hostname;
+
+    private final int port;
+
+    public ClientKexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
+                            ServerHostKeyVerifier keyVerifier, SecureRandom rnd) {
+        super(tm, csh, initialCwl, rnd);
+        this.hostname = hostname;
+        this.port = port;
+        this.verifier = keyVerifier;
+    }
+
+    protected boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException {
+        if (kxs.np.server_host_key_algo.startsWith("ecdsa-sha2-")) {
+            byte[] rs = ECDSASHA2Verify.decodeSSHECDSASignature(sig);
+            ECPublicKey epk = ECDSASHA2Verify.decodeSSHECDSAPublicKey(hostkey);
+            log.debug("Verifying ecdsa signature");
+            return ECDSASHA2Verify.verifySignature(kxs.H, rs, epk);
+        }
+
+        if (kxs.np.server_host_key_algo.equals("ssh-rsa")) {
+            byte[] rs = RSASHA1Verify.decodeSSHRSASignature(sig);
+            RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);
+            log.debug("Verifying ssh-rsa signature");
+            return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
+        }
+
+        if (kxs.np.server_host_key_algo.equals("ssh-dss")) {
+            byte[] ds = DSASHA1Verify.decodeSSHDSASignature(sig);
+            DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);
+            log.debug("Verifying ssh-dss signature");
+            return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);
+        }
+
+        throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
+    }
+
+    public void handleFailure(final IOException failure) {
+        synchronized (accessLock) {
+            connectionClosed = true;
+            accessLock.notifyAll();
+        }
+    }
+
+    public synchronized void handleMessage(byte[] msg) throws IOException {
+        PacketKexInit kip;
+        
+        if (msg == null) {
+            synchronized (accessLock) {
+                connectionClosed = true;
+                accessLock.notifyAll();
+                return;
+            }
+        }
+        
+        log.debug(String.format("client kex manager, packet type %d", msg[0]));
+
+        if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT)) {
+            throw new PacketTypeException(msg[0]);
+        }
+
+        if (ignore_next_kex_packet) {
+            ignore_next_kex_packet = false;
+            return;
+        }
+
+        if (msg[0] == Packets.SSH_MSG_KEXINIT) {
+            if ((kxs != null) && (kxs.state != 0)) {
+                throw new PacketTypeException(msg[0]);
+            }
+
+            if (kxs == null) {
+                /*
+                 * Ah, OK, peer wants to do KEX. Let's be nice and play
+                 * together.
+                 */
+                kxs = new KexState();
+                kxs.dhgexParameters = nextKEXdhgexParameters;
+                kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
+                kxs.localKEX = kip;
+                tm.sendKexMessage(kip.getPayload());
+            }
+
+            kip = new PacketKexInit(msg);
+            kxs.remoteKEX = kip;
+            kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());
+
+            if (kxs.np == null)
+                throw new IOException("Cannot negotiate, proposals do not match.");
+
+            if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false)) {
+                // Guess was wrong, we need to ignore the next kex packet.
+                ignore_next_kex_packet = true;
+            }
+
+            if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1") ||
+                    kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256")) {
+                if (kxs.dhgexParameters.getMin_group_len() == 0) {
+                    PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
+                    tm.sendKexMessage(dhgexreq.getPayload());
+                }
+                else {
+                    PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
+                    tm.sendKexMessage(dhgexreq.getPayload());
+                }
+
+                if (kxs.np.kex_algo.endsWith("sha1")) {
+                    kxs.hashAlgo = "SHA1";
+                }
+                else {
+                    kxs.hashAlgo = "SHA-256";
+                }
+
+                kxs.state = 1;
+                return;
+            }
+
+            if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")  ||
+                    kxs.np.kex_algo.equals("diffie-hellman-group14-sha1") ||
+                    kxs.np.kex_algo.equals("ecdh-sha2-nistp256")          ||
+                    kxs.np.kex_algo.equals("ecdh-sha2-nistp384")          ||
+                    kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) {
+                kxs.dhx = GenericDhExchange.getInstance(kxs.np.kex_algo);
+                kxs.dhx.init(kxs.np.kex_algo);
+                kxs.hashAlgo = kxs.dhx.getHashAlgo();
+                PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
+                tm.sendKexMessage(kp.getPayload());
+                kxs.state = 1;
+                return;
+            }
+
+            throw new IllegalStateException("Unkown KEX method!");
+        }
+
+        if (msg[0] == Packets.SSH_MSG_NEWKEYS) {
+            if (km == null) {
+                throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
+            }
+
+            BlockCipher cbc;
+            MAC mac;
+            Compressor comp;
+
+            try {
+                cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
+                                                      km.enc_key_server_to_client, km.initial_iv_server_to_client);
+
+                try {
+                    mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);
+                }
+                catch (DigestException e) {
+                    throw new IOException(e);
+                }
+
+                comp = CompressionFactory.createCompressor(kxs.np.comp_algo_server_to_client);
+            }
+            catch (IllegalArgumentException e) {
+                throw new IOException(e.getMessage());
+            }
+
+            tm.changeRecvCipher(cbc, mac);
+            tm.changeRecvCompression(comp);
+            ConnectionInfo sci = new ConnectionInfo();
+            kexCount++;
+            sci.keyExchangeAlgorithm = kxs.np.kex_algo;
+            sci.keyExchangeCounter = kexCount;
+            sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
+            sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
+            sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
+            sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
+            sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
+            sci.serverHostKey = kxs.remote_hostkey;
+
+            synchronized (accessLock) {
+                lastConnInfo = sci;
+                accessLock.notifyAll();
+            }
+
+            kxs = null;
+            return;
+        }
+
+        if ((kxs == null) || (kxs.state == 0)) {
+            throw new IOException("Unexpected Kex submessage!");
+        }
+
+        if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1") ||
+                kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256")) {
+            if (kxs.state == 1) {
+                PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg);
+                kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
+                kxs.dhgx.init(rnd);
+                PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
+                tm.sendKexMessage(dhgexinit.getPayload());
+                kxs.state = 2;
+                return;
+            }
+
+            if (kxs.state == 2) {
+                PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg);
+                kxs.remote_hostkey = dhgexrpl.getHostKey();
+
+                if (verifier != null) {
+                    try {
+                        if (!verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.remote_hostkey)) {
+                            throw new IOException("The server host key was not accepted by the verifier callback");
+                        }
+                    }
+                    catch (Exception e) {
+                        throw new IOException(
+                            "The server host key was not accepted by the verifier callback.", e);
+                    }
+                }
+
+                kxs.dhgx.setF(dhgexrpl.getF());
+
+                try {
+                    kxs.H = kxs.dhgx.calculateH(kxs.hashAlgo, csh.getClientString(), csh.getServerString(),
+                                                kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
+                                                kxs.dhgexParameters);
+                }
+                catch (IllegalArgumentException e) {
+                    throw new IOException("KEX error.", e);
+                }
+
+                if (!verifySignature(dhgexrpl.getSignature(), kxs.remote_hostkey)) {
+                    throw new IOException("Invalid remote host key signature");
+                }
+
+                kxs.K = kxs.dhgx.getK();
+                finishKex(true);
+                kxs.state = -1;
+                return;
+            }
+
+            throw new IllegalStateException("Illegal State in KEX Exchange!");
+        }
+
+        if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")  ||
+                kxs.np.kex_algo.equals("diffie-hellman-group14-sha1") ||
+                kxs.np.kex_algo.equals("ecdh-sha2-nistp256")          ||
+                kxs.np.kex_algo.equals("ecdh-sha2-nistp384")          ||
+                kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) {
+            if (kxs.state == 1) {
+                PacketKexDHReply dhr = new PacketKexDHReply(msg);
+                kxs.remote_hostkey = dhr.getHostKey();
+
+                if (verifier != null) {
+                    try {
+                        if (!verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.remote_hostkey)) {
+                            throw new IOException("The server host key was not accepted by the verifier callback");
+                        }
+                    }
+                    catch (Exception e) {
+                        throw new IOException("The server host key was not accepted by the verifier callback", e);
+                    }
+                }
+
+                kxs.dhx.setF(dhr.getF().toByteArray());
+
+                try {
+                    kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
+                                               kxs.remoteKEX.getPayload(), dhr.getHostKey());
+                }
+                catch (IllegalArgumentException e) {
+                    throw new IOException("KEX error.", e);
+                }
+
+                if (!verifySignature(dhr.getSignature(), kxs.remote_hostkey)) {
+                    throw new IOException("Invalid remote host key signature");
+                }
+
+                kxs.K = kxs.dhx.getK();
+                finishKex(true);
+                kxs.state = -1;
+                return;
+            }
+        }
+
+        throw new IllegalStateException(String.format("Unknown KEX method %s", kxs.np.kex_algo));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/ClientServerHello.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStream;
+
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: ClientServerHello.java 155 2014-04-28 12:01:19Z dkocher@sudo.ch $
+ */
+public class ClientServerHello {
+    private final String client_line;
+    private final String server_line;
+
+    public final static int readLineRN(InputStream is, byte[] buffer) throws IOException {
+        int pos = 0;
+        boolean need10 = false;
+        int len = 0;
+
+        while (true) {
+            int c = is.read();
+
+            if (c == -1)
+                throw new IOException("Premature connection close");
+
+            buffer[pos++] = (byte) c;
+
+            if (c == 13) {
+                need10 = true;
+                continue;
+            }
+
+            if (c == 10)
+                break;
+
+            if (need10 == true)
+                throw new IOException("Malformed line sent by the server, the line does not end correctly.");
+
+            len++;
+
+            if (pos >= buffer.length)
+                throw new IOException("The server sent a too long line.");
+        }
+
+        return len;
+    }
+
+    private ClientServerHello(String client_line, String server_line) {
+        this.client_line = client_line;
+        this.server_line = server_line;
+    }
+
+    public static ClientServerHello clientHello(String softwareversion, InputStream bi, OutputStream bo)
+    throws IOException {
+        return exchange(softwareversion, bi, bo, true);
+    }
+
+    public static ClientServerHello serverHello(String softwareversion, InputStream bi, OutputStream bo)
+    throws IOException {
+        return exchange(softwareversion, bi, bo, false);
+    }
+
+    private static ClientServerHello exchange(String softwareversion, InputStream bi, OutputStream bo, boolean clientMode)
+    throws IOException {
+        String localIdentifier = String.format("SSH-2.0-%s", softwareversion);
+        bo.write(StringEncoder.GetBytes(String.format("%s\r\n", localIdentifier)));
+        bo.flush();
+        // Expect SSH-protoversion-softwareversion SP comments CR LF
+        byte[] serverVersion = new byte[512];
+        String remoteIdentifier = null;
+        for (int i = 0; i < 50; i++) {
+            int len = readLineRN(bi, serverVersion);
+            remoteIdentifier = new String(serverVersion, 0, len, "ISO-8859-1");
+            if (remoteIdentifier.startsWith("SSH-")) break;
+        }
+
+        if (remoteIdentifier.equals("")) {
+            throw new IOException("Premature connection close");
+        }
+
+        if (!remoteIdentifier.startsWith("SSH-")) {
+            throw new IOException(String.format("Malformed SSH identification %s", remoteIdentifier));
+        }
+
+        if (!remoteIdentifier.startsWith("SSH-1.99-")
+                && !remoteIdentifier.startsWith("SSH-2.0-")) {
+            throw new IOException(String.format("Incompatible remote protocol version %s", remoteIdentifier));
+        }
+
+        if (clientMode) {
+            return new ClientServerHello(localIdentifier, remoteIdentifier);
+        }
+        else {
+            return new ClientServerHello(remoteIdentifier, localIdentifier);
+        }
+    }
+
+    /**
+     * @return Returns the client_versioncomment.
+     */
+    public byte[] getClientString() {
+        return StringEncoder.GetBytes(client_line);
+    }
+
+    /**
+     * @return Returns the server_versioncomment.
+     */
+    public byte[] getServerString() {
+        return StringEncoder.GetBytes(server_line);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/ClientTransportManager.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,54 @@
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.DHGexParameters;
+import ch.ethz.ssh2.ServerHostKeyVerifier;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+
+/**
+ * @version $Id: ClientTransportManager.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class ClientTransportManager extends TransportManager {
+
+    private final Socket sock;
+
+    public ClientTransportManager(final Socket socket) {
+        super(socket);
+        this.sock = socket;
+    }
+
+    public void setTcpNoDelay(boolean state) throws IOException {
+        sock.setTcpNoDelay(state);
+    }
+
+    public void setSoTimeout(int timeout) throws IOException {
+        sock.setSoTimeout(timeout);
+    }
+
+    public void connect(String hostname, int port, String softwareversion, CryptoWishList cwl,
+                        ServerHostKeyVerifier verifier, DHGexParameters dhgex, int connectTimeout, SecureRandom rnd)
+    throws IOException {
+        // Establish the TCP connection to the SSH-2 server
+        this.connect(hostname, port, connectTimeout);
+        // Parse the server line and say hello - important: this information is later needed for the
+        // key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
+        // for later use.
+        ClientServerHello csh = ClientServerHello.clientHello(softwareversion, sock.getInputStream(),
+                                sock.getOutputStream());
+        TransportConnection tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
+        KexManager km = new ClientKexManager(this, csh, cwl, hostname, port, verifier, rnd);
+        super.init(tc, km);
+        km.initiateKEX(cwl, dhgex, null, null, null);
+        this.startReceiver();
+    }
+
+    protected void connect(String hostname, int port, int connectTimeout) throws IOException {
+        log.debug(String.format("client transport manager connecting to %s:%d", hostname, port));
+        sock.connect(new InetSocketAddress(hostname, port), connectTimeout);
+        log.debug(String.format("client transport manager connected to %s:%d", hostname, port));
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/DisconnectException.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,22 @@
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.packets.PacketDisconnect;
+
+/**
+ * @version $Id: DisconnectException.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public class DisconnectException extends IOException {
+
+    private PacketDisconnect.Reason reason;
+
+    public DisconnectException(final PacketDisconnect.Reason reason, final String message) {
+        super(message);
+        this.reason = reason;
+    }
+
+    public PacketDisconnect.Reason getReason() {
+        return reason;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/HTTPProxyClientTransportManager.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,103 @@
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import ch.ethz.ssh2.HTTPProxyData;
+import ch.ethz.ssh2.HTTPProxyException;
+import ch.ethz.ssh2.crypto.Base64;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @version $Id: HTTPProxyClientTransportManager.java 155 2014-04-28 12:01:19Z dkocher@sudo.ch $
+ */
+public class HTTPProxyClientTransportManager extends ClientTransportManager {
+
+    /**
+     * Used to tell the library that the connection shall be established through a proxy server.
+     */
+
+    private HTTPProxyData pd;
+
+    private final Socket sock;
+
+    public HTTPProxyClientTransportManager(final Socket socket, final HTTPProxyData pd) {
+        super(socket);
+        this.sock = socket;
+        this.pd = pd;
+    }
+
+    @Override
+    protected void connect(final String hostname, final int port, final int connectTimeout) throws IOException {
+        sock.connect(new InetSocketAddress(pd.proxyHost, pd.proxyPort), connectTimeout);
+        // Tell the proxy where we actually want to connect to
+        StringBuilder sb = new StringBuilder();
+        sb.append("CONNECT ");
+        sb.append(hostname);
+        sb.append(':');
+        sb.append(port);
+        sb.append(" HTTP/1.0\r\n");
+
+        if ((pd.proxyUser != null) && (pd.proxyPass != null)) {
+            String credentials = pd.proxyUser + ":" + pd.proxyPass;
+            char[] encoded = Base64.encode(StringEncoder.GetBytes(credentials));
+            sb.append("Proxy-Authorization: Basic ");
+            sb.append(encoded);
+            sb.append("\r\n");
+        }
+
+        if (pd.requestHeaderLines != null) {
+            for (int i = 0; i < pd.requestHeaderLines.length; i++) {
+                if (pd.requestHeaderLines[i] != null) {
+                    sb.append(pd.requestHeaderLines[i]);
+                    sb.append("\r\n");
+                }
+            }
+        }
+
+        sb.append("\r\n");
+        OutputStream out = sock.getOutputStream();
+        out.write(StringEncoder.GetBytes(sb.toString()));
+        out.flush();
+        // Parse the HTTP response
+        InputStream in = sock.getInputStream();
+        final LineNumberReader reader = new LineNumberReader(new InputStreamReader(in));
+        String httpReponse = reader.readLine();
+
+        if (!httpReponse.startsWith("HTTP/")) {
+            throw new IOException("The proxy did not send back a valid HTTP response.");
+        }
+
+        // "HTTP/1.X XYZ X" => 14 characters minimum
+
+        if ((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' ')) {
+            throw new IOException("The proxy did not send back a valid HTTP response.");
+        }
+
+        int errorCode;
+
+        try {
+            errorCode = Integer.parseInt(httpReponse.substring(9, 12));
+        }
+        catch (NumberFormatException ignore) {
+            throw new IOException("The proxy did not send back a valid HTTP response.");
+        }
+
+        if ((errorCode < 0) || (errorCode > 999)) {
+            throw new IOException("The proxy did not send back a valid HTTP response.");
+        }
+
+        if (errorCode != 200) {
+            throw new HTTPProxyException(httpReponse.substring(13), errorCode);
+        }
+
+        while (reader.readLine() != null) {
+            // Read until empty line
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/KexManager.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.security.DigestException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.ECPrivateKey;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.TreeSet;
+
+import ch.ethz.ssh2.ConnectionInfo;
+import ch.ethz.ssh2.DHGexParameters;
+import ch.ethz.ssh2.compression.CompressionFactory;
+import ch.ethz.ssh2.compression.Compressor;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.crypto.KeyMaterial;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.PacketKexInit;
+import ch.ethz.ssh2.packets.PacketNewKeys;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.ECPrivateKey;
+
+/**
+ * @version $Id: KexManager.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $
+ */
+public abstract class KexManager implements MessageHandler {
+    protected static final Logger log = Logger.getLogger(KexManager.class);
+
+    private static final Set<String> HOSTKEY_ALGS = new TreeSet<String>();
+    static {
+        HOSTKEY_ALGS.add("ecdsa-sha2-nistp256");
+        HOSTKEY_ALGS.add("ecdsa-sha2-nistp384");
+        HOSTKEY_ALGS.add("ecdsa-sha2-nistp521");
+        HOSTKEY_ALGS.add("ssh-rsa");
+        HOSTKEY_ALGS.add("ssh-dss");
+    }
+
+    private static final Set<String> KEX_ALGS = new TreeSet<String>();
+    static {
+        KEX_ALGS.add("ecdh-sha2-nistp256");
+        KEX_ALGS.add("ecdh-sha2-nistp384");
+        KEX_ALGS.add("ecdh-sha2-nistp521");
+        KEX_ALGS.add("diffie-hellman-group-exchange-sha256");
+        KEX_ALGS.add("diffie-hellman-group-exchange-sha1");
+        KEX_ALGS.add("diffie-hellman-group14-sha1");
+        KEX_ALGS.add("diffie-hellman-group1-sha1");
+    }
+
+    KexState kxs;
+    int kexCount = 0;
+    KeyMaterial km;
+    byte[] sessionId;
+    ClientServerHello csh;
+
+    final Object accessLock = new Object();
+    ConnectionInfo lastConnInfo = null;
+
+    boolean connectionClosed = false;
+
+    boolean ignore_next_kex_packet = false;
+
+    final TransportManager tm;
+
+    CryptoWishList nextKEXcryptoWishList;
+    DHGexParameters nextKEXdhgexParameters;
+    KeyPair nextKEXdsakey;
+    KeyPair nextKEXrsakey;
+    KeyPair nextKEXeckey;
+
+    final SecureRandom rnd;
+
+    public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, SecureRandom rnd) {
+        this.tm = tm;
+        this.csh = csh;
+        this.nextKEXcryptoWishList = initialCwl;
+        this.nextKEXdhgexParameters = new DHGexParameters();
+        this.rnd = rnd;
+    }
+
+    public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException {
+        synchronized (accessLock) {
+            while (true) {
+                if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount)) {
+                    return lastConnInfo;
+                }
+
+                if (connectionClosed) {
+                    throw(IOException) new IOException("Key exchange was not finished, connection is closed.").initCause(tm.getReasonClosedCause());
+                }
+
+                try {
+                    accessLock.wait();
+                }
+                catch (InterruptedException e) {
+                    throw new InterruptedIOException(e.getMessage());
+                }
+            }
+        }
+    }
+
+    private String getFirstMatch(String[] client, String[] server) throws NegotiateException {
+        if (client == null || server == null) {
+            throw new IllegalArgumentException();
+        }
+
+        for (String c : client) {
+            for (String s : server) {
+                if (c.equals(s)) {
+                    return c;
+                }
+            }
+        }
+
+        throw new NegotiateException(String.format("Negotiation failed for %s", Arrays.toString(server)));
+    }
+
+    private boolean compareFirstOfNameList(String[] a, String[] b) {
+        if (a == null || b == null) {
+            throw new IllegalArgumentException();
+        }
+
+        if ((a.length == 0) && (b.length == 0)) {
+            return true;
+        }
+
+        if ((a.length == 0) || (b.length == 0)) {
+            return false;
+        }
+
+        return (a[0].equals(b[0]));
+    }
+
+    private boolean isGuessOK(KexParameters cpar, KexParameters spar) {
+        if (cpar == null || spar == null) {
+            throw new IllegalArgumentException();
+        }
+
+        if (!compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms)) {
+            return false;
+        }
+
+        if (!compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms)) {
+            return false;
+        }
+
+        /*
+         * We do NOT check here if the other algorithms can be agreed on, this
+         * is just a check if kex_algorithms and server_host_key_algorithms were
+         * guessed right!
+         */
+        return true;
+    }
+
+    protected NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
+    throws NegotiateException {
+        NegotiatedParameters np = new NegotiatedParameters();
+        np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);
+        log.info("kex_algo=" + np.kex_algo);
+        np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
+                                                server.server_host_key_algorithms);
+        log.info("server_host_key_algo=" + np.server_host_key_algo);
+        np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
+                                       server.encryption_algorithms_client_to_server);
+        np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
+                                       server.encryption_algorithms_server_to_client);
+        log.info("enc_algo_client_to_server=" + np.enc_algo_client_to_server);
+        log.info("enc_algo_server_to_client=" + np.enc_algo_server_to_client);
+        np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
+                                       server.mac_algorithms_client_to_server);
+        np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
+                                       server.mac_algorithms_server_to_client);
+        log.info("mac_algo_client_to_server=" + np.mac_algo_client_to_server);
+        log.info("mac_algo_server_to_client=" + np.mac_algo_server_to_client);
+        np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
+                                        server.compression_algorithms_client_to_server);
+        np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
+                                        server.compression_algorithms_server_to_client);
+        log.info("comp_algo_client_to_server=" + np.comp_algo_client_to_server);
+        log.info("comp_algo_server_to_client=" + np.comp_algo_server_to_client);
+        np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
+                                   server.languages_client_to_server);
+        np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
+                                   server.languages_server_to_client);
+
+        if (isGuessOK(client, server)) {
+            np.guessOK = true;
+        }
+
+        return np;
+    }
+
+    public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex, KeyPair dsa, KeyPair rsa, KeyPair ec)
+    throws IOException {
+        nextKEXcryptoWishList = cwl;
+        nextKEXdhgexParameters = dhgex;
+        nextKEXdsakey = dsa;
+        nextKEXrsakey = rsa;
+        nextKEXeckey  = ec;
+
+        if (kxs == null) {
+            kxs = new KexState();
+            kxs.local_dsa_key = dsa;
+            kxs.local_rsa_key = rsa;
+            kxs.local_ec_key  = ec;
+            kxs.dhgexParameters = nextKEXdhgexParameters;
+            kxs.localKEX = new PacketKexInit(nextKEXcryptoWishList, rnd);
+            tm.sendKexMessage(kxs.localKEX.getPayload());
+        }
+    }
+
+    private boolean establishKeyMaterial() throws IOException {
+        try {
+            int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
+            int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
+            int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);
+            int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
+            int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
+            int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);
+            km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
+                                    enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
+        }
+        catch (IllegalArgumentException e) {
+            return false;
+        }
+
+        return true;
+    }
+
+    protected void finishKex(boolean clientMode) throws IOException {
+        if (sessionId == null) {
+            sessionId = kxs.H;
+        }
+
+        establishKeyMaterial();
+        /* Tell the other side that we start using the new material */
+        PacketNewKeys ign = new PacketNewKeys();
+        tm.sendKexMessage(ign.getPayload());
+        BlockCipher cbc;
+        MAC mac;
+        Compressor comp;
+
+        try {
+            cbc = BlockCipherFactory.createCipher(clientMode ? kxs.np.enc_algo_client_to_server
+                                                  : kxs.np.enc_algo_server_to_client, true, clientMode ? km.enc_key_client_to_server
+                                                  : km.enc_key_server_to_client, clientMode ? km.initial_iv_client_to_server
+                                                  : km.initial_iv_server_to_client);
+
+            try {
+                mac = new MAC(clientMode ? kxs.np.mac_algo_client_to_server : kxs.np.mac_algo_server_to_client, clientMode
+                              ? km.integrity_key_client_to_server : km.integrity_key_server_to_client);
+            }
+            catch (DigestException e) {
+                throw new IOException(e);
+            }
+
+            comp = CompressionFactory.createCompressor(kxs.np.comp_algo_client_to_server);
+        }
+        catch (IllegalArgumentException f) {
+            throw new IOException(String.format("Fatal error initializing ciphers. %s", f.getMessage()));
+        }
+
+        tm.changeSendCipher(cbc, mac);
+        tm.changeSendCompression(comp);
+        tm.kexFinished();
+    }
+
+    public static String[] getDefaultServerHostkeyAlgorithmList() {
+        return HOSTKEY_ALGS.toArray(new String[HOSTKEY_ALGS.size()]);
+    }
+
+    public static void checkServerHostkeyAlgorithmsList(String[] algos) {
+        for (final String algo : algos) {
+            if (!HOSTKEY_ALGS.contains(algo))
+                throw new IllegalArgumentException("Unknown server host key algorithm '" + algo + "'");
+        }
+    }
+
+    public static String[] getDefaultClientKexAlgorithmList() {
+        return KEX_ALGS.toArray(new String[KEX_ALGS.size()]);
+    }
+
+    public static String[] getDefaultServerKexAlgorithmList() {
+        return KEX_ALGS.toArray(new String[KEX_ALGS.size()]);
+    }
+
+    public static void checkKexAlgorithmList(String[] algos) {
+        for (final String algo : algos) {
+            if (!KEX_ALGS.contains(algo))
+                throw new IllegalArgumentException("Unknown kex algorithm '" + algo + "'");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/KexParameters.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.util.Arrays;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: KexParameters.java 159 2014-05-01 14:12:34Z dkocher@sudo.ch $
+ */
+public class KexParameters {
+    public byte[] cookie;
+    public String[] kex_algorithms;
+    public String[] server_host_key_algorithms;
+    public String[] encryption_algorithms_client_to_server;
+    public String[] encryption_algorithms_server_to_client;
+    public String[] mac_algorithms_client_to_server;
+    public String[] mac_algorithms_server_to_client;
+    public String[] compression_algorithms_client_to_server;
+    public String[] compression_algorithms_server_to_client;
+    public String[] languages_client_to_server;
+    public String[] languages_server_to_client;
+    public boolean first_kex_packet_follows;
+    public int reserved_field1;
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("KexParameters{");
+        sb.append("cookie=").append(Arrays.toString(cookie));
+        sb.append(", kex_algorithms=").append(Arrays.toString(kex_algorithms));
+        sb.append(", server_host_key_algorithms=").append(Arrays.toString(server_host_key_algorithms));
+        sb.append(", encryption_algorithms_client_to_server=").append(Arrays.toString(encryption_algorithms_client_to_server));
+        sb.append(", encryption_algorithms_server_to_client=").append(Arrays.toString(encryption_algorithms_server_to_client));
+        sb.append(", mac_algorithms_client_to_server=").append(Arrays.toString(mac_algorithms_client_to_server));
+        sb.append(", mac_algorithms_server_to_client=").append(Arrays.toString(mac_algorithms_server_to_client));
+        sb.append(", compression_algorithms_client_to_server=").append(Arrays.toString(compression_algorithms_client_to_server));
+        sb.append(", compression_algorithms_server_to_client=").append(Arrays.toString(compression_algorithms_server_to_client));
+        sb.append(", languages_client_to_server=").append(Arrays.toString(languages_client_to_server));
+        sb.append(", languages_server_to_client=").append(Arrays.toString(languages_server_to_client));
+        sb.append(", first_kex_packet_follows=").append(first_kex_packet_follows);
+        sb.append(", reserved_field1=").append(reserved_field1);
+        sb.append('}');
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/KexState.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import ch.ethz.ssh2.DHGexParameters;
+import ch.ethz.ssh2.crypto.dh.GenericDhExchange;
+import ch.ethz.ssh2.crypto.dh.DhGroupExchange;
+import java.math.BigInteger;
+import ch.ethz.ssh2.packets.PacketKexInit;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.ECPrivateKey;
+
+/**
+ * KexState.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class KexState {
+    public PacketKexInit localKEX;
+    public PacketKexInit remoteKEX;
+    public NegotiatedParameters np;
+    public int state = 0;
+
+    public BigInteger K;
+    public byte[] H;
+
+    public byte[] remote_hostkey;
+
+    public String hashAlgo;
+    public GenericDhExchange dhx;
+    public DhGroupExchange dhgx;
+    public DHGexParameters dhgexParameters;
+
+    public KeyPair local_dsa_key;
+    public KeyPair local_rsa_key;
+    public KeyPair local_ec_key;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/MessageHandler.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: MessageHandler.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public interface MessageHandler {
+    public void handleMessage(byte[] msg) throws IOException;
+
+    public void handleFailure(IOException failure);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/NegotiateException.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+
+/**
+ * @version $Id: NegotiateException.java 149 2014-04-28 09:18:35Z dkocher@sudo.ch $
+ */
+public class NegotiateException extends IOException {
+    private static final long serialVersionUID = 3689910669428143157L;
+
+    public NegotiateException() {
+        //
+    }
+
+    public NegotiateException(String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/NegotiatedParameters.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: NegotiatedParameters.java 159 2014-05-01 14:12:34Z dkocher@sudo.ch $
+ */
+public class NegotiatedParameters {
+    public boolean guessOK;
+    public String kex_algo;
+    public String server_host_key_algo;
+    public String enc_algo_client_to_server;
+    public String enc_algo_server_to_client;
+    public String mac_algo_client_to_server;
+    public String mac_algo_server_to_client;
+    public String comp_algo_client_to_server;
+    public String comp_algo_server_to_client;
+    public String lang_client_to_server;
+    public String lang_server_to_client;
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("NegotiatedParameters{");
+        sb.append("guessOK=").append(guessOK);
+        sb.append(", kex_algo='").append(kex_algo).append('\'');
+        sb.append(", server_host_key_algo='").append(server_host_key_algo).append('\'');
+        sb.append(", enc_algo_client_to_server='").append(enc_algo_client_to_server).append('\'');
+        sb.append(", enc_algo_server_to_client='").append(enc_algo_server_to_client).append('\'');
+        sb.append(", mac_algo_client_to_server='").append(mac_algo_client_to_server).append('\'');
+        sb.append(", mac_algo_server_to_client='").append(mac_algo_server_to_client).append('\'');
+        sb.append(", comp_algo_client_to_server='").append(comp_algo_client_to_server).append('\'');
+        sb.append(", comp_algo_server_to_client='").append(comp_algo_server_to_client).append('\'');
+        sb.append(", lang_client_to_server='").append(lang_client_to_server).append('\'');
+        sb.append(", lang_server_to_client='").append(lang_server_to_client).append('\'');
+        sb.append('}');
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/ServerKexManager.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.security.DigestException;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.ConnectionInfo;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.auth.ServerAuthenticationManager;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
+import ch.ethz.ssh2.crypto.dh.GenericDhExchange;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.packets.PacketKexDHInit;
+import ch.ethz.ssh2.packets.PacketKexDHReply;
+import ch.ethz.ssh2.packets.PacketKexInit;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.server.ServerConnectionState;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
+
+/**
+ * @version $Id: ServerKexManager.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public class ServerKexManager extends KexManager {
+
+    private final ServerConnectionState state;
+
+    private boolean authenticationStarted = false;
+
+    public ServerKexManager(ServerConnectionState state) {
+        super(state.tm, state.csh, state.next_cryptoWishList, state.generator);
+        this.state = state;
+    }
+
+    public void handleFailure(final IOException failure) {
+        synchronized (accessLock) {
+            connectionClosed = true;
+            accessLock.notifyAll();
+        }
+    }
+
+    public void handleMessage(byte[] msg) throws IOException {
+        PacketKexInit kip;
+
+        if (msg == null) {
+            synchronized (accessLock) {
+                connectionClosed = true;
+                accessLock.notifyAll();
+                return;
+            }
+        }
+
+        if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT)) {
+            throw new PacketTypeException(msg[0]);
+        }
+
+        if (ignore_next_kex_packet) {
+            ignore_next_kex_packet = false;
+            return;
+        }
+
+        if (msg[0] == Packets.SSH_MSG_KEXINIT) {
+            if ((kxs != null) && (kxs.state != 0)) {
+                throw new PacketTypeException(msg[0]);
+            }
+
+            if (kxs == null) {
+                /*
+                 * Ah, OK, peer wants to do KEX. Let's be nice and play
+                 * together.
+                 */
+                kxs = new KexState();
+                kxs.local_dsa_key = nextKEXdsakey;
+                kxs.local_rsa_key = nextKEXrsakey;
+                kxs.local_ec_key  = nextKEXeckey;
+                kxs.dhgexParameters = nextKEXdhgexParameters;
+                kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
+                kxs.localKEX = kip;
+                tm.sendKexMessage(kip.getPayload());
+            }
+
+            kip = new PacketKexInit(msg);
+            kxs.remoteKEX = kip;
+            kxs.np = mergeKexParameters(kxs.remoteKEX.getKexParameters(), kxs.localKEX.getKexParameters());
+
+            if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false)) {
+                // Guess was wrong, we need to ignore the next kex packet.
+                ignore_next_kex_packet = true;
+            }
+
+            if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")  ||
+                    kxs.np.kex_algo.equals("diffie-hellman-group14-sha1") ||
+                    kxs.np.kex_algo.equals("ecdh-sha2-nistp256")          ||
+                    kxs.np.kex_algo.equals("ecdh-sha2-nistp384")          ||
+                    kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) {
+                kxs.dhx = GenericDhExchange.getInstance(kxs.np.kex_algo);
+                kxs.dhx.init(kxs.np.kex_algo);
+                kxs.state = 1;
+                return;
+            }
+
+            throw new IllegalStateException("Unkown KEX method!");
+        }
+
+        if (msg[0] == Packets.SSH_MSG_NEWKEYS) {
+            if (km == null) {
+                throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
+            }
+
+            BlockCipher cbc;
+            MAC mac;
+
+            try {
+                cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, false,
+                                                      km.enc_key_client_to_server, km.initial_iv_client_to_server);
+
+                try {
+                    mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);
+                }
+                catch (DigestException e) {
+                    throw new IOException(e);
+                }
+            }
+            catch (IllegalArgumentException e) {
+                throw new IOException(e);
+            }
+
+            tm.changeRecvCipher(cbc, mac);
+            ConnectionInfo sci = new ConnectionInfo();
+            kexCount++;
+            sci.keyExchangeAlgorithm = kxs.np.kex_algo;
+            sci.keyExchangeCounter = kexCount;
+            sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
+            sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
+            sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
+            sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
+            sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
+            sci.serverHostKey = kxs.remote_hostkey;
+
+            synchronized (accessLock) {
+                lastConnInfo = sci;
+                accessLock.notifyAll();
+            }
+
+            kxs = null;
+            return;
+        }
+
+        if ((kxs == null) || (kxs.state == 0)) {
+            throw new IOException("Unexpected Kex submessage!");
+        }
+
+        if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")  ||
+                kxs.np.kex_algo.equals("diffie-hellman-group14-sha1") ||
+                kxs.np.kex_algo.equals("ecdh-sha2-nistp256")          ||
+                kxs.np.kex_algo.equals("ecdh-sha2-nistp384")          ||
+                kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) {
+            if (kxs.state == 1) {
+                PacketKexDHInit dhi = new PacketKexDHInit(msg);
+                kxs.dhx.setE(dhi.getE());
+                byte[] hostKey = null;
+
+                if (kxs.np.server_host_key_algo.startsWith("ecdsa-sha2-")) {
+                    hostKey = ECDSASHA2Verify.encodeSSHECDSAPublicKey((ECPublicKey)kxs.local_ec_key.getPublic());
+                }
+
+                if (kxs.np.server_host_key_algo.equals("ssh-rsa")) {
+                    hostKey = RSASHA1Verify.encodeSSHRSAPublicKey((RSAPublicKey)kxs.local_rsa_key.getPublic());
+                }
+
+                if (kxs.np.server_host_key_algo.equals("ssh-dss")) {
+                    hostKey = DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey)kxs.local_dsa_key.getPublic());
+                }
+
+                try {
+                    kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(),
+                                               kxs.remoteKEX.getPayload(), kxs.localKEX.getPayload(), hostKey);
+                }
+                catch (IllegalArgumentException e) {
+                    throw new IOException("KEX error.", e);
+                }
+
+                kxs.K = kxs.dhx.getK();
+                byte[] signature = null;
+
+                if (kxs.np.server_host_key_algo.startsWith("ecdsa-sha2-")) {
+                    ECPrivateKey pk = (ECPrivateKey)kxs.local_ec_key.getPrivate();
+                    byte[] es = ECDSASHA2Verify.generateSignature(kxs.H, pk);
+                    signature = ECDSASHA2Verify.encodeSSHECDSASignature(es, pk.getParams());
+                }
+
+                if (kxs.np.server_host_key_algo.equals("ssh-rsa")) {
+                    byte[] rs = RSASHA1Verify.generateSignature(kxs.H, (RSAPrivateKey)kxs.local_rsa_key.getPrivate());
+                    signature = RSASHA1Verify.encodeSSHRSASignature(rs);
+                }
+
+                if (kxs.np.server_host_key_algo.equals("ssh-dss")) {
+                    byte[] ds = DSASHA1Verify.generateSignature(kxs.H, (DSAPrivateKey)kxs.local_dsa_key.getPrivate(), rnd);
+                    signature = DSASHA1Verify.encodeSSHDSASignature(ds);
+                }
+
+                PacketKexDHReply dhr = new PacketKexDHReply(hostKey, new BigInteger(kxs.dhx.getF()), signature);
+                tm.sendKexMessage(dhr.getPayload());
+                finishKex(false);
+                kxs.state = -1;
+
+                if (authenticationStarted == false) {
+                    authenticationStarted = true;
+                    state.am = new ServerAuthenticationManager(state);
+                }
+
+                return;
+            }
+        }
+
+        throw new IllegalStateException(String.format("Unknown KEX method %s", kxs.np.kex_algo));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/ServerTransportManager.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,34 @@
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import ch.ethz.ssh2.server.ServerConnectionState;
+
+/**
+ * @version $Id: ServerTransportManager.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class ServerTransportManager extends TransportManager {
+
+    private final Socket sock;
+
+    public ServerTransportManager(final Socket socket) {
+        super(socket);
+        // TCP connection is already established
+        this.sock = socket;
+    }
+
+    public void connect(ServerConnectionState state) throws IOException {
+        /* Parse the client lin
+        e and say hello - important: this information is later needed for the
+         * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
+        * for later use.
+        */
+        state.csh = ClientServerHello.serverHello(state.softwareversion, sock.getInputStream(), sock.getOutputStream());
+        TransportConnection tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), state.generator);
+        KexManager km = new ServerKexManager(state);
+        super.init(tc, km);
+        km.initiateKEX(state.next_cryptoWishList, null, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
+        this.startReceiver();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/TransportConnection.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.compression.Compressor;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.cipher.CipherInputStream;
+import ch.ethz.ssh2.crypto.cipher.CipherOutputStream;
+import ch.ethz.ssh2.crypto.cipher.NullCipher;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.Packets;
+
+/**
+ * TransportConnection.
+ *
+ * @author Christian Plattner
+ * @version $Id: TransportConnection.java 144 2014-04-25 12:48:25Z dkocher@sudo.ch $
+ */
+public class TransportConnection {
+    private static final Logger log = Logger.getLogger(TransportConnection.class);
+
+    int send_seq_number = 0;
+
+    int recv_seq_number = 0;
+
+    CipherInputStream cis;
+
+    CipherOutputStream cos;
+
+    boolean useRandomPadding;
+
+    /* Depends on current MAC and CIPHER */
+
+    MAC send_mac;
+
+    byte[] send_mac_buffer;
+
+    int send_padd_blocksize = 8;
+
+    MAC recv_mac;
+
+    byte[] recv_mac_buffer;
+
+    byte[] recv_mac_buffer_cmp;
+
+    int recv_padd_blocksize = 8;
+
+    Compressor recv_comp;
+
+    Compressor send_comp;
+
+    boolean can_compress;
+
+    byte[] recv_comp_buffer;
+
+    byte[] send_comp_buffer;
+
+    /* won't change */
+
+    final byte[] send_padding_buffer = new byte[256];
+
+    final byte[] send_packet_header_buffer = new byte[5];
+
+    final byte[] recv_padding_buffer = new byte[256];
+
+    final byte[] recv_packet_header_buffer = new byte[5];
+
+    boolean recv_packet_header_present = false;
+
+    ClientServerHello csh;
+
+    final SecureRandom rnd;
+
+    public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd) {
+        this.cis = new CipherInputStream(new NullCipher(), is);
+        this.cos = new CipherOutputStream(new NullCipher(), os);
+        this.rnd = rnd;
+    }
+
+    public void changeRecvCipher(BlockCipher bc, MAC mac) {
+        cis.changeCipher(bc);
+        recv_mac = mac;
+        recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
+        recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
+        recv_padd_blocksize = bc.getBlockSize();
+
+        if (recv_padd_blocksize < 8) {
+            recv_padd_blocksize = 8;
+        }
+    }
+
+    public void changeSendCipher(BlockCipher bc, MAC mac) {
+        if ((bc instanceof NullCipher) == false) {
+            /* Only use zero byte padding for the first few packets */
+            useRandomPadding = true;
+            /* Once we start encrypting, there is no way back */
+        }
+
+        cos.changeCipher(bc);
+        send_mac = mac;
+        send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
+        send_padd_blocksize = bc.getBlockSize();
+
+        if (send_padd_blocksize < 8) {
+            send_padd_blocksize = 8;
+        }
+    }
+
+    public void changeRecvCompression(Compressor comp) {
+        recv_comp = comp;
+
+        if (comp != null) {
+            recv_comp_buffer = new byte[comp.getBufferSize()];
+        }
+    }
+
+    public void changeSendCompression(Compressor comp) {
+        send_comp = comp;
+
+        if (comp != null) {
+            send_comp_buffer = new byte[comp.getBufferSize()];
+        }
+    }
+
+    public void sendMessage(byte[] message) throws IOException {
+        sendMessage(message, 0, message.length, 0);
+    }
+
+    public void sendMessage(byte[] message, int off, int len) throws IOException {
+        sendMessage(message, off, len, 0);
+    }
+
+    public int getPacketOverheadEstimate() {
+        // return an estimate for the paket overhead (for send operations)
+        return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
+    }
+
+    public void sendMessage(byte[] message, int off, int len, int padd) throws IOException {
+        if (padd < 4) {
+            padd = 4;
+        }
+        else if (padd > 64) {
+            padd = 64;
+        }
+
+        if (send_comp != null && can_compress) {
+            len = send_comp.compress(message, off, len, send_comp_buffer);
+            message = send_comp_buffer;
+        }
+
+        int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
+        int slack = packet_len % send_padd_blocksize;
+
+        if (slack != 0) {
+            packet_len += (send_padd_blocksize - slack);
+        }
+
+        if (packet_len < 16) {
+            packet_len = 16;
+        }
+
+        int padd_len = packet_len - (5 + len);
+
+        if (useRandomPadding) {
+            for (int i = 0; i < padd_len; i = i + 4) {
+                /*
+                 * don't waste calls to rnd.nextInt() (by using only 8bit of the
+                 * output). just believe me: even though we may write here up to 3
+                 * bytes which won't be used, there is no "buffer overflow" (i.e.,
+                 * arrayindexoutofbounds). the padding buffer is big enough =) (256
+                 * bytes, and that is bigger than any current cipher block size + 64).
+                 */
+                int r = rnd.nextInt();
+                send_padding_buffer[i] = (byte) r;
+                send_padding_buffer[i + 1] = (byte)(r >> 8);
+                send_padding_buffer[i + 2] = (byte)(r >> 16);
+                send_padding_buffer[i + 3] = (byte)(r >> 24);
+            }
+        }
+        else {
+            /* use zero padding for unencrypted traffic */
+            for (int i = 0; i < padd_len; i++) {
+                send_padding_buffer[i] = 0;
+            }
+
+            /* Actually this code is paranoid: we never filled any
+             * bytes into the padding buffer so far, therefore it should
+             * consist of zeros only.
+             */
+        }
+
+        send_packet_header_buffer[0] = (byte)((packet_len - 4) >> 24);
+        send_packet_header_buffer[1] = (byte)((packet_len - 4) >> 16);
+        send_packet_header_buffer[2] = (byte)((packet_len - 4) >> 8);
+        send_packet_header_buffer[3] = (byte)((packet_len - 4));
+        send_packet_header_buffer[4] = (byte) padd_len;
+        cos.write(send_packet_header_buffer, 0, 5);
+        cos.write(message, off, len);
+        cos.write(send_padding_buffer, 0, padd_len);
+
+        if (send_mac != null) {
+            send_mac.initMac(send_seq_number);
+            send_mac.update(send_packet_header_buffer, 0, 5);
+            send_mac.update(message, off, len);
+            send_mac.update(send_padding_buffer, 0, padd_len);
+            send_mac.getMac(send_mac_buffer, 0);
+            cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
+        }
+
+        cos.flush();
+
+        if (log.isDebugEnabled()) {
+            log.debug("Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
+        }
+
+        send_seq_number++;
+    }
+
+    public int peekNextMessageLength() throws IOException {
+        if (recv_packet_header_present == false) {
+            cis.read(recv_packet_header_buffer, 0, 5);
+            recv_packet_header_present = true;
+        }
+
+        int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
+                            | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
+                            | ((recv_packet_header_buffer[3] & 0xff));
+        int padding_length = recv_packet_header_buffer[4] & 0xff;
+
+        if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12) {
+            throw new PacketFormatException(String.format("Illegal packet size (%d)", packet_length));
+        }
+
+        int payload_length = packet_length - padding_length - 1;
+
+        if (payload_length < 0) {
+            throw new PacketFormatException(String.format("Illegal padding_length in packet from remote (%d)", padding_length));
+        }
+
+        return payload_length;
+    }
+
+    public int receiveMessage(byte buffer[], int off, int len) throws IOException {
+        if (recv_packet_header_present == false) {
+            cis.read(recv_packet_header_buffer, 0, 5);
+        }
+        else {
+            recv_packet_header_present = false;
+        }
+
+        int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
+                            | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
+                            | ((recv_packet_header_buffer[3] & 0xff));
+        int padding_length = recv_packet_header_buffer[4] & 0xff;
+
+        if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12) {
+            throw new PacketFormatException(String.format("Illegal packet size (%d)", packet_length));
+        }
+
+        int payload_length = packet_length - padding_length - 1;
+
+        if (payload_length < 0) {
+            throw new PacketFormatException(String.format("Illegal padding_length in packet from remote (%d)", padding_length));
+        }
+
+        if (payload_length >= len) {
+            throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");
+        }
+
+        cis.read(buffer, off, payload_length);
+        cis.read(recv_padding_buffer, 0, padding_length);
+
+        if (recv_mac != null) {
+            cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
+            recv_mac.initMac(recv_seq_number);
+            recv_mac.update(recv_packet_header_buffer, 0, 5);
+            recv_mac.update(buffer, off, payload_length);
+            recv_mac.update(recv_padding_buffer, 0, padding_length);
+            recv_mac.getMac(recv_mac_buffer_cmp, 0);
+
+            for (int i = 0; i < recv_mac_buffer.length; i++) {
+                if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i]) {
+                    throw new IOException("Remote sent corrupt MAC.");
+                }
+            }
+        }
+
+        recv_seq_number++;
+
+        if (log.isDebugEnabled()) {
+            log.debug("Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
+                      + " bytes payload");
+        }
+
+        if (recv_comp != null && can_compress) {
+            int[] uncomp_len = new int[] {payload_length};
+            buffer = recv_comp.uncompress(buffer, off, uncomp_len);
+            return uncomp_len[0];
+        }
+        else {
+            return payload_length;
+        }
+    }
+
+    public void startCompression() {
+        can_compress = true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/TransportManager.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,505 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.Socket;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.ethz.ssh2.ConnectionInfo;
+import ch.ethz.ssh2.ConnectionMonitor;
+import ch.ethz.ssh2.DHGexParameters;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.compression.Compressor;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.PacketDisconnect;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.packets.TypesReader;
+
+/**
+ * Yes, the "standard" is a big mess. On one side, the say that arbitrary channel
+ * packets are allowed during kex exchange, on the other side we need to blindly
+ * ignore the next _packet_ if the KEX guess was wrong. Where do we know from that
+ * the next packet is not a channel data packet? Yes, we could check if it is in
+ * the KEX range. But the standard says nothing about this. The OpenSSH guys
+ * block local "normal" traffic during KEX. That's fine - however, they assume
+ * that the other side is doing the same. During re-key, if they receive traffic
+ * other than KEX, they become horribly irritated and kill the connection. Since
+ * we are very likely going to communicate with OpenSSH servers, we have to play
+ * the same game - even though we could do better.
+ *
+ * @author Christian Plattner
+ * @version $Id: TransportManager.java 161 2014-05-01 18:01:55Z dkocher@sudo.ch $
+ */
+public abstract class TransportManager {
+    protected static final Logger log = Logger.getLogger(TransportManager.class);
+
+    private static final class HandlerEntry {
+        MessageHandler mh;
+        int low;
+        int high;
+    }
+
+    /**
+     * Advertised maximum SSH packet size that the other side can send to us.
+     */
+    public static final int MAX_PACKET_SIZE = 64 * 1024;
+
+    private final List<AsynchronousEntry> asynchronousQueue
+        = new ArrayList<AsynchronousEntry>();
+
+    private Thread asynchronousThread = null;
+    private boolean asynchronousPending = false;
+
+    private Socket socket;
+
+    protected TransportManager(final Socket socket) {
+        this.socket = socket;
+    }
+
+    private static final class AsynchronousEntry {
+        public byte[] message;
+
+        public AsynchronousEntry(byte[] message) {
+            this.message = message;
+        }
+    }
+
+    private final class AsynchronousWorker implements Runnable {
+        public void run() {
+            while (true) {
+                final AsynchronousEntry item;
+
+                synchronized (asynchronousQueue) {
+                    if (asynchronousQueue.size() == 0) {
+                        // Only now we may reset the flag, since we are sure that all queued items
+                        // have been sent (there is a slight delay between de-queuing and sending,
+                        // this is why we need this flag! See code below. Sending takes place outside
+                        // of this lock, this is why a test for size()==0 (from another thread) does not ensure
+                        // that all messages have been sent.
+                        asynchronousPending = false;
+                        // Notify any senders that they can proceed, all async messages have been delivered
+                        asynchronousQueue.notifyAll();
+
+                        // After the queue is empty for about 2 seconds, stop this thread
+                        try {
+                            asynchronousQueue.wait(2000);
+                        }
+                        catch (InterruptedException ignore) {
+                            //
+                        }
+
+                        if (asynchronousQueue.size() == 0) {
+                            asynchronousThread = null;
+                            return;
+                        }
+                    }
+
+                    item = asynchronousQueue.remove(0);
+                }
+
+                try {
+                    sendMessageImmediate(item.message);
+                }
+                catch (IOException e) {
+                    // There is no point in handling it - it simply means that the connection has a problem and we should stop
+                    // sending asynchronously messages. We do not need to signal that we have exited (asynchronousThread = null):
+                    // further messages in the queue cannot be sent by this or any other thread.
+                    // Other threads will sooner or later (when receiving or sending the next message) get the
+                    // same IOException and get to the same conclusion.
+                    log.warning(e.getMessage());
+                    return;
+                }
+            }
+        }
+    }
+
+    private final Object connectionSemaphore = new Object();
+
+    private boolean flagKexOngoing;
+
+    private boolean   connectionClosed;
+    private Throwable reasonClosedCause;
+
+    private TransportConnection tc;
+    private KexManager km;
+
+    private final List<HandlerEntry> messageHandlers = new ArrayList<HandlerEntry>();
+
+    private List<ConnectionMonitor> connectionMonitors = new ArrayList<ConnectionMonitor>();
+    boolean monitorsWereInformed = false;
+
+    protected void init(TransportConnection tc, KexManager km) {
+        this.tc = tc;
+        this.km = km;
+    }
+
+    public int getPacketOverheadEstimate() {
+        return tc.getPacketOverheadEstimate();
+    }
+
+    public ConnectionInfo getConnectionInfo(int kexNumber) throws IOException {
+        return km.getOrWaitForConnectionInfo(kexNumber);
+    }
+
+    public Throwable getReasonClosedCause() {
+        synchronized (connectionSemaphore) {
+            return reasonClosedCause;
+        }
+    }
+
+    public byte[] getSessionIdentifier() {
+        return km.sessionId;
+    }
+
+    public void close(Throwable cause, boolean useDisconnectPacket) {
+        if (useDisconnectPacket == false) {
+            // OK, hard shutdown - do not acquire the semaphore,
+            // perhaps somebody is inside (and waits until
+            // the remote side is ready to accept new data).
+            try {
+                socket.close();
+            }
+            catch (IOException ignore) {
+            }
+
+            // OK, whoever tried to send data, should now agree that
+            // there is no point in further waiting =)
+            // It is safe now to acquire the semaphore.
+        }
+
+        synchronized (connectionSemaphore) {
+            if (!connectionClosed) {
+                if (useDisconnectPacket == true) {
+                    try {
+                        if (tc != null)
+                            tc.sendMessage(new PacketDisconnect(PacketDisconnect.Reason.SSH_DISCONNECT_BY_APPLICATION, "").getPayload());
+                    }
+                    catch (IOException ignore) {
+                    }
+
+                    try {
+                        socket.close();
+                    }
+                    catch (IOException ignore) {
+                    }
+                }
+
+                connectionClosed = true;
+                reasonClosedCause = cause;
+            }
+
+            connectionSemaphore.notifyAll();
+        }
+
+        // check if we need to inform the monitors
+        List<ConnectionMonitor> monitors = null;
+
+        synchronized (this) {
+            // Short term lock to protect "connectionMonitors"
+            // and "monitorsWereInformed"
+            // (they may be modified concurrently)
+            if (monitorsWereInformed == false) {
+                monitorsWereInformed = true;
+                monitors = new ArrayList<ConnectionMonitor>(connectionMonitors);
+            }
+        }
+
+        if (monitors != null) {
+            for (ConnectionMonitor cmon : monitors) {
+                try {
+                    cmon.connectionLost(reasonClosedCause);
+                }
+                catch (Exception ignore) {
+                }
+            }
+        }
+    }
+
+    protected void startReceiver() throws IOException {
+        final Thread receiveThread = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    receiveLoop();
+                    // Can only exit with exception
+                }
+                catch (IOException e) {
+                    close(e, false);
+                    log.warning(e.getMessage());
+
+                    // Tell all handlers that it is time to say goodbye
+                    if (km != null) {
+                        km.handleFailure(e);
+                    }
+
+                    for (HandlerEntry he : messageHandlers) {
+                        he.mh.handleFailure(e);
+                    }
+                }
+
+                if (log.isDebugEnabled()) {
+                    log.debug("Receive thread: back from receiveLoop");
+                }
+            }
+        });
+        receiveThread.setName("Transport Manager");
+        receiveThread.setDaemon(true);
+        receiveThread.start();
+    }
+
+    public void registerMessageHandler(MessageHandler mh, int low, int high) {
+        HandlerEntry he = new HandlerEntry();
+        he.mh = mh;
+        he.low = low;
+        he.high = high;
+
+        synchronized (messageHandlers) {
+            messageHandlers.add(he);
+        }
+    }
+
+    public void removeMessageHandler(MessageHandler handler) {
+        synchronized (messageHandlers) {
+            for (int i = 0; i < messageHandlers.size(); i++) {
+                HandlerEntry he = messageHandlers.get(i);
+
+                if (he.mh == handler) {
+                    messageHandlers.remove(i);
+                    break;
+                }
+            }
+        }
+    }
+
+    public void sendKexMessage(byte[] msg) throws IOException {
+        synchronized (connectionSemaphore) {
+            if (connectionClosed) {
+                throw(IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
+            }
+
+            flagKexOngoing = true;
+
+            try {
+                tc.sendMessage(msg);
+            }
+            catch (IOException e) {
+                close(e, false);
+                throw e;
+            }
+        }
+    }
+
+    public void kexFinished() throws IOException {
+        synchronized (connectionSemaphore) {
+            flagKexOngoing = false;
+            connectionSemaphore.notifyAll();
+        }
+    }
+
+    /**
+     * @param cwl   Crypto wishlist
+     * @param dhgex Diffie-hellman group exchange
+     * @param dsa   may be null if this is a client connection
+     * @param rsa   may be null if this is a client connection
+     * @throws IOException
+     */
+    public void forceKeyExchange(CryptoWishList cwl, DHGexParameters dhgex, KeyPair dsa, KeyPair rsa, KeyPair ec)
+    throws IOException {
+        synchronized (connectionSemaphore) {
+            if (connectionClosed) {
+                throw(IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
+            }
+        }
+
+        km.initiateKEX(cwl, dhgex, dsa, rsa, ec);
+    }
+
+    public void changeRecvCipher(BlockCipher bc, MAC mac) {
+        tc.changeRecvCipher(bc, mac);
+    }
+
+    public void changeSendCipher(BlockCipher bc, MAC mac) {
+        tc.changeSendCipher(bc, mac);
+    }
+
+    public void changeRecvCompression(Compressor comp) {
+        tc.changeRecvCompression(comp);
+    }
+
+    public void changeSendCompression(Compressor comp) {
+        tc.changeSendCompression(comp);
+    }
+
+    public void sendAsynchronousMessage(byte[] msg) throws IOException {
+        synchronized (asynchronousQueue) {
+            asynchronousQueue.add(new AsynchronousEntry(msg));
+            asynchronousPending = true;
+
+            /* This limit should be flexible enough. We need this, otherwise the peer
+             * can flood us with global requests (and other stuff where we have to reply
+             * with an asynchronous message) and (if the server just sends data and does not
+             * read what we send) this will probably put us in a low memory situation
+             * (our send queue would grow and grow and...) */
+
+            if (asynchronousQueue.size() > 100) {
+                throw new IOException("The peer is not consuming our asynchronous replies.");
+            }
+
+            // Check if we have an asynchronous sending thread
+            if (asynchronousThread == null) {
+                asynchronousThread = new Thread(new AsynchronousWorker());
+                asynchronousThread.setDaemon(true);
+                asynchronousThread.start();
+                // The thread will stop after 2 seconds of inactivity (i.e., empty queue)
+            }
+
+            asynchronousQueue.notifyAll();
+        }
+    }
+
+    public void setConnectionMonitors(List<ConnectionMonitor> monitors) {
+        synchronized (this) {
+            connectionMonitors = new ArrayList<ConnectionMonitor>(monitors);
+        }
+    }
+
+    /**
+     * Send a message but ensure that all queued messages are being sent first.
+     *
+     * @param msg Message
+     * @throws IOException
+     */
+    public void sendMessage(byte[] msg) throws IOException {
+        synchronized (asynchronousQueue) {
+            while (asynchronousPending) {
+                try {
+                    asynchronousQueue.wait();
+                }
+                catch (InterruptedException e) {
+                    throw new InterruptedIOException(e.getMessage());
+                }
+            }
+        }
+
+        sendMessageImmediate(msg);
+    }
+
+    /**
+     * Send message, ignore queued async messages that have not been delivered yet.
+     * Will be called directly from the asynchronousThread thread.
+     *
+     * @param msg Message
+     * @throws IOException
+     */
+    public void sendMessageImmediate(byte[] msg) throws IOException {
+        synchronized (connectionSemaphore) {
+            while (true) {
+                if (connectionClosed) {
+                    throw(IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
+                }
+
+                if (!flagKexOngoing) {
+                    break;
+                }
+
+                try {
+                    connectionSemaphore.wait();
+                }
+                catch (InterruptedException e) {
+                    throw new InterruptedIOException(e.getMessage());
+                }
+            }
+
+            try {
+                tc.sendMessage(msg);
+            }
+            catch (IOException e) {
+                close(e, false);
+                throw e;
+            }
+        }
+    }
+
+    private void receiveLoop() throws IOException {
+        while (true) {
+            final byte[] buffer = new byte[MAX_PACKET_SIZE];
+            final int length = tc.receiveMessage(buffer, 0, buffer.length);
+            final byte[] packet = new byte[length];
+            System.arraycopy(buffer, 0, packet, 0, length);
+            final int type = packet[0] & 0xff;
+            log.debug(String.format("transport manager receive loop type %d", type));
+
+            switch (type) {
+                case Packets.SSH_MSG_IGNORE:
+                    break;
+
+                case Packets.SSH_MSG_DEBUG: {
+                        TypesReader tr = new TypesReader(packet);
+                        tr.readByte();
+                        // always_display
+                        tr.readBoolean();
+                        String message = tr.readString();
+
+                        if (log.isDebugEnabled()) {
+                            log.debug(String.format("Debug message from remote: '%s'", message));
+                        }
+
+                        break;
+                    }
+
+                case Packets.SSH_MSG_UNIMPLEMENTED:
+                    throw new PacketTypeException(type);
+
+                case Packets.SSH_MSG_DISCONNECT: {
+                        final PacketDisconnect disconnect = new PacketDisconnect(packet);
+                        throw new DisconnectException(disconnect.getReason(), disconnect.getMessage());
+                    }
+
+                case Packets.SSH_MSG_KEXINIT:
+                case Packets.SSH_MSG_NEWKEYS:
+                case Packets.SSH_MSG_KEXDH_INIT:
+                case Packets.SSH_MSG_KEXDH_REPLY:
+                case Packets.SSH_MSG_KEX_DH_GEX_REQUEST:
+                case Packets.SSH_MSG_KEX_DH_GEX_INIT:
+                case Packets.SSH_MSG_KEX_DH_GEX_REPLY:
+                    // Is it a KEX Packet
+                    km.handleMessage(packet);
+                    break;
+
+                case Packets.SSH_MSG_USERAUTH_SUCCESS:
+                    tc.startCompression();
+
+                // Continue with message handlers
+                default:
+                    boolean handled = false;
+
+                    for (HandlerEntry handler : messageHandlers) {
+                        if ((handler.low <= type) && (type <= handler.high)) {
+                            handler.mh.handleMessage(packet);
+                            handled = true;
+                            break;
+                        }
+                    }
+
+                    if (!handled) {
+                        throw new PacketTypeException(type);
+                    }
+
+                    break;
+            }
+
+            if (log.isDebugEnabled()) {
+                log.debug(String.format("Handled packet %d", type));
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/util/StringEncoder.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner.
+ * All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.util;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: StringEncoder.java 43 2011-06-21 18:34:06Z dkocher@sudo.ch $
+ */
+public class StringEncoder {
+    public static byte[] GetBytes(String data) {
+        try {
+            return data.getBytes("UTF-8");
+        }
+        catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String GetString(byte[] data) {
+        return GetString(data, 0, data.length);
+    }
+
+    public static String GetString(byte[] data, int off, int len) {
+        try {
+            return new String(data, off, len, "UTF-8");
+        }
+        catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/util/TimeoutService.java	Thu Jul 31 16:33:38 2014 -0700
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+
+import ch.ethz.ssh2.log.Logger;
+
+/**
+ * TimeoutService (beta). Here you can register a timeout.
+ * <p>
+ * Implemented having large scale programs in mind: if you open many concurrent SSH connections
+ * that rely on timeouts, then there will be only one timeout thread. Once all timeouts
+ * have expired/are cancelled, the thread will (sooner or later) exit.
+ * Only after new timeouts arrive a new thread (singleton) will be instantiated.
+ *
+ * @author Christian Plattner
+ * @version $Id: TimeoutService.java 89 2014-04-07 14:36:24Z dkocher@sudo.ch $
+ */
+public class TimeoutService {
+    private static final Logger log = Logger.getLogger(TimeoutService.class);
+
+    public static class TimeoutToken {
+        private long runTime;
+        private Runnable handler;
+
+        private TimeoutToken(long runTime, Runnable handler) {
+            this.runTime = runTime;
+            this.handler = handler;
+        }
+    }
+
+    private static class TimeoutThread extends Thread {
+        @Override
+        public void run() {
+            synchronized (todolist) {
+                while (true) {
+                    if (todolist.size() == 0) {
+                        timeoutThread = null;
+                        return;
+                    }
+
+                    long now = System.currentTimeMillis();
+                    TimeoutToken tt = todolist.getFirst();
+
+                    if (tt.runTime > now) {
+                        /* Not ready yet, sleep a little bit */
+                        try {
+                            todolist.wait(tt.runTime - now);
+                        }
+                        catch (InterruptedException ignored) {
+                        }
+
+                        /* We cannot simply go on, since it could be that the token
+                         * was removed (cancelled) or another one has been inserted in
+                         * the meantime.
+                         */
+                        continue;
+                    }
+
+                    todolist.removeFirst();
+
+                    try {
+                        tt.handler.run();
+                    }
+                    catch (Exception e) {
+                        StringWriter sw = new StringWriter();
+                        e.printStackTrace(new PrintWriter(sw));
+                        log.warning("Exeception in Timeout handler:" + e.getMessage() + "(" + sw.toString() + ")");
+                    }
+                }
+            }
+        }
+    }
+
+    /* The list object is also used for locking purposes */
+    private static final LinkedList<TimeoutToken> todolist = new LinkedList<TimeoutService.TimeoutToken>();
+
+    private static Thread timeoutThread = null;
+
+    /**
+     * It is assumed that the passed handler will not execute for a long time.
+     *
+     * @param runTime
+     * @param handler
+     * @return a TimeoutToken that can be used to cancel the timeout.
+     */
+    public static TimeoutToken addTimeoutHandler(long runTime, Runnable handler) {
+        TimeoutToken token = new TimeoutToken(runTime, handler);
+
+        synchronized (todolist) {
+            todolist.add(token);
+            Collections.sort(todolist, new Comparator<TimeoutToken>() {
+                public int compare(TimeoutToken o1, TimeoutToken o2) {
+                    if (o1.runTime > o2.runTime)
+                        return 1;
+
+                    if (o1.runTime == o2.runTime)
+                        return 0;
+
+                    return -1;
+                }
+            });
+
+            if (timeoutThread != null)
+                timeoutThread.interrupt();
+            else {
+                timeoutThread = new TimeoutThread();
+                timeoutThread.setDaemon(true);
+                timeoutThread.start();
+            }
+        }
+
+        return token;
+    }
+
+    public static void cancelTimeoutHandler(TimeoutToken token) {
+        synchronized (todolist) {
+            todolist.remove(token);
+
+            if (timeoutThread != null)
+                timeoutThread.interrupt();
+        }
+    }
+
+}
--- a/src/com/five_ten_sg/connectbot/GeneratePubkeyActivity.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/GeneratePubkeyActivity.java	Thu Jul 31 16:33:38 2014 -0700
@@ -48,7 +48,7 @@
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
 
 public class GeneratePubkeyActivity extends Activity implements OnEntropyGatheredListener {
     public final static String TAG = "ConnectBot.GeneratePubkeyActivity";
--- a/src/com/five_ten_sg/connectbot/HelpActivity.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/HelpActivity.java	Thu Jul 31 16:33:38 2014 -0700
@@ -50,6 +50,7 @@
                                     getResources().getText(R.string.title_help)));
         AssetManager am = this.getAssets();
         LinearLayout content = (LinearLayout)this.findViewById(R.id.topics);
+
         try {
             for (String name : am.list(HELPDIR)) {
                 if (name.endsWith(SUFFIX)) {
--- a/src/com/five_ten_sg/connectbot/HostEditorActivity.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/HostEditorActivity.java	Thu Jul 31 16:33:38 2014 -0700
@@ -323,8 +323,11 @@
         // get all the original text summaries
         for (String key : this.pref.values.keySet()) {
             Preference pref = this.findPreference(key);
+
             if (pref == null) continue;
+
             if (pref instanceof CheckBoxPreference) continue;
+
             CharSequence value = pref.getSummary();
             summaries.put(key, value);
         }
@@ -334,8 +337,11 @@
         // for all text preferences, set hint as current database value if it is non-empty
         for (String key : this.pref.values.keySet()) {
             Preference pref = this.findPreference(key);
+
             if (pref == null) continue;
+
             if (pref instanceof CheckBoxPreference) continue;
+
             CharSequence value = this.pref.getString(key, "");
 
             if (key.equals(HostDatabase.FIELD_HOST_PUBKEYID)) {
@@ -358,10 +364,12 @@
             else if ((pref instanceof ListPreference) && (value != null)) {
                 ListPreference listPref = (ListPreference) pref;
                 int entryIndex = listPref.findIndexOfValue(value.toString());
+
                 if (entryIndex >= 0) value = listPref.getEntries()[entryIndex];
             }
 
             if ((value == null) || (value.length() == 0)) value = summaries.get(key);
+
             pref.setSummary(value);
         }
     }
--- a/src/com/five_ten_sg/connectbot/HostListActivity.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/HostListActivity.java	Thu Jul 31 16:33:38 2014 -0700
@@ -403,21 +403,30 @@
                         File.separator + "deployment.connections";
             BufferedReader reader = new BufferedReader(new FileReader(fn));
             String line = null;
+
             while ((line = reader.readLine()) != null) {
                 if (line.length() == 0) continue;               // empty
-                if (line.substring(0,1).equals("#")) continue;  // comment
+
+                if (line.substring(0, 1).equals("#")) continue; // comment
+
                 if (!line.contains("://")) continue;            // invalid uri
+
                 Uri uri = Uri.parse(line);
                 ContentValues values = null;
+
                 while ((line = reader.readLine()).length() > 0) {
                     String [] parts = line.split("=");
+
                     if (parts.length != 2) continue;
+
                     if (values == null) values = new ContentValues();
+
                     values.put(parts[0].trim(), parts[1].trim());
                 }
+
                 if (uri.getScheme().equals("global")) {
                     Editor editor = prefs.edit();
-                    HashMap<String,String> types = new HashMap<String,String>();
+                    HashMap<String, String> types = new HashMap<String, String>();
                     types.put("memkeys", "boolean");
                     types.put("connPersist", "boolean");
                     types.put("emulation", "string");
@@ -451,30 +460,38 @@
                     types.put("upload_dest_prompt", "boolean");
                     types.put("background_file_transfer", "boolean");
                     types.put("debug_keycodes", "boolean");
+
                     for (String key : values.keySet()) {
                         if (types.containsKey(key)) {
                             String sv = values.getAsString(key);
                             editor.putString(key, sv);
                         }
                     }
+
                     editor.commit();
                 }
                 else {
                     HostBean host = TransportFactory.findHost(hostdb, uri);
+
                     if (host == null) {
                         host = TransportFactory.getTransport(uri.getScheme()).createHost(uri);
                         host.setColor(HostDatabase.COLOR_GRAY);
                         host.setPubkeyId(HostDatabase.PUBKEYID_ANY);
                         hostdb.saveHost(host);
                     }
+
                     host = TransportFactory.findHost(hostdb, uri);
+
                     if (host == null) continue;
+
                     if (values == null) continue;
+
                     SQLiteDatabase db = hostdb.getWritableDatabase();
                     db.update(HostDatabase.TABLE_HOSTS, values, "_id = ?", new String[] { String.valueOf(host.getId()) });
                     db.close();
                 }
             }
+
             reader.close();
             (new File(fn)).delete();
         }
--- a/src/com/five_ten_sg/connectbot/PubkeyListActivity.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/PubkeyListActivity.java	Thu Jul 31 16:33:38 2014 -0700
@@ -63,9 +63,9 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
-import com.trilead.ssh2.crypto.Base64;
-import com.trilead.ssh2.crypto.PEMDecoder;
-import com.trilead.ssh2.crypto.PEMStructure;
+import ch.ethz.ssh2.crypto.Base64;
+import ch.ethz.ssh2.crypto.PEMDecoder;
+import ch.ethz.ssh2.crypto.PEMStructure;
 
 /**
  * List public keys in database by nickname and describe their properties. Allow users to import,
@@ -638,7 +638,8 @@
             if (imported) {
                 try {
                     PEMStructure struct = PEMDecoder.parsePEM(new String(pubkey.getPrivateKey()).toCharArray());
-                    String type = (struct.pemType == PEMDecoder.PEM_RSA_PRIVATE_KEY) ? "RSA" : "DSA";
+                    String type = (struct.pemType == PEMDecoder.PEM_RSA_PRIVATE_KEY) ? "RSA" :
+                                  (struct.pemType == PEMDecoder.PEM_DSA_PRIVATE_KEY) ? "DSA" : "EC";
                     holder.caption.setText(String.format("%s unknown-bit", type));
                 }
                 catch (IOException e) {
--- a/src/com/five_ten_sg/connectbot/WizardActivity.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/WizardActivity.java	Thu Jul 31 16:33:38 2014 -0700
@@ -48,7 +48,6 @@
         // inflate the layout for EULA step
         LayoutInflater inflater = LayoutInflater.from(this);
         View helpView = inflater.inflate(R.layout.wiz_eula, this.flipper, false);
-
         this.flipper.addView(helpView);
         // Add a view for each help topic we want the user to see.
         String[] topics = getResources().getStringArray(R.array.list_wizard_topics);
--- a/src/com/five_ten_sg/connectbot/bean/PubkeyBean.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/bean/PubkeyBean.java	Thu Jul 31 16:33:38 2014 -0700
@@ -35,12 +35,6 @@
 public class PubkeyBean extends AbstractBean {
     public static final String BEAN_NAME = "pubkey";
 
-    private static final String KEY_TYPE_RSA = "RSA";
-
-    private static final String KEY_TYPE_DSA = "DSA";
-
-    private static final String KEY_TYPE_EC = "EC";
-
     /* Database fields */
     private long id;
     private String nickname;
@@ -167,7 +161,7 @@
             final StringBuilder sb = new StringBuilder();
 
             try {
-                final PublicKey pubKey = PubkeyUtils.decodePublic(privateKey, type);
+                final PublicKey pubKey = PubkeyUtils.decodePublic(publicKey, type);
 
                 if (PubkeyDatabase.KEY_TYPE_RSA.equals(type)) {
                     int bits = ((RSAPublicKey) pubKey).getModulus().bitLength();
--- a/src/com/five_ten_sg/connectbot/service/AuthAgentService.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/service/AuthAgentService.java	Thu Jul 31 16:33:38 2014 -0700
@@ -29,9 +29,9 @@
 import android.util.Log;
 
 import com.madgag.ssh.android.authagent.AndroidAuthAgent;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
-import com.trilead.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
 
 public class AuthAgentService extends Service {
     private static final String TAG = "ConnectBot.AuthAgentService";
@@ -90,6 +90,9 @@
             else if (privKey instanceof DSAPrivateKey) {
                 return sshEncodedSignatureFor(data, (DSAPrivateKey) privKey);
             }
+            else if (privKey instanceof ECPrivateKey) {
+                return sshEncodedSignatureFor(data, (ECPrivateKey) privKey);
+            }
 
             return null;
         }
@@ -136,6 +139,10 @@
                     DSAPublicKey pubkey = (DSAPublicKey)pair.getPublic();
                     return DSASHA1Verify.encodeSSHDSAPublicKey(pubkey);
                 }
+                else if (privKey instanceof ECPrivateKey) {
+                    ECPublicKey pubkey = (ECPublicKey) pair.getPublic();
+                    return ECDSASHA2Verify.encodeSSHECDSAPublicKey(pubkey);
+                }
             }
             catch (IOException e) {
                 Log.e(TAG, "Couldn't encode " + pair, e);
@@ -161,6 +168,15 @@
                 throw new RuntimeException(e);
             }
         }
+        private byte[] sshEncodedSignatureFor(byte[] data, ECPrivateKey privKey) {
+            try {
+                byte[] signature = ECDSASHA2Verify.generateSignature(data, privKey);
+                return ECDSASHA2Verify.encodeSSHECDSASignature(signature, privKey.getParams());
+            }
+            catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
         private KeyPair keyPairFor(byte[] publicKey) {
             String nickname = manager.getKeyNickname(publicKey);
 
--- a/src/com/five_ten_sg/connectbot/service/TerminalBridge.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/service/TerminalBridge.java	Thu Jul 31 16:33:38 2014 -0700
@@ -332,7 +332,6 @@
 
         // get proper font size
         setMyFontSize();
-
         // finally send any post-login string, if requested
         injectString(host.getPostLogin());
     }
@@ -445,6 +444,7 @@
      */
     final void setFontSize(float size) {
         if (size <= 0.0) size = 12.0f;
+
         size = (float)(int)((size * 10.0f) + 0.5f) / 10.0f;
         defaultPaint.setTextSize(size);
         fontSize = size;
@@ -782,6 +782,7 @@
         this.columns = cols;
         this.rows = rows;
         forcedSize = true;
+
         if (fixed) setFontSize(host.getFontSize());
         else       setFontSize(size);
     }
@@ -1136,7 +1137,7 @@
      */
     public boolean showArrowsDialog() {
         final String []pickerStrings = {"←", "→", "↑", "↓", "tab", "ins", "del", "ret"};
-        final HashMap<String,Integer> keymap = new HashMap<String,Integer>();
+        final HashMap<String, Integer> keymap = new HashMap<String, Integer>();
         keymap.put("←",   vt320.KEY_LEFT);
         keymap.put("→",   vt320.KEY_RIGHT);
         keymap.put("↑",   vt320.KEY_UP);
@@ -1145,15 +1146,14 @@
         keymap.put("ins", vt320.KEY_INSERT);
         keymap.put("del", vt320.KEY_DELETE);
         keymap.put("ret", vt320.KEY_ENTER);
-
         CharSequence str = "";
         Editable content = Editable.Factory.getInstance().newEditable(str);
 
         if (parent == null) return false;
 
         StringPickerDialog cpd = new StringPickerDialog(parent.getContext(),
-                                                        parent, content,
-                                                        pickerStrings, true) {
+                parent, content,
+        pickerStrings, true) {
             private void buttonPressed(String s) {
                 if (keymap.containsKey(s)) buffer.keyPressed(keymap.get(s), ' ', 0);
             }
@@ -1165,6 +1165,7 @@
             public void onClick(View v) {
                 if (v instanceof Button) {
                     final String s = ((Button) v).getText().toString();
+
                     if (s.equals("")) dismiss();
                     else buttonPressed(s);
                 }
@@ -1260,7 +1261,7 @@
      */
     public boolean showFKeysDialog() {
         final String []pickerStrings = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", "←", "→", "↑", "↓", "tab", "ins", "del", "ret"};
-        final HashMap<String,Integer> keymap = new HashMap<String,Integer>();
+        final HashMap<String, Integer> keymap = new HashMap<String, Integer>();
         keymap.put("F1", vt320.KEY_F1);
         keymap.put("F2", vt320.KEY_F2);
         keymap.put("F3", vt320.KEY_F3);
@@ -1293,17 +1294,17 @@
         keymap.put("ins", vt320.KEY_INSERT);
         keymap.put("del", vt320.KEY_DELETE);
         keymap.put("ret", vt320.KEY_ENTER);
-
         CharSequence str = "";
         Editable content = Editable.Factory.getInstance().newEditable(str);
 
         if (parent == null) return false;
 
         StringPickerDialog cpd = new StringPickerDialog(parent.getContext(),
-                                                        parent, content,
-                                                        pickerStrings, true) {
+                parent, content,
+        pickerStrings, true) {
             private void buttonPressed(String s) {
                 if (keymap.containsKey(s)) buffer.keyPressed(keymap.get(s), ' ', 0);
+
                 dismiss();
             }
             @Override
@@ -1314,6 +1315,7 @@
             public void onClick(View v) {
                 if (v instanceof Button) {
                     final String s = ((Button) v).getText().toString();
+
                     if (s.equals("")) dismiss();
                     else buttonPressed(s);
                 }
--- a/src/com/five_ten_sg/connectbot/service/TerminalKeyListener.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/service/TerminalKeyListener.java	Thu Jul 31 16:33:38 2014 -0700
@@ -569,40 +569,41 @@
 
     private boolean handleShortcuts(View v, int keyCode, KeyEvent event, int repeat, boolean down) {
         String hwbuttonShortcut;
+
         switch (keyCode) {
             case KeyEvent.KEYCODE_CAMERA:
                 // check to see which shortcut the camera button triggers
                 hwbuttonShortcut = manager.prefs.getString(
-                                        PreferenceConstants.CAMERA,
-                                        PreferenceConstants.HWBUTTON_SCREEN_CAPTURE);
+                                       PreferenceConstants.CAMERA,
+                                       PreferenceConstants.HWBUTTON_SCREEN_CAPTURE);
                 return (handleShortcut(v, hwbuttonShortcut, repeat, down));
 
             case KeyEvent.KEYCODE_VOLUME_UP:
                 // check to see which shortcut the volume button triggers
                 hwbuttonShortcut = manager.prefs.getString(
-                                        PreferenceConstants.VOLUP,
-                                        PreferenceConstants.HWBUTTON_FUNCTION_KEYS);
+                                       PreferenceConstants.VOLUP,
+                                       PreferenceConstants.HWBUTTON_FUNCTION_KEYS);
                 return (handleShortcut(v, hwbuttonShortcut, repeat, down));
 
             case KeyEvent.KEYCODE_VOLUME_DOWN:
                 // check to see which shortcut the camera button triggers
                 hwbuttonShortcut = manager.prefs.getString(
-                                        PreferenceConstants.VOLDN,
-                                        PreferenceConstants.HWBUTTON_TAB);
+                                       PreferenceConstants.VOLDN,
+                                       PreferenceConstants.HWBUTTON_TAB);
                 return (handleShortcut(v, hwbuttonShortcut, repeat, down));
 
             case KeyEvent.KEYCODE_SEARCH:
                 // check to see which shortcut the search button triggers
                 hwbuttonShortcut = manager.prefs.getString(
-                                        PreferenceConstants.SEARCH,
-                                        PreferenceConstants.HWBUTTON_ESC);
+                                       PreferenceConstants.SEARCH,
+                                       PreferenceConstants.HWBUTTON_ESC);
                 return (handleShortcut(v, hwbuttonShortcut, repeat, down));
 
             case KeyEvent.KEYCODE_BUTTON_L2:
                 // check to see which shortcut the ptt button triggers
                 hwbuttonShortcut = manager.prefs.getString(
-                                        PreferenceConstants.PTT,
-                                        PreferenceConstants.HWBUTTON_MONITOR);
+                                       PreferenceConstants.PTT,
+                                       PreferenceConstants.HWBUTTON_MONITOR);
                 return (handleShortcut(v, hwbuttonShortcut, repeat, down));
 
             default: return false;
@@ -612,51 +613,64 @@
     private boolean handleShortcut(View v, String shortcut, int repeat, boolean down) {
         if (PreferenceConstants.HWBUTTON_DECREASE_FONTSIZE.equals(shortcut)) {
             if (!down) return false;
+
             bridge.decreaseFontSize();
         }
         else if (PreferenceConstants.HWBUTTON_INCREASE_FONTSIZE.equals(shortcut)) {
             if (!down) return false;
+
             bridge.increaseFontSize();
         }
         else if (PreferenceConstants.HWBUTTON_FUNCTION_KEYS.equals(shortcut)) {
             if (repeat > 0) return false;
+
             if (!down) return false;
+
             bridge.showFKeysDialog();
         }
         else if (PreferenceConstants.HWBUTTON_MONITOR.equals(shortcut)) {
             if (repeat > 0) return false;
+
             buffer.monitorKey(down);
         }
         else if (PreferenceConstants.HWBUTTON_SCREEN_CAPTURE.equals(shortcut)) {
             if (repeat > 0) return false;
+
             if (!down) return false;
+
             bridge.captureScreen();
         }
         else if (PreferenceConstants.HWBUTTON_CTRL.equals(shortcut)) {
             if (!down) return false;
+
             showMetakeyToast(v, PreferenceConstants.HWBUTTON_CTRL);
             metaPress(META_CTRL_ON);
         }
         else if (PreferenceConstants.HWBUTTON_TAB.equals(shortcut)) {
             if (!down) return false;
+
             buffer.keyPressed(vt320.KEY_TAB, ' ',  getStateForBuffer());
         }
         else if (PreferenceConstants.HWBUTTON_CTRLA_SPACE.equals(shortcut)) {
             if (!down) return false;
+
             buffer.write(0x01);
             buffer.write(' ');
         }
         else if (PreferenceConstants.HWBUTTON_CTRLA.equals(shortcut)) {
             if (!down) return false;
+
             buffer.write(0x01);
         }
         else if (PreferenceConstants.HWBUTTON_ESC.equals(shortcut)) {
             if (!down) return false;
+
             showMetakeyToast(v, PreferenceConstants.HWBUTTON_ESC);
             sendEscape();
         }
         else if (PreferenceConstants.HWBUTTON_ESC_A.equals(shortcut)) {
             if (!down) return false;
+
             sendEscape();
             buffer.write('a');
         }
--- a/src/com/five_ten_sg/connectbot/service/TerminalMonitor.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/service/TerminalMonitor.java	Thu Jul 31 16:33:38 2014 -0700
@@ -378,6 +378,7 @@
 
     public synchronized void cursorMove(int l, int c) {
         if ((to_line != l) || (to_column != c)) moved = true;
+
         to_line = l;
         to_column = c;
     }
@@ -421,21 +422,23 @@
         int len = data.length - offset;
         char[] da = new char[len];
         System.arraycopy(data, offset, da, 0, len);
+
         if ((l > 60000) || (c > 60000)) {
             l = -1;
             c = -1;
         }
         else {
             // ignore setfield outside screen boundaries
-            if ((l >= buffer.height) || (c+len >= buffer.width)) return;
+            if ((l >= buffer.height) || (c + len >= buffer.width)) return;
         }
+
         buffer.setField(l, c, da);
     }
 
     public synchronized void showUrl(char [] data, int offset) {
         Log.i(TAG, "setField()");
         char[] da = new char[data.length - offset];
-        System.arraycopy(data, offset, da, 0, data.length-offset);
+        System.arraycopy(data, offset, da, 0, data.length - offset);
         String url = new String(da);
         Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
         parent.startActivity(intent);
@@ -447,12 +450,14 @@
         arg2[2] = (char)(l & 0x0000ffff);
         arg2[3] = (char)(c & 0x0000ffff);
         int base = 4;
-        if ((l >= buffer.height) || (c+len >= buffer.width)) {
-            Arrays.fill(arg2, base, len-1, ' ');
+
+        if ((l >= buffer.height) || (c + len >= buffer.width)) {
+            Arrays.fill(arg2, base, len - 1, ' ');
         }
         else {
             System.arraycopy(buffer.charArray[buffer.screenBase + l], c, arg2, base, len);
         }
+
         monitorWrite(MONITOR_CMD_FIELDVALUE, arg2);
     }
 
@@ -467,6 +472,7 @@
     public synchronized void depress(int vk_key) {
         Log.i(TAG, String.format("depress() %d", vk_key));
         Integer x = keymap.get(new Integer(vk_key));
+
         if (x != null) buffer.keyDepressed(x, ' ', 0);
     }
 
--- a/src/com/five_ten_sg/connectbot/transport/SSH.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/transport/SSH.java	Thu Jul 31 16:33:38 2014 -0700
@@ -31,6 +31,8 @@
 import java.security.PublicKey;
 import java.security.interfaces.DSAPrivateKey;
 import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.security.spec.InvalidKeySpecException;
@@ -59,23 +61,24 @@
 import android.os.Environment;
 import android.util.Log;
 
-import com.trilead.ssh2.AuthAgentCallback;
-import com.trilead.ssh2.ChannelCondition;
-import com.trilead.ssh2.Connection;
-import com.trilead.ssh2.ConnectionInfo;
-import com.trilead.ssh2.ConnectionMonitor;
-import com.trilead.ssh2.DynamicPortForwarder;
-import com.trilead.ssh2.InteractiveCallback;
-import com.trilead.ssh2.KnownHosts;
-import com.trilead.ssh2.LocalPortForwarder;
-import com.trilead.ssh2.SCPClient;
-import com.trilead.ssh2.ServerHostKeyVerifier;
-import com.trilead.ssh2.Session;
-import com.trilead.ssh2.HTTPProxyData;
-import com.trilead.ssh2.HTTPProxyException;
-import com.trilead.ssh2.crypto.PEMDecoder;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.AuthAgentCallback;
+import ch.ethz.ssh2.ChannelCondition;
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.ConnectionInfo;
+import ch.ethz.ssh2.ConnectionMonitor;
+import ch.ethz.ssh2.DynamicPortForwarder;
+import ch.ethz.ssh2.InteractiveCallback;
+import ch.ethz.ssh2.KnownHosts;
+import ch.ethz.ssh2.LocalPortForwarder;
+import ch.ethz.ssh2.SCPClient;
+import ch.ethz.ssh2.ServerHostKeyVerifier;
+import ch.ethz.ssh2.Session;
+import ch.ethz.ssh2.HTTPProxyData;
+import ch.ethz.ssh2.HTTPProxyException;
+import ch.ethz.ssh2.crypto.PEMDecoder;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
 
 /**
  * @author Kenny Root
@@ -144,7 +147,7 @@
                 algorithmName = "RSA";
             else if ("ssh-dss".equals(serverHostKeyAlgorithm))
                 algorithmName = "DSA";
-            else if (serverHostKeyAlgorithm.startsWith("ecdsa-"))
+            else if (serverHostKeyAlgorithm.startsWith("ecdsa-sha2-"))
                 algorithmName = "EC";
             else
                 algorithmName = serverHostKeyAlgorithm;
@@ -562,7 +565,9 @@
         catch (IOException e) {
             Log.e(TAG, "Problem in SSH connection thread during authentication", e);
             // Display the reason in the text.
-            bridge.outputLine(e.getCause().getMessage());
+            Throwable t = e.getCause();
+            String m = (t == null) ? e.getMessage() : t.getMessage();
+            bridge.outputLine(m);
             onDisconnect();
             return;
         }
@@ -1062,6 +1067,10 @@
                     DSAPublicKey pubkey = (DSAPublicKey) pair.getPublic();
                     pubKeys.put(entry.getKey(), DSASHA1Verify.encodeSSHDSAPublicKey(pubkey));
                 }
+                else if (privKey instanceof ECPrivateKey) {
+                    ECPublicKey pubkey = (ECPublicKey) pair.getPublic();
+                    pubKeys.put(entry.getKey(), ECDSASHA2Verify.encodeSSHECDSAPublicKey(pubkey));
+                }
                 else
                     continue;
             }
--- a/src/com/five_ten_sg/connectbot/transport/TN5250.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/transport/TN5250.java	Thu Jul 31 16:33:38 2014 -0700
@@ -163,6 +163,7 @@
         public void keyDepressed(int keyCode, char keyChar, int modifiers) {
             if (mnemonics.containsKey(keyCode)) {
                 String s = mnemonics.get(keyCode);
+
                 if (s != "") screen52.sendKeys(s);
             }
         }
@@ -177,17 +178,20 @@
         @Override
         public void write(byte[] b) {
             screen52.sendKeys(new String(b));
+
             if (bridge.monitor != null) bridge.monitor.testMoved();
         }
         @Override
         public void write(int b) {
             if (controls.containsKey(b)) keyPressed(controls.get(b), ' ', 0);
             else                         screen52.sendKeys(new String(new byte[] {(byte)b}));
+
             if (bridge.monitor != null) bridge.monitor.testMoved();
         }
         @Override
         public void keyPressed(int keyCode, char keyChar, int modifiers) {
             keyDepressed(keyCode, keyChar, modifiers);
+
             if (bridge.monitor != null) bridge.monitor.testMoved();
         }
 
@@ -281,6 +285,7 @@
         screen52.setBuffer(buffer);
         bridge.addFontSizeChangedListener(screen52);
         connected = handler.connect(host, homeDirectory, buffer);
+
         if (connected) bridge.onConnected();
     }
 
@@ -435,8 +440,8 @@
         if (nickname == null || nickname.length() == 0) {
             nickname = getDefaultNickname(host.getUsername(), host.getHostname(), host.getPort());
         }
+
         host.setNickname(nickname);
-
         return host;
     }
 
--- a/src/com/five_ten_sg/connectbot/util/HostDatabase.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/util/HostDatabase.java	Thu Jul 31 16:33:38 2014 -0700
@@ -33,7 +33,7 @@
 import android.database.sqlite.SQLiteException;
 import android.util.Log;
 
-import com.trilead.ssh2.KnownHosts;
+import ch.ethz.ssh2.KnownHosts;
 
 /**
  * Contains information about various SSH hosts, include public hostkey if known
--- a/src/com/five_ten_sg/connectbot/util/PubkeyUtils.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/util/PubkeyUtils.java	Thu Jul 31 16:33:38 2014 -0700
@@ -64,11 +64,11 @@
 import com.five_ten_sg.connectbot.bean.PubkeyBean;
 import android.util.Log;
 
-import com.trilead.ssh2.crypto.Base64;
-import com.trilead.ssh2.crypto.SimpleDERReader;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
-import com.trilead.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.crypto.Base64;
+import ch.ethz.ssh2.crypto.SimpleDERReader;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
 
 public class PubkeyUtils {
     private static final String TAG = "PubkeyUtils";
--- a/src/com/five_ten_sg/connectbot/util/StringPickerDialog.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/util/StringPickerDialog.java	Thu Jul 31 16:33:38 2014 -0700
@@ -38,7 +38,7 @@
  * Dialog for choosing accented characters related to a base character.
  */
 public class StringPickerDialog extends Dialog
-        implements OnItemClickListener, OnClickListener {
+    implements OnItemClickListener, OnClickListener {
     private View mView;
     private Editable mText;
     private String []mOptions;
@@ -52,10 +52,9 @@
      * the sense of <code>insert</code>) into <code>text</code>.
      */
     public StringPickerDialog(Context context, View view,
-                                 Editable text, String []options,
-                                 boolean insert) {
+                              Editable text, String []options,
+                              boolean insert) {
         //super(context, com.android.internal.R.style.Theme_Panel);
-
         //Resources res = Resources.getSystem();
         //int id = res.getIdentifier("Theme_Panel", "style", "android");
         //int id = android.R.style.Theme_Panel;
@@ -70,18 +69,14 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
         WindowManager.LayoutParams params = getWindow().getAttributes();
         params.token = mView.getApplicationWindowToken();
         params.type = params.TYPE_APPLICATION_ATTACHED_DIALOG;
         params.flags = params.flags | Window.FEATURE_NO_TITLE;
-
         setContentView(R.layout.string_picker);
-
         GridView grid = (GridView) findViewById(R.id.stringPicker);
         grid.setAdapter(new OptionsAdapter(getContext()));
         grid.setOnItemClickListener(this);
-
         mCancelButton = (Button) findViewById(R.id.cancel);
         mCancelButton.setOnClickListener(this);
     }
@@ -96,9 +91,11 @@
 
     private void replaceCharacterAndClose(CharSequence replace) {
         int selEnd = Selection.getSelectionEnd(mText);
+
         if (mInsert || selEnd == 0) {
             mText.insert(selEnd, replace);
-        } else {
+        }
+        else {
             mText.replace(selEnd - 1, selEnd, replace);
         }
 
@@ -111,7 +108,8 @@
     public void onClick(View v) {
         if (v == mCancelButton) {
             dismiss();
-        } else if (v instanceof Button) {
+        }
+        else if (v instanceof Button) {
             CharSequence result = ((Button) v).getText();
             replaceCharacterAndClose(result);
         }
@@ -125,7 +123,7 @@
 
         public View getView(int position, View convertView, ViewGroup parent) {
             Button b = (Button)
-                mInflater.inflate(R.layout.string_picker_button, null);
+                       mInflater.inflate(R.layout.string_picker_button, null);
             b.setText(mOptions[position]);
             b.setOnClickListener(StringPickerDialog.this);
             return b;
--- a/src/com/five_ten_sg/connectbot/util/XmlBuilder.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/util/XmlBuilder.java	Thu Jul 31 16:33:38 2014 -0700
@@ -17,7 +17,7 @@
 
 package com.five_ten_sg.connectbot.util;
 
-import com.trilead.ssh2.crypto.Base64;
+import ch.ethz.ssh2.crypto.Base64;
 
 /**
  * @author Kenny Root
--- a/src/com/trilead/ssh2/AuthAgentCallback.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-package com.trilead.ssh2;
-
-import java.security.KeyPair;
-import java.util.Map;
-
-/**
- * AuthAgentCallback.
- *
- * @author Kenny Root
- * @version $Id$
- */
-public interface AuthAgentCallback {
-
-    /**
-     * @return array of blobs containing the OpenSSH-format encoded public keys
-     */
-    Map<String, byte[]> retrieveIdentities();
-
-    /**
-     * @param key A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code>
-     *            containing a DSA or RSA private key of
-     *            the user in Trilead object format.
-     * @param comment comment associated with this key
-     * @param confirmUse whether to prompt before using this key
-     * @param lifetime lifetime in seconds for key to be remembered
-     * @return success or failure
-     */
-    boolean addIdentity(KeyPair pair, String comment, boolean confirmUse, int lifetime);
-
-    /**
-     * @param publicKey byte blob containing the OpenSSH-format encoded public key
-     * @return success or failure
-     */
-    boolean removeIdentity(byte[] publicKey);
-
-    /**
-     * @return success or failure
-     */
-    boolean removeAllIdentities();
-
-    /**
-     * @param publicKey byte blob containing the OpenSSH-format encoded public key
-     * @return A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code>
-     *         containing a DSA or RSA private key of
-     *         the user in Trilead object format.
-     */
-    KeyPair getKeyPair(byte[] publicKey);
-
-    /**
-     * @return
-     */
-    boolean isAgentLocked();
-
-    /**
-     * @param lockPassphrase
-     */
-    boolean setAgentLock(String lockPassphrase);
-
-    /**
-     * @param unlockPassphrase
-     * @return
-     */
-    boolean requestAgentUnlock(String unlockPassphrase);
-}
--- a/src/com/trilead/ssh2/ChannelCondition.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * Contains constants that can be used to specify what conditions to wait for on
- * a SSH-2 channel (e.g., represented by a {@link Session}).
- *
- * @see Session#waitForCondition(int, long)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ChannelCondition.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public abstract interface ChannelCondition {
-    /**
-     * A timeout has occurred, none of your requested conditions is fulfilled.
-     * However, other conditions may be true - therefore, NEVER use the "=="
-     * operator to test for this (or any other) condition. Always use
-     * something like <code>((cond & ChannelCondition.CLOSED) != 0)</code>.
-     */
-    public static final int TIMEOUT = 1;
-
-    /**
-     * The underlying SSH-2 channel, however not necessarily the whole connection,
-     * has been closed. This implies <code>EOF</code>. Note that there may still
-     * be unread stdout or stderr data in the local window, i.e, <code>STDOUT_DATA</code>
-     * or/and <code>STDERR_DATA</code> may be set at the same time.
-     */
-    public static final int CLOSED = 2;
-
-    /**
-     * There is stdout data available that is ready to be consumed.
-     */
-    public static final int STDOUT_DATA = 4;
-
-    /**
-     * There is stderr data available that is ready to be consumed.
-     */
-    public static final int STDERR_DATA = 8;
-
-    /**
-     * EOF on has been reached, no more _new_ stdout or stderr data will arrive
-     * from the remote server. However, there may be unread stdout or stderr
-     * data, i.e, <code>STDOUT_DATA</code> or/and <code>STDERR_DATA</code>
-     * may be set at the same time.
-     */
-    public static final int EOF = 16;
-
-    /**
-     * The exit status of the remote process is available.
-     * Some servers never send the exist status, or occasionally "forget" to do so.
-     */
-    public static final int EXIT_STATUS = 32;
-
-    /**
-     * The exit signal of the remote process is available.
-     */
-    public static final int EXIT_SIGNAL = 64;
-
-}
--- a/src/com/trilead/ssh2/Connection.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1585 +0,0 @@
-
-package com.trilead.ssh2;
-
-import java.io.CharArrayWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketTimeoutException;
-import java.security.KeyPair;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.util.Set;
-import java.util.Vector;
-
-import com.trilead.ssh2.auth.AuthenticationManager;
-import com.trilead.ssh2.channel.ChannelManager;
-import com.trilead.ssh2.crypto.CryptoWishList;
-import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
-import com.trilead.ssh2.crypto.digest.MAC;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.PacketIgnore;
-import com.trilead.ssh2.transport.KexManager;
-import com.trilead.ssh2.transport.TransportManager;
-import com.trilead.ssh2.util.TimeoutService;
-import com.trilead.ssh2.util.TimeoutService.TimeoutToken;
-
-/**
- * A <code>Connection</code> is used to establish an encrypted TCP/IP
- * connection to a SSH-2 server.
- * <p>
- * Typically, one
- * <ol>
- * <li>creates a {@link #Connection(String) Connection} object.</li>
- * <li>calls the {@link #connect() connect()} method.</li>
- * <li>calls some of the authentication methods (e.g.,
- * {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
- * <li>calls one or several times the {@link #openSession() openSession()}
- * method.</li>
- * <li>finally, one must close the connection and release resources with the
- * {@link #close() close()} method.</li>
- * </ol>
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Connection.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $
- */
-
-public class Connection {
-    /**
-     * The identifier presented to the SSH-2 server.
-     */
-    public final static String identification = "TrileadSSH2Java_213";
-
-    /**
-     * Will be used to generate all random data needed for the current
-     * connection. Note: SecureRandom.nextBytes() is thread safe.
-     */
-    private SecureRandom generator;
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @return The list of supported cipher algorithms by this implementation.
-     */
-
-    public static synchronized String[] getAvailableCiphers() {
-        return BlockCipherFactory.getDefaultCipherList();
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @return The list of supported MAC algorthims by this implementation.
-     */
-
-    public static synchronized String[] getAvailableMACs() {
-        return MAC.getMacList();
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @return The list of supported server host key algorthims by this
-     *         implementation.
-     */
-
-    public static synchronized String[] getAvailableServerHostKeyAlgorithms() {
-        return KexManager.getDefaultServerHostkeyAlgorithmList();
-    }
-
-    private AuthenticationManager am;
-
-    private boolean authenticated = false;
-    private boolean compression = false;
-    private ChannelManager cm;
-
-    private CryptoWishList cryptoWishList = new CryptoWishList();
-
-    private DHGexParameters dhgexpara = new DHGexParameters();
-
-    private final String hostname;
-
-    private final int port;
-
-    private TransportManager tm;
-
-    private boolean tcpNoDelay = false;
-
-    private ProxyData proxyData = null;
-
-    private Vector<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
-
-    /**
-     * Prepares a fresh <code>Connection</code> object which can then be used
-     * to establish a connection to the specified SSH-2 server.
-     * <p>
-     * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
-     *
-     * @param hostname
-     *            the hostname of the SSH-2 server.
-     */
-    public Connection(String hostname) {
-        this(hostname, 22);
-    }
-
-    /**
-     * Prepares a fresh <code>Connection</code> object which can then be used
-     * to establish a connection to the specified SSH-2 server.
-     *
-     * @param hostname
-     *            the host where we later want to connect to.
-     * @param port
-     *            port on the server, normally 22.
-     */
-    public Connection(String hostname, int port) {
-        this.hostname = hostname;
-        this.port = port;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * is based on DSA (it uses DSA to sign a challenge sent by the server).
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param pem
-     *            A <code>String</code> containing the DSA private key of the
-     *            user in OpenSSH key format (PEM, you can't miss the
-     *            "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
-     *            linefeeds.
-     * @param password
-     *            If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
-     *            must specify the password. Otherwise, this argument will be
-     *            ignored and can be set to <code>null</code>.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     *
-     * @deprecated You should use one of the
-     *             {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
-     *             methods, this method is just a wrapper for it and will
-     *             disappear in future builds.
-     *
-     */
-
-    public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        if (pem == null)
-            throw new IllegalArgumentException("pem argument is null");
-
-        authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());
-        return authenticated;
-    }
-
-    /**
-     * A wrapper that calls
-     * {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
-     * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod
-     * list.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param cb
-     *            An <code>InteractiveCallback</code> which will be used to
-     *            determine the responses to the questions asked by the server.
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-
-    public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
-    throws IOException {
-        return authenticateWithKeyboardInteractive(user, null, cb);
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * is based on "keyboard-interactive", specified in
-     * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
-     * callback object which will be feeded with challenges generated by the
-     * server. Answers are then sent back to the server. It is possible that the
-     * callback will be called several times during the invocation of this
-     * method (e.g., if the server replies to the callback's answer(s) with
-     * another challenge...)
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     * <p>
-     * Note: some SSH servers advertise "keyboard-interactive", however, any
-     * interactive request will be denied (without having sent any challenge to
-     * the client).
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param submethods
-     *            An array of submethod names, see
-     *            draft-ietf-secsh-auth-kbdinteract-XX. May be <code>null</code>
-     *            to indicate an empty list.
-     * @param cb
-     *            An <code>InteractiveCallback</code> which will be used to
-     *            determine the responses to the questions asked by the server.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-
-    public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
-            InteractiveCallback cb) throws IOException {
-        if (cb == null)
-            throw new IllegalArgumentException("Callback may not ne NULL!");
-
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        authenticated = am.authenticateInteractive(user, submethods, cb);
-        return authenticated;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * sends username and password to the server.
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     * <p>
-     * Note: if this method fails, then please double-check that it is actually
-     * offered by the server (use
-     * {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
-     * <p>
-     * Often, password authentication is disabled, but users are not aware of
-     * it. Many servers only offer "publickey" and "keyboard-interactive".
-     * However, even though "keyboard-interactive" *feels* like password
-     * authentication (e.g., when using the putty or openssh clients) it is
-     * *not* the same mechanism.
-     *
-     * @param user
-     * @param password
-     * @return if the connection is now authenticated.
-     * @throws IOException
-     */
-
-    public synchronized boolean authenticateWithPassword(String user, String password) throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        if (password == null)
-            throw new IllegalArgumentException("password argument is null");
-
-        authenticated = am.authenticatePassword(user, password);
-        return authenticated;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * can be used to explicitly use the special "none" authentication method
-     * (where only a username has to be specified).
-     * <p>
-     * Note 1: The "none" method may always be tried by clients, however as by
-     * the specs, the server will not explicitly announce it. In other words,
-     * the "none" token will never show up in the list returned by
-     * {@link #getRemainingAuthMethods(String)}.
-     * <p>
-     * Note 2: no matter which one of the authenticateWithXXX() methods you
-     * call, the library will always issue exactly one initial "none"
-     * authentication request to retrieve the initially allowed list of
-     * authentication methods by the server. Please read RFC 4252 for the
-     * details.
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If further authentication steps are needed, <code>false</code>
-     * is returned and one can retry by any other authentication method (use the
-     * <code>getRemainingAuthMethods</code> method to get a list of the
-     * remaining possible methods).
-     *
-     * @param user
-     * @return if the connection is now authenticated.
-     * @throws IOException
-     */
-
-    public synchronized boolean authenticateWithNone(String user) throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        /* Trigger the sending of the PacketUserauthRequestNone packet */
-        /* (if not already done) */
-        authenticated = am.authenticateNone(user);
-        return authenticated;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. The
-     * authentication method "publickey" works by signing a challenge sent by
-     * the server. The signature is either DSA or RSA based - it just depends on
-     * the type of private key you specify, either a DSA or RSA private key in
-     * PEM format. And yes, this is may seem to be a little confusing, the
-     * method is called "publickey" in the SSH-2 protocol specification, however
-     * since we need to generate a signature, you actually have to supply a
-     * private key =).
-     * <p>
-     * The private key contained in the PEM file may also be encrypted
-     * ("Proc-Type: 4,ENCRYPTED"). The library supports DES-CBC and DES-EDE3-CBC
-     * encryption, as well as the more exotic PEM encrpytions AES-128-CBC,
-     * AES-192-CBC and AES-256-CBC.
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     * <p>
-     * NOTE PUTTY USERS: Event though your key file may start with
-     * "-----BEGIN..." it is not in the expected format. You have to convert it
-     * to the OpenSSH key format by using the "puttygen" tool (can be downloaded
-     * from the Putty website). Simply load your key and then use the
-     * "Conversions/Export OpenSSH key" functionality to get a proper PEM file.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param pemPrivateKey
-     *            A <code>char[]</code> containing a DSA or RSA private key of
-     *            the user in OpenSSH key format (PEM, you can't miss the
-     *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE
-     *            KEY-----" tag). The char array may contain
-     *            linebreaks/linefeeds.
-     * @param password
-     *            If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED")
-     *            then you must specify a password. Otherwise, this argument
-     *            will be ignored and can be set to <code>null</code>.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-
-    public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
-    throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        if (pemPrivateKey == null)
-            throw new IllegalArgumentException("pemPrivateKey argument is null");
-
-        authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());
-        return authenticated;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. The
-     * authentication method "publickey" works by signing a challenge sent by
-     * the server. The signature is either DSA or RSA based - it just depends on
-     * the type of private key you specify, either a DSA or RSA private key in
-     * PEM format. And yes, this is may seem to be a little confusing, the
-     * method is called "publickey" in the SSH-2 protocol specification, however
-     * since we need to generate a signature, you actually have to supply a
-     * private key =).
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param key
-     *            A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code>
-     *            containing a DSA or RSA private key of
-     *            the user in Trilead object format.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-
-    public synchronized boolean authenticateWithPublicKey(String user, KeyPair pair)
-    throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        if (pair == null)
-            throw new IllegalArgumentException("Key pair argument is null");
-
-        authenticated = am.authenticatePublicKey(user, pair, getOrCreateSecureRND());
-        return authenticated;
-    }
-
-    /**
-     * A convenience wrapper function which reads in a private key (PEM format,
-     * either DSA or RSA) and then calls
-     * <code>authenticateWithPublicKey(String, char[], String)</code>.
-     * <p>
-     * NOTE PUTTY USERS: Event though your key file may start with
-     * "-----BEGIN..." it is not in the expected format. You have to convert it
-     * to the OpenSSH key format by using the "puttygen" tool (can be downloaded
-     * from the Putty website). Simply load your key and then use the
-     * "Conversions/Export OpenSSH key" functionality to get a proper PEM file.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param pemFile
-     *            A <code>File</code> object pointing to a file containing a
-     *            DSA or RSA private key of the user in OpenSSH key format (PEM,
-     *            you can't miss the "-----BEGIN DSA PRIVATE KEY-----" or
-     *            "-----BEGIN RSA PRIVATE KEY-----" tag).
-     * @param password
-     *            If the PEM file is encrypted then you must specify the
-     *            password. Otherwise, this argument will be ignored and can be
-     *            set to <code>null</code>.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-
-    public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
-    throws IOException {
-        if (pemFile == null)
-            throw new IllegalArgumentException("pemFile argument is null");
-
-        char[] buff = new char[256];
-        CharArrayWriter cw = new CharArrayWriter();
-        FileReader fr = new FileReader(pemFile);
-
-        while (true) {
-            int len = fr.read(buff);
-
-            if (len < 0)
-                break;
-
-            cw.write(buff, 0, len);
-        }
-
-        fr.close();
-        return authenticateWithPublicKey(user, cw.toCharArray(), password);
-    }
-
-    /**
-     * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any
-     * time, but it is best to add connection monitors before invoking
-     * <code>connect()</code> to avoid glitches (e.g., you add a connection
-     * monitor after a successful connect(), but the connection has died in the
-     * mean time. Then, your connection monitor won't be notified.)
-     * <p>
-     * You can add as many monitors as you like.
-     *
-     * @see ConnectionMonitor
-     *
-     * @param cmon
-     *            An object implementing the <code>ConnectionMonitor</code>
-     *            interface.
-     */
-
-    public synchronized void addConnectionMonitor(ConnectionMonitor cmon) {
-        if (cmon == null)
-            throw new IllegalArgumentException("cmon argument is null");
-
-        connectionMonitors.addElement(cmon);
-
-        if (tm != null)
-            tm.setConnectionMonitors(connectionMonitors);
-    }
-
-    /**
-     * Controls whether compression is used on the link or not.
-     * <p>
-     * Note: This can only be called before connect()
-     * @param enabled whether to enable compression
-     * @throws IOException
-     */
-
-    public synchronized void setCompression(boolean enabled) throws IOException {
-        if (tm != null)
-            throw new IOException("Connection to " + hostname + " is already in connected state!");
-
-        compression = enabled;
-    }
-
-    /**
-     * Close the connection to the SSH-2 server. All assigned sessions will be
-     * closed, too. Can be called at any time. Don't forget to call this once
-     * you don't need a connection anymore - otherwise the receiver thread may
-     * run forever.
-     */
-
-    public synchronized void close() {
-        Throwable t = new Throwable("Closed due to user request.");
-        close(t, false);
-    }
-
-    private void close(Throwable t, boolean hard) {
-        if (cm != null)
-            cm.closeAllChannels();
-
-        if (tm != null) {
-            tm.close(t, hard == false);
-            tm = null;
-        }
-
-        am = null;
-        cm = null;
-        authenticated = false;
-    }
-
-    /**
-     * Same as
-     * {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
-     *
-     * @return see comments for the
-     *         {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
-     *         method.
-     * @throws IOException
-     */
-
-    public synchronized ConnectionInfo connect() throws IOException {
-        return connect(null, 0, 0);
-    }
-
-    /**
-     * Same as
-     * {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
-     *
-     * @return see comments for the
-     *         {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
-     *         method.
-     * @throws IOException
-     */
-
-    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException {
-        return connect(verifier, 0, 0);
-    }
-
-    /**
-     * Connect to the SSH-2 server and, as soon as the server has presented its
-     * host key, use the
-     * {@link ServerHostKeyVerifier#verifyServerHostKey(String, int, String,
-     * byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method of the
-     * <code>verifier</code> to ask for permission to proceed. If
-     * <code>verifier</code> is <code>null</code>, then any host key will
-     * be accepted - this is NOT recommended, since it makes man-in-the-middle
-     * attackes VERY easy (somebody could put a proxy SSH server between you and
-     * the real server).
-     * <p>
-     * Note: The verifier will be called before doing any crypto calculations
-     * (i.e., diffie-hellman). Therefore, if you don't like the presented host
-     * key then no CPU cycles are wasted (and the evil server has less
-     * information about us).
-     * <p>
-     * However, it is still possible that the server presented a fake host key:
-     * the server cheated (typically a sign for a man-in-the-middle attack) and
-     * is not able to generate a signature that matches its host key. Don't
-     * worry, the library will detect such a scenario later when checking the
-     * signature (the signature cannot be checked before having completed the
-     * diffie-hellman exchange).
-     * <p>
-     * Note 2: The {@link ServerHostKeyVerifier#verifyServerHostKey(String, int,
-     * String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method will
-     * *NOT* be called from the current thread, the call is being made from a
-     * background thread (there is a background dispatcher thread for every
-     * established connection).
-     * <p>
-     * Note 3: This method will block as long as the key exchange of the
-     * underlying connection has not been completed (and you have not specified
-     * any timeouts).
-     * <p>
-     * Note 4: If you want to re-use a connection object that was successfully
-     * connected, then you must call the {@link #close()} method before invoking
-     * <code>connect()</code> again.
-     *
-     * @param verifier
-     *            An object that implements the {@link ServerHostKeyVerifier}
-     *            interface. Pass <code>null</code> to accept any server host
-     *            key - NOT recommended.
-     *
-     * @param connectTimeout
-     *            Connect the underlying TCP socket to the server with the given
-     *            timeout value (non-negative, in milliseconds). Zero means no
-     *            timeout. If a proxy is being used (see
-     *            {@link #setProxyData(ProxyData)}), then this timeout is used
-     *            for the connection establishment to the proxy.
-     *
-     * @param kexTimeout
-     *            Timeout for complete connection establishment (non-negative,
-     *            in milliseconds). Zero means no timeout. The timeout counts
-     *            from the moment you invoke the connect() method and is
-     *            cancelled as soon as the first key-exchange round has
-     *            finished. It is possible that the timeout event will be fired
-     *            during the invocation of the <code>verifier</code> callback,
-     *            but it will only have an effect after the
-     *            <code>verifier</code> returns.
-     *
-     * @return A {@link ConnectionInfo} object containing the details of the
-     *         established connection.
-     *
-     * @throws IOException
-     *             If any problem occurs, e.g., the server's host key is not
-     *             accepted by the <code>verifier</code> or there is problem
-     *             during the initial crypto setup (e.g., the signature sent by
-     *             the server is wrong).
-     *             <p>
-     *             In case of a timeout (either connectTimeout or kexTimeout) a
-     *             SocketTimeoutException is thrown.
-     *             <p>
-     *             An exception may also be thrown if the connection was already
-     *             successfully connected (no matter if the connection broke in
-     *             the mean time) and you invoke <code>connect()</code> again
-     *             without having called {@link #close()} first.
-     *             <p>
-     *             If a HTTP proxy is being used and the proxy refuses the
-     *             connection, then a {@link HTTPProxyException} may be thrown,
-     *             which contains the details returned by the proxy. If the
-     *             proxy is buggy and does not return a proper HTTP response,
-     *             then a normal IOException is thrown instead.
-     */
-
-    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
-    throws IOException {
-        final class TimeoutState {
-            boolean isCancelled = false;
-            boolean timeoutSocketClosed = false;
-        }
-
-        if (tm != null)
-            throw new IOException("Connection to " + hostname + " is already in connected state!");
-
-        if (connectTimeout < 0)
-            throw new IllegalArgumentException("connectTimeout must be non-negative!");
-
-        if (kexTimeout < 0)
-            throw new IllegalArgumentException("kexTimeout must be non-negative!");
-
-        final TimeoutState state = new TimeoutState();
-        tm = new TransportManager(hostname, port);
-        tm.setConnectionMonitors(connectionMonitors);
-
-        // Don't offer compression if not requested
-        if (!compression) {
-            cryptoWishList.c2s_comp_algos = new String[] { "none" };
-            cryptoWishList.s2c_comp_algos = new String[] { "none" };
-        }
-
-        /*
-         * Make sure that the runnable below will observe the new value of "tm"
-         * and "state" (the runnable will be executed in a different thread,
-         * which may be already running, that is why we need a memory barrier
-         * here). See also the comment in Channel.java if you are interested in
-         * the details.
-         *
-         * OKOK, this is paranoid since adding the runnable to the todo list of
-         * the TimeoutService will ensure that all writes have been flushed
-         * before the Runnable reads anything (there is a synchronized block in
-         * TimeoutService.addTimeoutHandler).
-         */
-
-        synchronized (tm) {
-            /* We could actually synchronize on anything. */
-        }
-
-        try {
-            TimeoutToken token = null;
-
-            if (kexTimeout > 0) {
-                final Runnable timeoutHandler = new Runnable() {
-                    public void run() {
-                        synchronized (state) {
-                            if (state.isCancelled)
-                                return;
-
-                            state.timeoutSocketClosed = true;
-                            tm.close(new SocketTimeoutException("The connect timeout expired"), false);
-                        }
-                    }
-                };
-                long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
-                token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
-            }
-
-            try {
-                tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, getOrCreateSecureRND(), proxyData);
-            }
-            catch (SocketTimeoutException se) {
-                throw(SocketTimeoutException) new SocketTimeoutException(
-                    "The connect() operation on the socket timed out.").initCause(se);
-            }
-
-            tm.setTcpNoDelay(tcpNoDelay);
-            /* Wait until first KEX has finished */
-            ConnectionInfo ci = tm.getConnectionInfo(1);
-
-            /* Now try to cancel the timeout, if needed */
-
-            if (token != null) {
-                TimeoutService.cancelTimeoutHandler(token);
-
-                /* Were we too late? */
-
-                synchronized (state) {
-                    if (state.timeoutSocketClosed)
-                        throw new IOException("This exception will be replaced by the one below =)");
-
-                    /*
-                     * Just in case the "cancelTimeoutHandler" invocation came
-                     * just a little bit too late but the handler did not enter
-                     * the semaphore yet - we can still stop it.
-                     */
-                    state.isCancelled = true;
-                }
-            }
-
-            return ci;
-        }
-        catch (SocketTimeoutException ste) {
-            throw ste;
-        }
-        catch (IOException e1) {
-            /* This will also invoke any registered connection monitors */
-            close(new Throwable("There was a problem during connect."), false);
-
-            synchronized (state) {
-                /*
-                 * Show a clean exception, not something like "the socket is
-                 * closed!?!"
-                 */
-                if (state.timeoutSocketClosed)
-                    throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
-            }
-
-            /* Do not wrap a HTTPProxyException */
-            if (e1 instanceof HTTPProxyException)
-                throw e1;
-
-            throw(IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port)
-            .initCause(e1);
-        }
-    }
-
-    /**
-     * Creates a new {@link LocalPortForwarder}. A
-     * <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive
-     * at a local port via the secure tunnel to another host (which may or may
-     * not be identical to the remote SSH-2 server).
-     * <p>
-     * This method must only be called after one has passed successfully the
-     * authentication step. There is no limit on the number of concurrent
-     * forwardings.
-     *
-     * @param local_port
-     *            the local port the LocalPortForwarder shall bind to.
-     * @param host_to_connect
-     *            target address (IP or hostname)
-     * @param port_to_connect
-     *            target port
-     * @return A {@link LocalPortForwarder} object.
-     * @throws IOException
-     */
-
-    public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
-            int port_to_connect) throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
-        return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
-    }
-
-    /**
-     * Creates a new {@link LocalPortForwarder}. A
-     * <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive
-     * at a local port via the secure tunnel to another host (which may or may
-     * not be identical to the remote SSH-2 server).
-     * <p>
-     * This method must only be called after one has passed successfully the
-     * authentication step. There is no limit on the number of concurrent
-     * forwardings.
-     *
-     * @param addr
-     *            specifies the InetSocketAddress where the local socket shall
-     *            be bound to.
-     * @param host_to_connect
-     *            target address (IP or hostname)
-     * @param port_to_connect
-     *            target port
-     * @return A {@link LocalPortForwarder} object.
-     * @throws IOException
-     */
-
-    public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect,
-            int port_to_connect) throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
-        return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect);
-    }
-
-    /**
-     * Creates a new {@link LocalStreamForwarder}. A
-     * <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
-     * that is being forwarded via the secure tunnel into a TCP/IP connection to
-     * another host (which may or may not be identical to the remote SSH-2
-     * server).
-     *
-     * @param host_to_connect
-     * @param port_to_connect
-     * @return A {@link LocalStreamForwarder} object.
-     * @throws IOException
-     */
-
-    public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
-    throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward, connection is not authenticated.");
-
-        return new LocalStreamForwarder(cm, host_to_connect, port_to_connect);
-    }
-
-    /**
-     * Creates a new {@link DynamicPortForwarder}. A
-     * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
-     * at a local port via the secure tunnel to another host that is chosen via
-     * the SOCKS protocol.
-     * <p>
-     * This method must only be called after one has passed successfully the
-     * authentication step. There is no limit on the number of concurrent
-     * forwardings.
-     *
-     * @param local_port
-     * @return A {@link DynamicPortForwarder} object.
-     * @throws IOException
-     */
-
-    public synchronized DynamicPortForwarder createDynamicPortForwarder(int local_port) throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
-        return new DynamicPortForwarder(cm, local_port);
-    }
-
-    /**
-     * Creates a new {@link DynamicPortForwarder}. A
-     * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
-     * at a local port via the secure tunnel to another host that is chosen via
-     * the SOCKS protocol.
-     * <p>
-     * This method must only be called after one has passed successfully the
-     * authentication step. There is no limit on the number of concurrent
-     * forwardings.
-     *
-     * @param addr
-     *            specifies the InetSocketAddress where the local socket shall
-     *            be bound to.
-     * @return A {@link DynamicPortForwarder} object.
-     * @throws IOException
-     */
-
-    public synchronized DynamicPortForwarder createDynamicPortForwarder(InetSocketAddress addr) throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
-        return new DynamicPortForwarder(cm, addr);
-    }
-
-    /**
-     * Create a very basic {@link SCPClient} that can be used to copy files
-     * from/to the SSH-2 server.
-     * <p>
-     * Works only after one has passed successfully the authentication step.
-     * There is no limit on the number of concurrent SCP clients.
-     * <p>
-     * Note: This factory method will probably disappear in the future.
-     *
-     * @return A {@link SCPClient} object.
-     * @throws IOException
-     */
-
-    public synchronized SCPClient createSCPClient() throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Cannot create SCP client, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot create SCP client, connection is not authenticated.");
-
-        return new SCPClient(this);
-    }
-
-    /**
-     * Force an asynchronous key re-exchange (the call does not block). The
-     * latest values set for MAC, Cipher and DH group exchange parameters will
-     * be used. If a key exchange is currently in progress, then this method has
-     * the only effect that the so far specified parameters will be used for the
-     * next (server driven) key exchange.
-     * <p>
-     * Note: This implementation will never start a key exchange (other than the
-     * initial one) unless you or the SSH-2 server ask for it.
-     *
-     * @throws IOException
-     *             In case of any failure behind the scenes.
-     */
-
-    public synchronized void forceKeyExchange() throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("You need to establish a connection first.");
-
-        tm.forceKeyExchange(cryptoWishList, dhgexpara);
-    }
-
-    /**
-     * Returns the hostname that was passed to the constructor.
-     *
-     * @return the hostname
-     */
-
-    public synchronized String getHostname() {
-        return hostname;
-    }
-
-    /**
-     * Returns the port that was passed to the constructor.
-     *
-     * @return the TCP port
-     */
-
-    public synchronized int getPort() {
-        return port;
-    }
-
-    /**
-     * Returns a {@link ConnectionInfo} object containing the details of the
-     * connection. Can be called as soon as the connection has been established
-     * (successfully connected).
-     *
-     * @return A {@link ConnectionInfo} object.
-     * @throws IOException
-     *             In case of any failure behind the scenes.
-     */
-
-    public synchronized ConnectionInfo getConnectionInfo() throws IOException {
-        if (tm == null)
-            throw new IllegalStateException(
-                "Cannot get details of connection, you need to establish a connection first.");
-
-        return tm.getConnectionInfo(1);
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * can be used to tell which authentication methods are supported by the
-     * server at a certain stage of the authentication process (for the given
-     * username).
-     * <p>
-     * Note 1: the username will only be used if no authentication step was done
-     * so far (it will be used to ask the server for a list of possible
-     * authentication methods by sending the initial "none" request). Otherwise,
-     * this method ignores the user name and returns a cached method list (which
-     * is based on the information contained in the last negative server
-     * response).
-     * <p>
-     * Note 2: the server may return method names that are not supported by this
-     * implementation.
-     * <p>
-     * After a successful authentication, this method must not be called
-     * anymore.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     *
-     * @return a (possibly emtpy) array holding authentication method names.
-     * @throws IOException
-     */
-
-    public synchronized String[] getRemainingAuthMethods(String user) throws IOException {
-        if (user == null)
-            throw new IllegalArgumentException("user argument may not be NULL!");
-
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        return am.getRemainingMethods(user);
-    }
-
-    /**
-     * Determines if the authentication phase is complete. Can be called at any
-     * time.
-     *
-     * @return <code>true</code> if no further authentication steps are
-     *         needed.
-     */
-
-    public synchronized boolean isAuthenticationComplete() {
-        return authenticated;
-    }
-
-    /**
-     * Returns true if there was at least one failed authentication request and
-     * the last failed authentication request was marked with "partial success"
-     * by the server. This is only needed in the rare case of SSH-2 server
-     * setups that cannot be satisfied with a single successful authentication
-     * request (i.e., multiple authentication steps are needed.)
-     * <p>
-     * If you are interested in the details, then have a look at RFC4252.
-     *
-     * @return if the there was a failed authentication step and the last one
-     *         was marked as a "partial success".
-     */
-
-    public synchronized boolean isAuthenticationPartialSuccess() {
-        if (am == null)
-            return false;
-
-        return am.getPartialSuccess();
-    }
-
-    /**
-     * Checks if a specified authentication method is available. This method is
-     * actually just a wrapper for {@link #getRemainingAuthMethods(String)
-     * getRemainingAuthMethods()}.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param method
-     *            An authentication method name (e.g., "publickey", "password",
-     *            "keyboard-interactive") as specified by the SSH-2 standard.
-     * @return if the specified authentication method is currently available.
-     * @throws IOException
-     */
-
-    public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException {
-        if (method == null)
-            throw new IllegalArgumentException("method argument may not be NULL!");
-
-        String methods[] = getRemainingAuthMethods(user);
-
-        for (int i = 0; i < methods.length; i++) {
-            if (methods[i].compareTo(method) == 0)
-                return true;
-        }
-
-        return false;
-    }
-
-    private final SecureRandom getOrCreateSecureRND() {
-        if (generator == null)
-            generator = new SecureRandom();
-
-        return generator;
-    }
-
-    /**
-     * Open a new {@link Session} on this connection. Works only after one has
-     * passed successfully the authentication step. There is no limit on the
-     * number of concurrent sessions.
-     *
-     * @return A {@link Session} object.
-     * @throws IOException
-     */
-
-    public synchronized Session openSession() throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("Cannot open session, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot open session, connection is not authenticated.");
-
-        return new Session(cm, getOrCreateSecureRND());
-    }
-
-    /**
-     * Send an SSH_MSG_IGNORE packet. This method will generate a random data
-     * attribute (length between 0 (invlusive) and 16 (exclusive) bytes,
-     * contents are random bytes).
-     * <p>
-     * This method must only be called once the connection is established.
-     *
-     * @throws IOException
-     */
-
-    public synchronized void sendIgnorePacket() throws IOException {
-        SecureRandom rnd = getOrCreateSecureRND();
-        byte[] data = new byte[rnd.nextInt(16)];
-        rnd.nextBytes(data);
-        sendIgnorePacket(data);
-    }
-
-    /**
-     * Send an SSH_MSG_IGNORE packet with the given data attribute.
-     * <p>
-     * This method must only be called once the connection is established.
-     *
-     * @throws IOException
-     */
-
-    public synchronized void sendIgnorePacket(byte[] data) throws IOException {
-        if (data == null)
-            throw new IllegalArgumentException("data argument must not be null.");
-
-        if (tm == null)
-            throw new IllegalStateException(
-                "Cannot send SSH_MSG_IGNORE packet, you need to establish a connection first.");
-
-        PacketIgnore pi = new PacketIgnore();
-        pi.setData(data);
-        tm.sendMessage(pi.getPayload());
-    }
-
-    /**
-     * Removes duplicates from a String array, keeps only first occurence of
-     * each element. Does not destroy order of elements; can handle nulls. Uses
-     * a very efficient O(N^2) algorithm =)
-     *
-     * @param list
-     *            a String array.
-     * @return a cleaned String array.
-     */
-    private String[] removeDuplicates(String[] list) {
-        if ((list == null) || (list.length < 2))
-            return list;
-
-        String[] list2 = new String[list.length];
-        int count = 0;
-
-        for (int i = 0; i < list.length; i++) {
-            boolean duplicate = false;
-            String element = list[i];
-
-            for (int j = 0; j < count; j++) {
-                if (((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j])))) {
-                    duplicate = true;
-                    break;
-                }
-            }
-
-            if (duplicate)
-                continue;
-
-            list2[count++] = list[i];
-        }
-
-        if (count == list2.length)
-            return list2;
-
-        String[] tmp = new String[count];
-        System.arraycopy(list2, 0, tmp, 0, count);
-        return tmp;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param ciphers
-     */
-
-    public synchronized void setClient2ServerCiphers(String[] ciphers) {
-        if ((ciphers == null) || (ciphers.length == 0))
-            throw new IllegalArgumentException();
-
-        ciphers = removeDuplicates(ciphers);
-        BlockCipherFactory.checkCipherList(ciphers);
-        cryptoWishList.c2s_enc_algos = ciphers;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param macs
-     */
-
-    public synchronized void setClient2ServerMACs(String[] macs) {
-        if ((macs == null) || (macs.length == 0))
-            throw new IllegalArgumentException();
-
-        macs = removeDuplicates(macs);
-        MAC.checkMacList(macs);
-        cryptoWishList.c2s_mac_algos = macs;
-    }
-
-    /**
-     * Sets the parameters for the diffie-hellman group exchange. Unless you
-     * know what you are doing, you will never need this. Default values are
-     * defined in the {@link DHGexParameters} class.
-     *
-     * @param dgp
-     *            {@link DHGexParameters}, non null.
-     *
-     */
-
-    public synchronized void setDHGexParameters(DHGexParameters dgp) {
-        if (dgp == null)
-            throw new IllegalArgumentException();
-
-        dhgexpara = dgp;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param ciphers
-     */
-
-    public synchronized void setServer2ClientCiphers(String[] ciphers) {
-        if ((ciphers == null) || (ciphers.length == 0))
-            throw new IllegalArgumentException();
-
-        ciphers = removeDuplicates(ciphers);
-        BlockCipherFactory.checkCipherList(ciphers);
-        cryptoWishList.s2c_enc_algos = ciphers;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param macs
-     */
-
-    public synchronized void setServer2ClientMACs(String[] macs) {
-        if ((macs == null) || (macs.length == 0))
-            throw new IllegalArgumentException();
-
-        macs = removeDuplicates(macs);
-        MAC.checkMacList(macs);
-        cryptoWishList.s2c_mac_algos = macs;
-    }
-
-    /**
-     * Define the set of allowed server host key algorithms to be used for the
-     * following key exchange operations.
-     * <p>
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param algos
-     *            An array of allowed server host key algorithms. SSH-2 defines
-     *            <code>ssh-dss</code> and <code>ssh-rsa</code>. The
-     *            entries of the array must be ordered after preference, i.e.,
-     *            the entry at index 0 is the most preferred one. You must
-     *            specify at least one entry.
-     */
-
-    public synchronized void setServerHostKeyAlgorithms(String[] algos) {
-        if ((algos == null) || (algos.length == 0))
-            throw new IllegalArgumentException();
-
-        algos = removeDuplicates(algos);
-        KexManager.checkServerHostkeyAlgorithmsList(algos);
-        cryptoWishList.serverHostKeyAlgorithms = algos;
-    }
-
-    /**
-     * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the
-     * underlying socket.
-     * <p>
-     * Can be called at any time. If the connection has not yet been established
-     * then the passed value will be stored and set after the socket has been
-     * set up. The default value that will be used is <code>false</code>.
-     *
-     * @param enable
-     *            the argument passed to the <code>Socket.setTCPNoDelay()</code>
-     *            method.
-     * @throws IOException
-     */
-
-    public synchronized void setTCPNoDelay(boolean enable) throws IOException {
-        tcpNoDelay = enable;
-
-        if (tm != null)
-            tm.setTcpNoDelay(enable);
-    }
-
-    /**
-     * Used to tell the library that the connection shall be established through
-     * a proxy server. It only makes sense to call this method before calling
-     * the {@link #connect() connect()} method.
-     * <p>
-     * At the moment, only HTTP proxies are supported.
-     * <p>
-     * Note: This method can be called any number of times. The
-     * {@link #connect() connect()} method will use the value set in the last
-     * preceding invocation of this method.
-     *
-     * @see HTTPProxyData
-     *
-     * @param proxyData
-     *            Connection information about the proxy. If <code>null</code>,
-     *            then no proxy will be used (non surprisingly, this is also the
-     *            default).
-     */
-
-    public synchronized void setProxyData(ProxyData proxyData) {
-        this.proxyData = proxyData;
-    }
-
-    /**
-     * Request a remote port forwarding. If successful, then forwarded
-     * connections will be redirected to the given target address. You can
-     * cancle a requested remote port forwarding by calling
-     * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
-     * <p>
-     * A call of this method will block until the peer either agreed or
-     * disagreed to your request-
-     * <p>
-     * Note 1: this method typically fails if you
-     * <ul>
-     * <li>pass a port number for which the used remote user has not enough
-     * permissions (i.e., port &lt; 1024)</li>
-     * <li>or pass a port number that is already in use on the remote server</li>
-     * <li>or if remote port forwarding is disabled on the server.</li>
-     * </ul>
-     * <p>
-     * Note 2: (from the openssh man page): By default, the listening socket on
-     * the server will be bound to the loopback interface only. This may be
-     * overriden by specifying a bind address. Specifying a remote bind address
-     * will only succeed if the server's <b>GatewayPorts</b> option is enabled
-     * (see sshd_config(5)).
-     *
-     * @param bindAddress
-     *            address to bind to on the server:
-     *            <ul>
-     *            <li>"" means that connections are to be accepted on all
-     *            protocol families supported by the SSH implementation</li>
-     *            <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
-     *            <li>"::" means to listen on all IPv6 addresses</li>
-     *            <li>"localhost" means to listen on all protocol families
-     *            supported by the SSH implementation on loopback addresses
-     *            only, [RFC3330] and RFC3513]</li>
-     *            <li>"127.0.0.1" and "::1" indicate listening on the loopback
-     *            interfaces for IPv4 and IPv6 respectively</li>
-     *            </ul>
-     * @param bindPort
-     *            port number to bind on the server (must be &gt; 0)
-     * @param targetAddress
-     *            the target address (IP or hostname)
-     * @param targetPort
-     *            the target port
-     * @throws IOException
-     */
-
-    public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
-            int targetPort) throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("You need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("The connection is not authenticated.");
-
-        if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0))
-            throw new IllegalArgumentException();
-
-        cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
-    }
-
-    /**
-     * Cancel an earlier requested remote port forwarding. Currently active
-     * forwardings will not be affected (e.g., disrupted). Note that further
-     * connection forwarding requests may be received until this method has
-     * returned.
-     *
-     * @param bindPort
-     *            the allocated port number on the server
-     * @throws IOException
-     *             if the remote side refuses the cancel request or another low
-     *             level error occurs (e.g., the underlying connection is
-     *             closed)
-     */
-
-    public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("You need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("The connection is not authenticated.");
-
-        cm.requestCancelGlobalForward(bindPort);
-    }
-
-    /**
-     * Provide your own instance of SecureRandom. Can be used, e.g., if you want
-     * to seed the used SecureRandom generator manually.
-     * <p>
-     * The SecureRandom instance is used during key exchanges, public key
-     * authentication, x11 cookie generation and the like.
-     *
-     * @param rnd
-     *            a SecureRandom instance
-     */
-
-    public synchronized void setSecureRandom(SecureRandom rnd) {
-        if (rnd == null)
-            throw new IllegalArgumentException();
-
-        this.generator = rnd;
-    }
-
-    /**
-     * Enable/disable debug logging. <b>Only do this when requested by Trilead
-     * support.</b>
-     * <p>
-     * For speed reasons, some static variables used to check whether debugging
-     * is enabled are not protected with locks. In other words, if you
-     * dynamicaly enable/disable debug logging, then some threads may still use
-     * the old setting. To be on the safe side, enable debugging before doing
-     * the <code>connect()</code> call.
-     *
-     * @param enable
-     *            on/off
-     * @param logger
-     *            a {@link DebugLogger DebugLogger} instance, <code>null</code>
-     *            means logging using the simple logger which logs all messages
-     *            to to stderr. Ignored if enabled is <code>false</code>
-     */
-
-    public synchronized void enableDebugging(boolean enable, DebugLogger logger) {
-        Logger.enabled = enable;
-
-        if (enable == false) {
-            Logger.logger = null;
-        }
-        else {
-            if (logger == null) {
-                logger = new DebugLogger() {
-                    public void log(int level, String className, String message) {
-                        long now = System.currentTimeMillis();
-                        System.err.println(now + " : " + className + ": " + message);
-                    }
-                };
-            }
-
-            Logger.logger = logger;
-        }
-    }
-
-    /**
-     * This method can be used to perform end-to-end connection testing. It
-     * sends a 'ping' message to the server and waits for the 'pong' from the
-     * server.
-     * <p>
-     * When this method throws an exception, then you can assume that the
-     * connection should be abandoned.
-     * <p>
-     * Note: Works only after one has passed successfully the authentication
-     * step.
-     * <p>
-     * Implementation details: this method sends a SSH_MSG_GLOBAL_REQUEST
-     * request ('trilead-ping') to the server and waits for the
-     * SSH_MSG_REQUEST_FAILURE reply packet from the server.
-     *
-     * @throws IOException
-     *             in case of any problem
-     */
-
-    public synchronized void ping() throws IOException {
-        if (tm == null)
-            throw new IllegalStateException("You need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("The connection is not authenticated.");
-
-        cm.requestGlobalTrileadPing();
-    }
-}
--- a/src/com/trilead/ssh2/ConnectionInfo.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * In most cases you probably do not need the information contained in here.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ConnectionInfo.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class ConnectionInfo {
-    /**
-     * The used key exchange (KEX) algorithm in the latest key exchange.
-     */
-    public String keyExchangeAlgorithm;
-
-    /**
-     * The currently used crypto algorithm for packets from to the client to the
-     * server.
-     */
-    public String clientToServerCryptoAlgorithm;
-    /**
-     * The currently used crypto algorithm for packets from to the server to the
-     * client.
-     */
-    public String serverToClientCryptoAlgorithm;
-
-    /**
-     * The currently used MAC algorithm for packets from to the client to the
-     * server.
-     */
-    public String clientToServerMACAlgorithm;
-    /**
-     * The currently used MAC algorithm for packets from to the server to the
-     * client.
-     */
-    public String serverToClientMACAlgorithm;
-
-    /**
-     * The type of the server host key (currently either "ssh-dss" or
-     * "ssh-rsa").
-     */
-    public String serverHostKeyAlgorithm;
-    /**
-     * The server host key that was sent during the latest key exchange.
-     */
-    public byte[] serverHostKey;
-
-    /**
-     * Number of kex exchanges performed on this connection so far.
-     */
-    public int keyExchangeCounter = 0;
-}
--- a/src/com/trilead/ssh2/ConnectionMonitor.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>ConnectionMonitor</code> is used to get notified when the
- * underlying socket of a connection is closed.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ConnectionMonitor.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public interface ConnectionMonitor {
-    /**
-     * This method is called after the connection's underlying
-     * socket has been closed. E.g., due to the {@link Connection#close()} request of the
-     * user, if the peer closed the connection, due to a fatal error during connect()
-     * (also if the socket cannot be established) or if a fatal error occured on
-     * an established connection.
-     * <p>
-     * This is an experimental feature.
-     * <p>
-     * You MUST NOT make any assumption about the thread that invokes this method.
-     * <p>
-     * <b>Please note: if the connection is not connected (e.g., there was no successful
-     * connect() call), then the invocation of {@link Connection#close()} will NOT trigger
-     * this method.</b>
-     *
-     * @see Connection#addConnectionMonitor(ConnectionMonitor)
-     *
-     * @param reason Includes an indication why the socket was closed.
-     */
-    public void connectionLost(Throwable reason);
-}
\ No newline at end of file
--- a/src/com/trilead/ssh2/DHGexParameters.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>DHGexParameters</code> object can be used to specify parameters for
- * the diffie-hellman group exchange.
- * <p>
- * Depending on which constructor is used, either the use of a
- * <code>SSH_MSG_KEX_DH_GEX_REQUEST</code> or <code>SSH_MSG_KEX_DH_GEX_REQUEST_OLD</code>
- * can be forced.
- *
- * @see Connection#setDHGexParameters(DHGexParameters)
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DHGexParameters.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class DHGexParameters {
-    private final int min_group_len;
-    private final int pref_group_len;
-    private final int max_group_len;
-
-    private static final int MIN_ALLOWED = 1024;
-    private static final int MAX_ALLOWED = 8192;
-
-    /**
-     * Same as calling {@link #DHGexParameters(int, int, int) DHGexParameters(1024, 1024, 4096)}.
-     * This is also the default used by the Connection class.
-     *
-     */
-    public DHGexParameters() {
-        this(1024, 1024, 4096);
-    }
-
-    /**
-     * This constructor can be used to force the sending of a
-     * <code>SSH_MSG_KEX_DH_GEX_REQUEST_OLD</code> request.
-     * Internally, the minimum and maximum group lengths will
-     * be set to zero.
-     *
-     * @param pref_group_len has to be &gt= 1024 and &lt;= 8192
-     */
-    public DHGexParameters(int pref_group_len) {
-        if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
-            throw new IllegalArgumentException("pref_group_len out of range!");
-
-        this.pref_group_len = pref_group_len;
-        this.min_group_len = 0;
-        this.max_group_len = 0;
-    }
-
-    /**
-     * This constructor can be used to force the sending of a
-     * <code>SSH_MSG_KEX_DH_GEX_REQUEST</code> request.
-     * <p>
-     * Note: older OpenSSH servers don't understand this request, in which
-     * case you should use the {@link #DHGexParameters(int)} constructor.
-     * <p>
-     * All values have to be &gt= 1024 and &lt;= 8192. Furthermore,
-     * min_group_len &lt;= pref_group_len &lt;= max_group_len.
-     *
-     * @param min_group_len
-     * @param pref_group_len
-     * @param max_group_len
-     */
-    public DHGexParameters(int min_group_len, int pref_group_len, int max_group_len) {
-        if ((min_group_len < MIN_ALLOWED) || (min_group_len > MAX_ALLOWED))
-            throw new IllegalArgumentException("min_group_len out of range!");
-
-        if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
-            throw new IllegalArgumentException("pref_group_len out of range!");
-
-        if ((max_group_len < MIN_ALLOWED) || (max_group_len > MAX_ALLOWED))
-            throw new IllegalArgumentException("max_group_len out of range!");
-
-        if ((pref_group_len < min_group_len) || (pref_group_len > max_group_len))
-            throw new IllegalArgumentException("pref_group_len is incompatible with min and max!");
-
-        if (max_group_len < min_group_len)
-            throw new IllegalArgumentException("max_group_len must not be smaller than min_group_len!");
-
-        this.min_group_len = min_group_len;
-        this.pref_group_len = pref_group_len;
-        this.max_group_len = max_group_len;
-    }
-
-    /**
-     * Get the maximum group length.
-     *
-     * @return the maximum group length, may be <code>zero</code> if
-     *         SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested
-     */
-    public int getMax_group_len() {
-        return max_group_len;
-    }
-
-    /**
-     * Get the minimum group length.
-     *
-     * @return minimum group length, may be <code>zero</code> if
-     *         SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested
-     */
-    public int getMin_group_len() {
-        return min_group_len;
-    }
-
-    /**
-     * Get the preferred group length.
-     *
-     * @return the preferred group length
-     */
-    public int getPref_group_len() {
-        return pref_group_len;
-    }
-}
--- a/src/com/trilead/ssh2/DebugLogger.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-package com.trilead.ssh2;
-
-/**
- * An interface which needs to be implemented if you
- * want to capture debugging messages.
- *
- * @see Connection#enableDebugging(boolean, DebugLogger)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DebugLogger.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $
- */
-public interface DebugLogger {
-
-    /**
-     * Log a debug message.
-     *
-     * @param level 0-99, 99 is a the most verbose level
-     * @param className the class that generated the message
-     * @param message the debug message
-     */
-    public void log(int level, String className, String message);
-}
--- a/src/com/trilead/ssh2/DynamicPortForwarder.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * ConnectBot: simple, powerful, open-source SSH client for Android
- * Copyright 2007 Kenny Root, Jeffrey Sharkey
- *
- * Licensed 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 com.trilead.ssh2;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-
-import com.trilead.ssh2.channel.ChannelManager;
-import com.trilead.ssh2.channel.DynamicAcceptThread;
-
-/**
- * A <code>DynamicPortForwarder</code> forwards TCP/IP connections to a local
- * port via the secure tunnel to another host which is selected via the
- * SOCKS protocol. Checkout {@link Connection#createDynamicPortForwarder(int)}
- * on how to create one.
- *
- * @author Kenny Root
- * @version $Id: $
- */
-public class DynamicPortForwarder {
-    ChannelManager cm;
-
-    DynamicAcceptThread dat;
-
-    DynamicPortForwarder(ChannelManager cm, int local_port)
-    throws IOException {
-        this.cm = cm;
-        dat = new DynamicAcceptThread(cm, local_port);
-        dat.setDaemon(true);
-        dat.start();
-    }
-
-    DynamicPortForwarder(ChannelManager cm, InetSocketAddress addr) throws IOException {
-        this.cm = cm;
-        dat = new DynamicAcceptThread(cm, addr);
-        dat.setDaemon(true);
-        dat.start();
-    }
-
-    /**
-     * Stop TCP/IP forwarding of newly arriving connections.
-     *
-     * @throws IOException
-     */
-    public void close() throws IOException {
-        dat.stopWorking();
-    }
-}
--- a/src/com/trilead/ssh2/HTTPProxyData.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>HTTPProxyData</code> object is used to specify the needed connection data
- * to connect through a HTTP proxy.
- *
- * @see Connection#setProxyData(ProxyData)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: HTTPProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class HTTPProxyData implements ProxyData {
-    public final String proxyHost;
-    public final int proxyPort;
-    public final String proxyUser;
-    public final String proxyPass;
-    public final String[] requestHeaderLines;
-
-    /**
-     * Same as calling {@link #HTTPProxyData(String, int, String, String) HTTPProxyData(proxyHost, proxyPort, <code>null</code>, <code>null</code>)}
-     *
-     * @param proxyHost Proxy hostname.
-     * @param proxyPort Proxy port.
-     */
-    public HTTPProxyData(String proxyHost, int proxyPort) {
-        this(proxyHost, proxyPort, null, null);
-    }
-
-    /**
-     * Same as calling {@link #HTTPProxyData(String, int, String, String, String[]) HTTPProxyData(proxyHost, proxyPort, <code>null</code>, <code>null</code>, <code>null</code>)}
-     *
-     * @param proxyHost Proxy hostname.
-     * @param proxyPort Proxy port.
-     * @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
-     * @param proxyPass Password for basic authentication (<code>null</code> if no authentication is needed).
-     */
-    public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass) {
-        this(proxyHost, proxyPort, proxyUser, proxyPass, null);
-    }
-
-    /**
-     * Connection data for a HTTP proxy. It is possible to specify a username and password
-     * if the proxy requires basic authentication. Also, additional request header lines can
-     * be specified (e.g., "User-Agent: CERN-LineMode/2.15 libwww/2.17b3").
-     * <p>
-     * Please note: if you want to use basic authentication, then both <code>proxyUser</code>
-     * and <code>proxyPass</code> must be non-null.
-     * <p>
-     * Here is an example:
-     * <p>
-     * <code>
-     * new HTTPProxyData("192.168.1.1", "3128", "proxyuser", "secret", new String[] {"User-Agent: TrileadBasedClient/1.0", "X-My-Proxy-Option: something"});
-     * </code>
-     *
-     * @param proxyHost Proxy hostname.
-     * @param proxyPort Proxy port.
-     * @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
-     * @param proxyPass Password for basic authentication (<code>null</code> if no authentication is needed).
-     * @param requestHeaderLines An array with additional request header lines (without end-of-line markers)
-     *        that have to be sent to the server. May be <code>null</code>.
-     */
-
-    public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass,
-                         String[] requestHeaderLines) {
-        if (proxyHost == null)
-            throw new IllegalArgumentException("proxyHost must be non-null");
-
-        if (proxyPort < 0)
-            throw new IllegalArgumentException("proxyPort must be non-negative");
-
-        this.proxyHost = proxyHost;
-        this.proxyPort = proxyPort;
-        this.proxyUser = proxyUser;
-        this.proxyPass = proxyPass;
-        this.requestHeaderLines = requestHeaderLines;
-    }
-}
--- a/src/com/trilead/ssh2/HTTPProxyException.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-
-/**
- * May be thrown upon connect() if a HTTP proxy is being used.
- *
- * @see Connection#connect()
- * @see Connection#setProxyData(ProxyData)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: HTTPProxyException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class HTTPProxyException extends IOException {
-    private static final long serialVersionUID = 2241537397104426186L;
-
-    public final String httpResponse;
-    public final int httpErrorCode;
-
-    public HTTPProxyException(String httpResponse, int httpErrorCode) {
-        super("HTTP Proxy Error (" + httpErrorCode + " " + httpResponse + ")");
-        this.httpResponse = httpResponse;
-        this.httpErrorCode = httpErrorCode;
-    }
-}
--- a/src/com/trilead/ssh2/InteractiveCallback.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * An <code>InteractiveCallback</code> is used to respond to challenges sent
- * by the server if authentication mode "keyboard-interactive" is selected.
- *
- * @see Connection#authenticateWithKeyboardInteractive(String,
- *      String[], InteractiveCallback)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: InteractiveCallback.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public interface InteractiveCallback {
-    /**
-     * This callback interface is used during a "keyboard-interactive"
-     * authentication. Every time the server sends a set of challenges (however,
-     * most often just one challenge at a time), this callback function will be
-     * called to give your application a chance to talk to the user and to
-     * determine the response(s).
-     * <p>
-     * Some copy-paste information from the standard: a command line interface
-     * (CLI) client SHOULD print the name and instruction (if non-empty), adding
-     * newlines. Then for each prompt in turn, the client SHOULD display the
-     * prompt and read the user input. The name and instruction fields MAY be
-     * empty strings, the client MUST be prepared to handle this correctly. The
-     * prompt field(s) MUST NOT be empty strings.
-     * <p>
-     * Please refer to draft-ietf-secsh-auth-kbdinteract-XX.txt for the details.
-     * <p>
-     * Note: clients SHOULD use control character filtering as discussed in
-     * RFC4251 to avoid attacks by including
-     * terminal control characters in the fields to be displayed.
-     *
-     * @param name
-     *            the name String sent by the server.
-     * @param instruction
-     *            the instruction String sent by the server.
-     * @param numPrompts
-     *            number of prompts - may be zero (in this case, you should just
-     *            return a String array of length zero).
-     * @param prompt
-     *            an array (length <code>numPrompts</code>) of Strings
-     * @param echo
-     *            an array (length <code>numPrompts</code>) of booleans. For
-     *            each prompt, the corresponding echo field indicates whether or
-     *            not the user input should be echoed as characters are typed.
-     * @return an array of reponses - the array size must match the parameter
-     *         <code>numPrompts</code>.
-     */
-    public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo)
-    throws Exception;
-}
--- a/src/com/trilead/ssh2/KnownHosts.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,759 +0,0 @@
-
-package com.trilead.ssh2;
-
-import java.io.BufferedReader;
-import java.io.CharArrayReader;
-import java.io.CharArrayWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.io.UnsupportedEncodingException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPublicKey;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Locale;
-import java.util.Vector;
-
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-
-import com.trilead.ssh2.crypto.Base64;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
-import com.trilead.ssh2.signature.RSASHA1Verify;
-
-
-/**
- * The <code>KnownHosts</code> class is a handy tool to verify received server hostkeys
- * based on the information in <code>known_hosts</code> files (the ones used by OpenSSH).
- * <p>
- * It offers basically an in-memory database for known_hosts entries, as well as some
- * helper functions. Entries from a <code>known_hosts</code> file can be loaded at construction time.
- * It is also possible to add more keys later (e.g., one can parse different
- * <code>known_hosts<code> files).
- * <p>
- * It is a thread safe implementation, therefore, you need only to instantiate one
- * <code>KnownHosts</code> for your whole application.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: KnownHosts.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-
-public class KnownHosts {
-    public static final int HOSTKEY_IS_OK = 0;
-    public static final int HOSTKEY_IS_NEW = 1;
-    public static final int HOSTKEY_HAS_CHANGED = 2;
-
-    private class KnownHostsEntry {
-        String[] patterns;
-        PublicKey key;
-
-        KnownHostsEntry(String[] patterns, PublicKey key) {
-            this.patterns = patterns;
-            this.key = key;
-        }
-    }
-
-    private LinkedList<KnownHostsEntry> publicKeys = new LinkedList<KnownHostsEntry>();
-
-    public KnownHosts() {
-    }
-
-    public KnownHosts(char[] knownHostsData) throws IOException {
-        initialize(knownHostsData);
-    }
-
-    public KnownHosts(File knownHosts) throws IOException {
-        initialize(knownHosts);
-    }
-
-    /**
-     * Adds a single public key entry to the database. Note: this will NOT add the public key
-     * to any physical file (e.g., "~/.ssh/known_hosts") - use <code>addHostkeyToFile()</code> for that purpose.
-     * This method is designed to be used in a {@link ServerHostKeyVerifier}.
-     *
-     * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
-     *        OpenSSH sshd man page for a description of the pattern matching algorithm.
-     * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
-     * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
-     * @throws IOException
-     */
-    public void addHostkey(String hostnames[], String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException {
-        if (hostnames == null)
-            throw new IllegalArgumentException("hostnames may not be null");
-
-        if ("ssh-rsa".equals(serverHostKeyAlgorithm)) {
-            RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
-
-            synchronized (publicKeys) {
-                publicKeys.add(new KnownHostsEntry(hostnames, rpk));
-            }
-        }
-        else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
-            DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
-
-            synchronized (publicKeys) {
-                publicKeys.add(new KnownHostsEntry(hostnames, dpk));
-            }
-        }
-        else if (serverHostKeyAlgorithm.startsWith(ECDSASHA2Verify.ECDSA_SHA2_PREFIX)) {
-            ECPublicKey epk = ECDSASHA2Verify.decodeSSHECDSAPublicKey(serverHostKey);
-
-            synchronized (publicKeys) {
-                publicKeys.add(new KnownHostsEntry(hostnames, epk));
-            }
-        }
-        else
-            throw new IOException("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
-    }
-
-    /**
-     * Parses the given known_hosts data and adds entries to the database.
-     *
-     * @param knownHostsData
-     * @throws IOException
-     */
-    public void addHostkeys(char[] knownHostsData) throws IOException {
-        initialize(knownHostsData);
-    }
-
-    /**
-     * Parses the given known_hosts file and adds entries to the database.
-     *
-     * @param knownHosts
-     * @throws IOException
-     */
-    public void addHostkeys(File knownHosts) throws IOException {
-        initialize(knownHosts);
-    }
-
-    /**
-     * Generate the hashed representation of the given hostname. Useful for adding entries
-     * with hashed hostnames to a known_hosts file. (see -H option of OpenSSH key-gen).
-     *
-     * @param hostname
-     * @return the hashed representation, e.g., "|1|cDhrv7zwEUV3k71CEPHnhHZezhA=|Xo+2y6rUXo2OIWRAYhBOIijbJMA="
-     */
-    public static final String createHashedHostname(String hostname) {
-        MessageDigest sha1;
-
-        try {
-            sha1 = MessageDigest.getInstance("SHA1");
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("VM doesn't support SHA1", e);
-        }
-
-        byte[] salt = new byte[sha1.getDigestLength()];
-        new SecureRandom().nextBytes(salt);
-        byte[] hash = hmacSha1Hash(salt, hostname);
-        String base64_salt = new String(Base64.encode(salt));
-        String base64_hash = new String(Base64.encode(hash));
-        return new String("|1|" + base64_salt + "|" + base64_hash);
-    }
-
-    private static final byte[] hmacSha1Hash(byte[] salt, String hostname) {
-        Mac hmac;
-
-        try {
-            hmac = Mac.getInstance("HmacSHA1");
-
-            if (salt.length != hmac.getMacLength())
-                throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")");
-
-            hmac.init(new SecretKeySpec(salt, "HmacSHA1"));
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("Unable to HMAC-SHA1", e);
-        }
-        catch (InvalidKeyException e) {
-            throw new RuntimeException("Unable to create SecretKey", e);
-        }
-
-        try {
-            hmac.update(hostname.getBytes("ISO-8859-1"));
-        }
-        catch (UnsupportedEncodingException ignore) {
-            /* Actually, ISO-8859-1 is supported by all correct
-             * Java implementations. But... you never know. */
-            hmac.update(hostname.getBytes());
-        }
-
-        return hmac.doFinal();
-    }
-
-    private final boolean checkHashed(String entry, String hostname) {
-        if (entry.startsWith("|1|") == false)
-            return false;
-
-        int delim_idx = entry.indexOf('|', 3);
-
-        if (delim_idx == -1)
-            return false;
-
-        String salt_base64 = entry.substring(3, delim_idx);
-        String hash_base64 = entry.substring(delim_idx + 1);
-        byte[] salt = null;
-        byte[] hash = null;
-
-        try {
-            salt = Base64.decode(salt_base64.toCharArray());
-            hash = Base64.decode(hash_base64.toCharArray());
-        }
-        catch (IOException e) {
-            return false;
-        }
-
-        try {
-            MessageDigest sha1 = MessageDigest.getInstance("SHA1");
-
-            if (salt.length != sha1.getDigestLength())
-                return false;
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("VM does not support SHA1", e);
-        }
-
-        byte[] dig = hmacSha1Hash(salt, hostname);
-
-        for (int i = 0; i < dig.length; i++)
-            if (dig[i] != hash[i])
-                return false;
-
-        return true;
-    }
-
-    private int checkKey(String remoteHostname, PublicKey remoteKey) {
-        int result = HOSTKEY_IS_NEW;
-
-        synchronized (publicKeys) {
-            Iterator<KnownHostsEntry> i = publicKeys.iterator();
-
-            while (i.hasNext()) {
-                KnownHostsEntry ke = i.next();
-
-                if (hostnameMatches(ke.patterns, remoteHostname) == false)
-                    continue;
-
-                boolean res = matchKeys(ke.key, remoteKey);
-
-                if (res == true)
-                    return HOSTKEY_IS_OK;
-
-                result = HOSTKEY_HAS_CHANGED;
-            }
-        }
-
-        return result;
-    }
-
-    private Vector<PublicKey> getAllKeys(String hostname) {
-        Vector<PublicKey> keys = new Vector<PublicKey>();
-
-        synchronized (publicKeys) {
-            Iterator<KnownHostsEntry> i = publicKeys.iterator();
-
-            while (i.hasNext()) {
-                KnownHostsEntry ke = i.next();
-
-                if (hostnameMatches(ke.patterns, hostname) == false)
-                    continue;
-
-                keys.addElement(ke.key);
-            }
-        }
-
-        return keys;
-    }
-
-    /**
-     * Try to find the preferred order of hostkey algorithms for the given hostname.
-     * Based on the type of hostkey that is present in the internal database
-     * (i.e., either <code>ssh-rsa</code> or <code>ssh-dss</code>)
-     * an ordered list of hostkey algorithms is returned which can be passed
-     * to <code>Connection.setServerHostKeyAlgorithms</code>.
-     *
-     * @param hostname
-     * @return <code>null</code> if no key for the given hostname is present or
-     * there are keys of multiple types present for the given hostname. Otherwise,
-     * an array with hostkey algorithms is returned (i.e., an array of length 2).
-     */
-    public String[] getPreferredServerHostkeyAlgorithmOrder(String hostname) {
-        String[] algos = recommendHostkeyAlgorithms(hostname);
-
-        if (algos != null)
-            return algos;
-
-        InetAddress[] ipAdresses = null;
-
-        try {
-            ipAdresses = InetAddress.getAllByName(hostname);
-        }
-        catch (UnknownHostException e) {
-            return null;
-        }
-
-        for (int i = 0; i < ipAdresses.length; i++) {
-            algos = recommendHostkeyAlgorithms(ipAdresses[i].getHostAddress());
-
-            if (algos != null)
-                return algos;
-        }
-
-        return null;
-    }
-
-    private final boolean hostnameMatches(String[] hostpatterns, String hostname) {
-        boolean isMatch = false;
-        boolean negate = false;
-        hostname = hostname.toLowerCase(Locale.US);
-
-        for (int k = 0; k < hostpatterns.length; k++) {
-            if (hostpatterns[k] == null)
-                continue;
-
-            String pattern = null;
-
-            /* In contrast to OpenSSH we also allow negated hash entries (as well as hashed
-             * entries in lines with multiple entries).
-             */
-
-            if ((hostpatterns[k].length() > 0) && (hostpatterns[k].charAt(0) == '!')) {
-                pattern = hostpatterns[k].substring(1);
-                negate = true;
-            }
-            else {
-                pattern = hostpatterns[k];
-                negate = false;
-            }
-
-            /* Optimize, no need to check this entry */
-
-            if ((isMatch) && (negate == false))
-                continue;
-
-            /* Now compare */
-
-            if (pattern.charAt(0) == '|') {
-                if (checkHashed(pattern, hostname)) {
-                    if (negate)
-                        return false;
-
-                    isMatch = true;
-                }
-            }
-            else {
-                pattern = pattern.toLowerCase(Locale.US);
-
-                if ((pattern.indexOf('?') != -1) || (pattern.indexOf('*') != -1)) {
-                    if (pseudoRegex(pattern.toCharArray(), 0, hostname.toCharArray(), 0)) {
-                        if (negate)
-                            return false;
-
-                        isMatch = true;
-                    }
-                }
-                else if (pattern.compareTo(hostname) == 0) {
-                    if (negate)
-                        return false;
-
-                    isMatch = true;
-                }
-            }
-        }
-
-        return isMatch;
-    }
-
-    private void initialize(char[] knownHostsData) throws IOException {
-        BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
-
-        while (true) {
-            String line = br.readLine();
-
-            if (line == null)
-                break;
-
-            line = line.trim();
-
-            if (line.startsWith("#"))
-                continue;
-
-            String[] arr = line.split(" ");
-
-            if (arr.length >= 3) {
-                if ((arr[1].compareTo("ssh-rsa") == 0) || (arr[1].compareTo("ssh-dss") == 0)) {
-                    String[] hostnames = arr[0].split(",");
-                    byte[] msg = Base64.decode(arr[2].toCharArray());
-                    addHostkey(hostnames, arr[1], msg);
-                }
-            }
-        }
-    }
-
-    private void initialize(File knownHosts) throws IOException {
-        char[] buff = new char[512];
-        CharArrayWriter cw = new CharArrayWriter();
-        knownHosts.createNewFile();
-        FileReader fr = new FileReader(knownHosts);
-
-        while (true) {
-            int len = fr.read(buff);
-
-            if (len < 0)
-                break;
-
-            cw.write(buff, 0, len);
-        }
-
-        fr.close();
-        initialize(cw.toCharArray());
-    }
-
-    private final boolean matchKeys(PublicKey key1, PublicKey key2) {
-        return key1.equals(key2);
-    }
-
-    private final boolean pseudoRegex(char[] pattern, int i, char[] match, int j) {
-        /* This matching logic is equivalent to the one present in OpenSSH 4.1 */
-        while (true) {
-            /* Are we at the end of the pattern? */
-            if (pattern.length == i)
-                return (match.length == j);
-
-            if (pattern[i] == '*') {
-                i++;
-
-                if (pattern.length == i)
-                    return true;
-
-                if ((pattern[i] != '*') && (pattern[i] != '?')) {
-                    while (true) {
-                        if ((pattern[i] == match[j]) && pseudoRegex(pattern, i + 1, match, j + 1))
-                            return true;
-
-                        j++;
-
-                        if (match.length == j)
-                            return false;
-                    }
-                }
-
-                while (true) {
-                    if (pseudoRegex(pattern, i, match, j))
-                        return true;
-
-                    j++;
-
-                    if (match.length == j)
-                        return false;
-                }
-            }
-
-            if (match.length == j)
-                return false;
-
-            if ((pattern[i] != '?') && (pattern[i] != match[j]))
-                return false;
-
-            i++;
-            j++;
-        }
-    }
-
-    private String[] recommendHostkeyAlgorithms(String hostname) {
-        String preferredAlgo = null;
-        Vector<PublicKey> keys = getAllKeys(hostname);
-
-        for (int i = 0; i < keys.size(); i++) {
-            String thisAlgo = null;
-
-            if (keys.elementAt(i) instanceof RSAPublicKey)
-                thisAlgo = "ssh-rsa";
-            else if (keys.elementAt(i) instanceof DSAPublicKey)
-                thisAlgo = "ssh-dss";
-            else
-                continue;
-
-            if (preferredAlgo != null) {
-                /* If we find different key types, then return null */
-                if (preferredAlgo.compareTo(thisAlgo) != 0)
-                    return null;
-
-                /* OK, we found the same algo again, optimize */
-                continue;
-            }
-        }
-
-        /* If we did not find anything that we know of, return null */
-
-        if (preferredAlgo == null)
-            return null;
-
-        /* Now put the preferred algo to the start of the array.
-         * You may ask yourself why we do it that way - basically, we could just
-         * return only the preferred algorithm: since we have a saved key of that
-         * type (sent earlier from the remote host), then that should work out.
-         * However, imagine that the server is (for whatever reasons) not offering
-         * that type of hostkey anymore (e.g., "ssh-rsa" was disabled and
-         * now "ssh-dss" is being used). If we then do not let the server send us
-         * a fresh key of the new type, then we shoot ourself into the foot:
-         * the connection cannot be established and hence the user cannot decide
-         * if he/she wants to accept the new key.
-         */
-
-        if (preferredAlgo.equals("ssh-rsa"))
-            return new String[] { "ssh-rsa", "ssh-dss" };
-
-        return new String[] { "ssh-dss", "ssh-rsa" };
-    }
-
-    /**
-     * Checks the internal hostkey database for the given hostkey.
-     * If no matching key can be found, then the hostname is resolved to an IP address
-     * and the search is repeated using that IP address.
-     *
-     * @param hostname the server's hostname, will be matched with all hostname patterns
-     * @param serverHostKeyAlgorithm type of hostkey, either <code>ssh-rsa</code> or <code>ssh-dss</code>
-     * @param serverHostKey the key blob
-     * @return <ul>
-     *         <li><code>HOSTKEY_IS_OK</code>: the given hostkey matches an entry for the given hostname</li>
-     *         <li><code>HOSTKEY_IS_NEW</code>: no entries found for this hostname and this type of hostkey</li>
-     *         <li><code>HOSTKEY_HAS_CHANGED</code>: hostname is known, but with another key of the same type
-     *         (man-in-the-middle attack?)</li>
-     *         </ul>
-     * @throws IOException if the supplied key blob cannot be parsed or does not match the given hostkey type.
-     */
-    public int verifyHostkey(String hostname, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException {
-        PublicKey remoteKey = null;
-
-        if ("ssh-rsa".equals(serverHostKeyAlgorithm)) {
-            remoteKey = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
-        }
-        else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
-            remoteKey = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
-        }
-        else if (serverHostKeyAlgorithm.startsWith("ecdsa-sha2-")) {
-            remoteKey = ECDSASHA2Verify.decodeSSHECDSAPublicKey(serverHostKey);
-        }
-        else
-            throw new IllegalArgumentException("Unknown hostkey type " + serverHostKeyAlgorithm);
-
-        int result = checkKey(hostname, remoteKey);
-
-        if (result == HOSTKEY_IS_OK)
-            return result;
-
-        InetAddress[] ipAdresses = null;
-
-        try {
-            ipAdresses = InetAddress.getAllByName(hostname);
-        }
-        catch (UnknownHostException e) {
-            return result;
-        }
-
-        for (int i = 0; i < ipAdresses.length; i++) {
-            int newresult = checkKey(ipAdresses[i].getHostAddress(), remoteKey);
-
-            if (newresult == HOSTKEY_IS_OK)
-                return newresult;
-
-            if (newresult == HOSTKEY_HAS_CHANGED)
-                result = HOSTKEY_HAS_CHANGED;
-        }
-
-        return result;
-    }
-
-    /**
-     * Adds a single public key entry to the a known_hosts file.
-     * This method is designed to be used in a {@link ServerHostKeyVerifier}.
-     *
-     * @param knownHosts the file where the publickey entry will be appended.
-     * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
-     *        OpenSSH sshd man page for a description of the pattern matching algorithm.
-     * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
-     * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
-     * @throws IOException
-     */
-    public final static void addHostkeyToFile(File knownHosts, String[] hostnames, String serverHostKeyAlgorithm,
-            byte[] serverHostKey) throws IOException {
-        if ((hostnames == null) || (hostnames.length == 0))
-            throw new IllegalArgumentException("Need at least one hostname specification");
-
-        if ((serverHostKeyAlgorithm == null) || (serverHostKey == null))
-            throw new IllegalArgumentException();
-
-        CharArrayWriter writer = new CharArrayWriter();
-
-        for (int i = 0; i < hostnames.length; i++) {
-            if (i != 0)
-                writer.write(',');
-
-            writer.write(hostnames[i]);
-        }
-
-        writer.write(' ');
-        writer.write(serverHostKeyAlgorithm);
-        writer.write(' ');
-        writer.write(Base64.encode(serverHostKey));
-        writer.write("\n");
-        char[] entry = writer.toCharArray();
-        RandomAccessFile raf = new RandomAccessFile(knownHosts, "rw");
-        long len = raf.length();
-
-        if (len > 0) {
-            raf.seek(len - 1);
-            int last = raf.read();
-
-            if (last != '\n')
-                raf.write('\n');
-        }
-
-        raf.write(new String(entry).getBytes("ISO-8859-1"));
-        raf.close();
-    }
-
-    /**
-     * Generates a "raw" fingerprint of a hostkey.
-     *
-     * @param type either "md5" or "sha1"
-     * @param keyType either "ssh-rsa" or "ssh-dss"
-     * @param hostkey the hostkey
-     * @return the raw fingerprint
-     */
-    static final private byte[] rawFingerPrint(String type, String keyType, byte[] hostkey) {
-        MessageDigest dig = null;
-
-        try {
-            if ("md5".equals(type)) {
-                dig = MessageDigest.getInstance("MD5");
-            }
-            else if ("sha1".equals(type)) {
-                dig = MessageDigest.getInstance("SHA1");
-            }
-            else {
-                throw new IllegalArgumentException("Unknown hash type " + type);
-            }
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw new IllegalArgumentException("Unknown hash type " + type);
-        }
-
-        if (keyType.startsWith("ecdsa-sha2-")) {
-        }
-        else if ("ssh-rsa".equals(keyType)) {
-        }
-        else if ("ssh-dss".equals(keyType)) {
-        }
-        else
-            throw new IllegalArgumentException("Unknown key type " + keyType);
-
-        if (hostkey == null)
-            throw new IllegalArgumentException("hostkey is null");
-
-        dig.update(hostkey);
-        return dig.digest();
-    }
-
-    /**
-     * Convert a raw fingerprint to hex representation (XX:YY:ZZ...).
-     * @param fingerprint raw fingerprint
-     * @return the hex representation
-     */
-    static final private String rawToHexFingerprint(byte[] fingerprint) {
-        final char[] alpha = "0123456789abcdef".toCharArray();
-        StringBuffer sb = new StringBuffer();
-
-        for (int i = 0; i < fingerprint.length; i++) {
-            if (i != 0)
-                sb.append(':');
-
-            int b = fingerprint[i] & 0xff;
-            sb.append(alpha[b >> 4]);
-            sb.append(alpha[b & 15]);
-        }
-
-        return sb.toString();
-    }
-
-    /**
-     * Convert a raw fingerprint to bubblebabble representation.
-     * @param raw raw fingerprint
-     * @return the bubblebabble representation
-     */
-    static final private String rawToBubblebabbleFingerprint(byte[] raw) {
-        final char[] v = "aeiouy".toCharArray();
-        final char[] c = "bcdfghklmnprstvzx".toCharArray();
-        StringBuffer sb = new StringBuffer();
-        int seed = 1;
-        int rounds = (raw.length / 2) + 1;
-        sb.append('x');
-
-        for (int i = 0; i < rounds; i++) {
-            if (((i + 1) < rounds) || ((raw.length) % 2 != 0)) {
-                sb.append(v[(((raw[2 * i] >> 6) & 3) + seed) % 6]);
-                sb.append(c[(raw[2 * i] >> 2) & 15]);
-                sb.append(v[((raw[2 * i] & 3) + (seed / 6)) % 6]);
-
-                if ((i + 1) < rounds) {
-                    sb.append(c[(((raw[(2 * i) + 1])) >> 4) & 15]);
-                    sb.append('-');
-                    sb.append(c[(((raw[(2 * i) + 1]))) & 15]);
-                    // As long as seed >= 0, seed will be >= 0 afterwards
-                    seed = ((seed * 5) + (((raw[2 * i] & 0xff) * 7) + (raw[(2 * i) + 1] & 0xff))) % 36;
-                }
-            }
-            else {
-                sb.append(v[seed % 6]); // seed >= 0, therefore index positive
-                sb.append('x');
-                sb.append(v[seed / 6]);
-            }
-        }
-
-        sb.append('x');
-        return sb.toString();
-    }
-
-    /**
-     * Convert a ssh2 key-blob into a human readable hex fingerprint.
-     * Generated fingerprints are identical to those generated by OpenSSH.
-     * <p>
-     * Example fingerprint: d0:cb:76:19:99:5a:03:fc:73:10:70:93:f2:44:63:47.
-
-     * @param keytype either "ssh-rsa" or "ssh-dss"
-     * @param publickey key blob
-     * @return Hex fingerprint
-     */
-    public final static String createHexFingerprint(String keytype, byte[] publickey) {
-        byte[] raw = rawFingerPrint("md5", keytype, publickey);
-        return rawToHexFingerprint(raw);
-    }
-
-    /**
-     * Convert a ssh2 key-blob into a human readable bubblebabble fingerprint.
-     * The used bubblebabble algorithm (taken from OpenSSH) generates fingerprints
-     * that are easier to remember for humans.
-     * <p>
-     * Example fingerprint: xofoc-bubuz-cazin-zufyl-pivuk-biduk-tacib-pybur-gonar-hotat-lyxux.
-     *
-     * @param keytype either "ssh-rsa" or "ssh-dss"
-     * @param publickey key data
-     * @return Bubblebabble fingerprint
-     */
-    public final static String createBubblebabbleFingerprint(String keytype, byte[] publickey) {
-        byte[] raw = rawFingerPrint("sha1", keytype, publickey);
-        return rawToBubblebabbleFingerprint(raw);
-    }
-}
--- a/src/com/trilead/ssh2/LocalPortForwarder.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-
-import com.trilead.ssh2.channel.ChannelManager;
-import com.trilead.ssh2.channel.LocalAcceptThread;
-
-
-/**
- * A <code>LocalPortForwarder</code> forwards TCP/IP connections to a local
- * port via the secure tunnel to another host (which may or may not be identical
- * to the remote SSH-2 server). Checkout {@link Connection#createLocalPortForwarder(int, String, int)}
- * on how to create one.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: LocalPortForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class LocalPortForwarder {
-    ChannelManager cm;
-
-    String host_to_connect;
-
-    int port_to_connect;
-
-    LocalAcceptThread lat;
-
-    LocalPortForwarder(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
-    throws IOException {
-        this.cm = cm;
-        this.host_to_connect = host_to_connect;
-        this.port_to_connect = port_to_connect;
-        lat = new LocalAcceptThread(cm, local_port, host_to_connect, port_to_connect);
-        lat.setDaemon(true);
-        lat.start();
-    }
-
-    LocalPortForwarder(ChannelManager cm, InetSocketAddress addr, String host_to_connect, int port_to_connect)
-    throws IOException {
-        this.cm = cm;
-        this.host_to_connect = host_to_connect;
-        this.port_to_connect = port_to_connect;
-        lat = new LocalAcceptThread(cm, addr, host_to_connect, port_to_connect);
-        lat.setDaemon(true);
-        lat.start();
-    }
-
-    /**
-     * Stop TCP/IP forwarding of newly arriving connections.
-     *
-     * @throws IOException
-     */
-    public void close() throws IOException {
-        lat.stopWorking();
-    }
-}
--- a/src/com/trilead/ssh2/LocalStreamForwarder.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import com.trilead.ssh2.channel.Channel;
-import com.trilead.ssh2.channel.ChannelManager;
-import com.trilead.ssh2.channel.LocalAcceptThread;
-
-
-/**
- * A <code>LocalStreamForwarder</code> forwards an Input- and Outputstream
- * pair via the secure tunnel to another host (which may or may not be identical
- * to the remote SSH-2 server).
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: LocalStreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class LocalStreamForwarder {
-    ChannelManager cm;
-
-    String host_to_connect;
-    int port_to_connect;
-    LocalAcceptThread lat;
-
-    Channel cn;
-
-    LocalStreamForwarder(ChannelManager cm, String host_to_connect, int port_to_connect) throws IOException {
-        this.cm = cm;
-        this.host_to_connect = host_to_connect;
-        this.port_to_connect = port_to_connect;
-        cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, "127.0.0.1", 0);
-    }
-
-    /**
-     * @return An <code>InputStream</code> object.
-     * @throws IOException
-     */
-    public InputStream getInputStream() throws IOException {
-        return cn.getStdoutStream();
-    }
-
-    /**
-     * Get the OutputStream. Please be aware that the implementation MAY use an
-     * internal buffer. To make sure that the buffered data is sent over the
-     * tunnel, you have to call the <code>flush</code> method of the
-     * <code>OutputStream</code>. To signal EOF, please use the
-     * <code>close</code> method of the <code>OutputStream</code>.
-     *
-     * @return An <code>OutputStream</code> object.
-     * @throws IOException
-     */
-    public OutputStream getOutputStream() throws IOException {
-        return cn.getStdinStream();
-    }
-
-    /**
-     * Close the underlying SSH forwarding channel and free up resources.
-     * You can also use this method to force the shutdown of the underlying
-     * forwarding channel. Pending output (OutputStream not flushed) will NOT
-     * be sent. Pending input (InputStream) can still be read. If the shutdown
-     * operation is already in progress (initiated from either side), then this
-     * call is a no-op.
-     *
-     * @throws IOException
-     */
-    public void close() throws IOException {
-        cm.closeChannel(cn, "Closed due to user request.", true);
-    }
-}
--- a/src/com/trilead/ssh2/ProxyData.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * An abstract marker interface implemented by all proxy data implementations.
- *
- * @see HTTPProxyData
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public abstract interface ProxyData {
-}
--- a/src/com/trilead/ssh2/SCPClient.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,644 +0,0 @@
-
-package com.trilead.ssh2;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * A very basic <code>SCPClient</code> that can be used to copy files from/to
- * the SSH-2 server. On the server side, the "scp" program must be in the PATH.
- * <p>
- * This scp client is thread safe - you can download (and upload) different sets
- * of files concurrently without any troubles. The <code>SCPClient</code> is
- * actually mapping every request to a distinct {@link Session}.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SCPClient.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-
-public class SCPClient {
-    Connection conn;
-
-    class LenNamePair {
-        long length;
-        String filename;
-    }
-
-    public SCPClient(Connection conn) {
-        if (conn == null)
-            throw new IllegalArgumentException("Cannot accept null argument!");
-
-        this.conn = conn;
-    }
-
-    private void readResponse(InputStream is) throws IOException {
-        int c = is.read();
-
-        if (c == 0)
-            return;
-
-        if (c == -1)
-            throw new IOException("Remote scp terminated unexpectedly.");
-
-        if ((c != 1) && (c != 2))
-            throw new IOException("Remote scp sent illegal error code.");
-
-        if (c == 2)
-            throw new IOException("Remote scp terminated with error.");
-
-        String err = receiveLine(is);
-        throw new IOException("Remote scp terminated with error (" + err + ").");
-    }
-
-    private String receiveLine(InputStream is) throws IOException {
-        StringBuffer sb = new StringBuffer(30);
-
-        while (true) {
-            /*
-             * This is a random limit - if your path names are longer, then
-             * adjust it
-             */
-            if (sb.length() > 8192)
-                throw new IOException("Remote scp sent a too long line");
-
-            int c = is.read();
-
-            if (c < 0)
-                throw new IOException("Remote scp terminated unexpectedly.");
-
-            if (c == '\n')
-                break;
-
-            sb.append((char) c);
-        }
-
-        return sb.toString();
-    }
-
-    private LenNamePair parseCLine(String line) throws IOException {
-        /* Minimum line: "xxxx y z" ---> 8 chars */
-        long len;
-
-        if (line.length() < 8)
-            throw new IOException("Malformed C line sent by remote SCP binary, line too short.");
-
-        if ((line.charAt(4) != ' ') || (line.charAt(5) == ' '))
-            throw new IOException("Malformed C line sent by remote SCP binary.");
-
-        int length_name_sep = line.indexOf(' ', 5);
-
-        if (length_name_sep == -1)
-            throw new IOException("Malformed C line sent by remote SCP binary.");
-
-        String length_substring = line.substring(5, length_name_sep);
-        String name_substring = line.substring(length_name_sep + 1);
-
-        if ((length_substring.length() <= 0) || (name_substring.length() <= 0))
-            throw new IOException("Malformed C line sent by remote SCP binary.");
-
-        if ((6 + length_substring.length() + name_substring.length()) != line.length())
-            throw new IOException("Malformed C line sent by remote SCP binary.");
-
-        try {
-            len = Long.parseLong(length_substring);
-        }
-        catch (NumberFormatException e) {
-            throw new IOException("Malformed C line sent by remote SCP binary, cannot parse file length.");
-        }
-
-        if (len < 0)
-            throw new IOException("Malformed C line sent by remote SCP binary, illegal file length.");
-
-        LenNamePair lnp = new LenNamePair();
-        lnp.length = len;
-        lnp.filename = name_substring;
-        return lnp;
-    }
-
-    private void sendBytes(Session sess, byte[] data, String fileName, String mode) throws IOException {
-        OutputStream os = sess.getStdin();
-        InputStream is = new BufferedInputStream(sess.getStdout(), 512);
-        readResponse(is);
-        String cline = "C" + mode + " " + data.length + " " + fileName + "\n";
-        os.write(cline.getBytes("ISO-8859-1"));
-        os.flush();
-        readResponse(is);
-        os.write(data, 0, data.length);
-        os.write(0);
-        os.flush();
-        readResponse(is);
-        os.write("E\n".getBytes("ISO-8859-1"));
-        os.flush();
-    }
-
-    private void sendFiles(Session sess, String[] files, String[] remoteFiles, String mode) throws IOException {
-        byte[] buffer = new byte[8192];
-        OutputStream os = new BufferedOutputStream(sess.getStdin(), 40000);
-        InputStream is = new BufferedInputStream(sess.getStdout(), 512);
-        readResponse(is);
-
-        for (int i = 0; i < files.length; i++) {
-            File f = new File(files[i]);
-            long remain = f.length();
-            String remoteName;
-
-            if ((remoteFiles != null) && (remoteFiles.length > i) && (remoteFiles[i] != null))
-                remoteName = remoteFiles[i];
-            else
-                remoteName = f.getName();
-
-            String cline = "C" + mode + " " + remain + " " + remoteName + "\n";
-            os.write(cline.getBytes("ISO-8859-1"));
-            os.flush();
-            readResponse(is);
-            FileInputStream fis = null;
-
-            try {
-                fis = new FileInputStream(f);
-
-                while (remain > 0) {
-                    int trans;
-
-                    if (remain > buffer.length)
-                        trans = buffer.length;
-                    else
-                        trans = (int) remain;
-
-                    if (fis.read(buffer, 0, trans) != trans)
-                        throw new IOException("Cannot read enough from local file " + files[i]);
-
-                    os.write(buffer, 0, trans);
-                    remain -= trans;
-                }
-            }
-            finally {
-                if (fis != null)
-                    fis.close();
-            }
-
-            os.write(0);
-            os.flush();
-            readResponse(is);
-        }
-
-        os.write("E\n".getBytes("ISO-8859-1"));
-        os.flush();
-    }
-
-    private void receiveFiles(Session sess, OutputStream[] targets) throws IOException {
-        byte[] buffer = new byte[8192];
-        OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
-        InputStream is = new BufferedInputStream(sess.getStdout(), 40000);
-        os.write(0x0);
-        os.flush();
-
-        for (int i = 0; i < targets.length; i++) {
-            LenNamePair lnp = null;
-
-            while (true) {
-                int c = is.read();
-
-                if (c < 0)
-                    throw new IOException("Remote scp terminated unexpectedly.");
-
-                String line = receiveLine(is);
-
-                if (c == 'T') {
-                    /* Ignore modification times */
-                    continue;
-                }
-
-                if ((c == 1) || (c == 2))
-                    throw new IOException("Remote SCP error: " + line);
-
-                if (c == 'C') {
-                    lnp = parseCLine(line);
-                    break;
-                }
-
-                throw new IOException("Remote SCP error: " + ((char) c) + line);
-            }
-
-            os.write(0x0);
-            os.flush();
-            long remain = lnp.length;
-
-            while (remain > 0) {
-                int trans;
-
-                if (remain > buffer.length)
-                    trans = buffer.length;
-                else
-                    trans = (int) remain;
-
-                int this_time_received = is.read(buffer, 0, trans);
-
-                if (this_time_received < 0) {
-                    throw new IOException("Remote scp terminated connection unexpectedly");
-                }
-
-                targets[i].write(buffer, 0, this_time_received);
-                remain -= this_time_received;
-            }
-
-            readResponse(is);
-            os.write(0x0);
-            os.flush();
-        }
-    }
-
-    private void receiveFiles(Session sess, String[] files, String target) throws IOException {
-        byte[] buffer = new byte[8192];
-        OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
-        InputStream is = new BufferedInputStream(sess.getStdout(), 40000);
-        os.write(0x0);
-        os.flush();
-
-        for (int i = 0; i < files.length; i++) {
-            LenNamePair lnp = null;
-
-            while (true) {
-                int c = is.read();
-
-                if (c < 0)
-                    throw new IOException("Remote scp terminated unexpectedly.");
-
-                String line = receiveLine(is);
-
-                if (c == 'T') {
-                    /* Ignore modification times */
-                    continue;
-                }
-
-                if ((c == 1) || (c == 2))
-                    throw new IOException("Remote SCP error: " + line);
-
-                if (c == 'C') {
-                    lnp = parseCLine(line);
-                    break;
-                }
-
-                throw new IOException("Remote SCP error: " + ((char) c) + line);
-            }
-
-            os.write(0x0);
-            os.flush();
-            File f = new File(target + File.separatorChar + lnp.filename);
-            FileOutputStream fop = null;
-
-            try {
-                fop = new FileOutputStream(f);
-                long remain = lnp.length;
-
-                while (remain > 0) {
-                    int trans;
-
-                    if (remain > buffer.length)
-                        trans = buffer.length;
-                    else
-                        trans = (int) remain;
-
-                    int this_time_received = is.read(buffer, 0, trans);
-
-                    if (this_time_received < 0) {
-                        throw new IOException("Remote scp terminated connection unexpectedly");
-                    }
-
-                    fop.write(buffer, 0, this_time_received);
-                    remain -= this_time_received;
-                }
-            }
-            finally {
-                if (fop != null)
-                    fop.close();
-            }
-
-            readResponse(is);
-            os.write(0x0);
-            os.flush();
-        }
-    }
-
-    /**
-     * Copy a local file to a remote directory, uses mode 0600 when creating the
-     * file on the remote side.
-     *
-     * @param localFile
-     *            Path and name of local file.
-     * @param remoteTargetDirectory
-     *            Remote target directory. Use an empty string to specify the
-     *            default directory.
-     *
-     * @throws IOException
-     */
-    public void put(String localFile, String remoteTargetDirectory) throws IOException {
-        put(new String[] { localFile }, remoteTargetDirectory, "0600");
-    }
-
-    /**
-     * Copy a set of local files to a remote directory, uses mode 0600 when
-     * creating files on the remote side.
-     *
-     * @param localFiles
-     *            Paths and names of local file names.
-     * @param remoteTargetDirectory
-     *            Remote target directory. Use an empty string to specify the
-     *            default directory.
-     *
-     * @throws IOException
-     */
-
-    public void put(String[] localFiles, String remoteTargetDirectory) throws IOException {
-        put(localFiles, remoteTargetDirectory, "0600");
-    }
-
-    /**
-     * Copy a local file to a remote directory, uses the specified mode when
-     * creating the file on the remote side.
-     *
-     * @param localFile
-     *            Path and name of local file.
-     * @param remoteTargetDirectory
-     *            Remote target directory. Use an empty string to specify the
-     *            default directory.
-     * @param mode
-     *            a four digit string (e.g., 0644, see "man chmod", "man open")
-     * @throws IOException
-     */
-    public void put(String localFile, String remoteTargetDirectory, String mode) throws IOException {
-        put(new String[] { localFile }, remoteTargetDirectory, mode);
-    }
-
-    /**
-     * Copy a local file to a remote directory, uses the specified mode and
-     * remote filename when creating the file on the remote side.
-     *
-     * @param localFile
-     *            Path and name of local file.
-     * @param remoteFileName
-     *            The name of the file which will be created in the remote
-     *            target directory.
-     * @param remoteTargetDirectory
-     *            Remote target directory. Use an empty string to specify the
-     *            default directory.
-     * @param mode
-     *            a four digit string (e.g., 0644, see "man chmod", "man open")
-     * @throws IOException
-     */
-    public void put(String localFile, String remoteFileName, String remoteTargetDirectory, String mode)
-    throws IOException {
-        put(new String[] { localFile }, new String[] { remoteFileName }, remoteTargetDirectory, mode);
-    }
-
-    /**
-     * Create a remote file and copy the contents of the passed byte array into
-     * it. Uses mode 0600 for creating the remote file.
-     *
-     * @param data
-     *            the data to be copied into the remote file.
-     * @param remoteFileName
-     *            The name of the file which will be created in the remote
-     *            target directory.
-     * @param remoteTargetDirectory
-     *            Remote target directory. Use an empty string to specify the
-     *            default directory.
-     * @throws IOException
-     */
-
-    public void put(byte[] data, String remoteFileName, String remoteTargetDirectory) throws IOException {
-        put(data, remoteFileName, remoteTargetDirectory, "0600");
-    }
-
-    /**
-     * Create a remote file and copy the contents of the passed byte array into
-     * it. The method use the specified mode when creating the file on the
-     * remote side.
-     *
-     * @param data
-     *            the data to be copied into the remote file.
-     * @param remoteFileName
-     *            The name of the file which will be created in the remote
-     *            target directory.
-     * @param remoteTargetDirectory
-     *            Remote target directory. Use an empty string to specify the
-     *            default directory.
-     * @param mode
-     *            a four digit string (e.g., 0644, see "man chmod", "man open")
-     * @throws IOException
-     */
-    public void put(byte[] data, String remoteFileName, String remoteTargetDirectory, String mode) throws IOException {
-        Session sess = null;
-
-        if ((remoteFileName == null) || (remoteTargetDirectory == null) || (mode == null))
-            throw new IllegalArgumentException("Null argument.");
-
-        if (mode.length() != 4)
-            throw new IllegalArgumentException("Invalid mode.");
-
-        for (int i = 0; i < mode.length(); i++)
-            if (Character.isDigit(mode.charAt(i)) == false)
-                throw new IllegalArgumentException("Invalid mode.");
-
-        remoteTargetDirectory = remoteTargetDirectory.trim();
-        remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
-        String cmd = "scp -t -d " + remoteTargetDirectory;
-
-        try {
-            sess = conn.openSession();
-            sess.execCommand(cmd);
-            sendBytes(sess, data, remoteFileName, mode);
-        }
-        catch (IOException e) {
-            throw(IOException) new IOException("Error during SCP transfer.").initCause(e);
-        }
-        finally {
-            if (sess != null)
-                sess.close();
-        }
-    }
-
-    /**
-     * Copy a set of local files to a remote directory, uses the specified mode
-     * when creating the files on the remote side.
-     *
-     * @param localFiles
-     *            Paths and names of the local files.
-     * @param remoteTargetDirectory
-     *            Remote target directory. Use an empty string to specify the
-     *            default directory.
-     * @param mode
-     *            a four digit string (e.g., 0644, see "man chmod", "man open")
-     * @throws IOException
-     */
-    public void put(String[] localFiles, String remoteTargetDirectory, String mode) throws IOException {
-        put(localFiles, null, remoteTargetDirectory, mode);
-    }
-
-    public void put(String[] localFiles, String[] remoteFiles, String remoteTargetDirectory, String mode)
-    throws IOException {
-        Session sess = null;
-
-        /*
-         * remoteFiles may be null, indicating that the local filenames shall be
-         * used
-         */
-
-        if ((localFiles == null) || (remoteTargetDirectory == null) || (mode == null))
-            throw new IllegalArgumentException("Null argument.");
-
-        if (mode.length() != 4)
-            throw new IllegalArgumentException("Invalid mode.");
-
-        for (int i = 0; i < mode.length(); i++)
-            if (Character.isDigit(mode.charAt(i)) == false)
-                throw new IllegalArgumentException("Invalid mode.");
-
-        if (localFiles.length == 0)
-            return;
-
-        remoteTargetDirectory = remoteTargetDirectory.trim();
-        remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
-        String cmd = "scp -t -d " + remoteTargetDirectory;
-
-        for (int i = 0; i < localFiles.length; i++) {
-            if (localFiles[i] == null)
-                throw new IllegalArgumentException("Cannot accept null filename.");
-        }
-
-        try {
-            sess = conn.openSession();
-            sess.execCommand(cmd);
-            sendFiles(sess, localFiles, remoteFiles, mode);
-        }
-        catch (IOException e) {
-            throw(IOException) new IOException("Error during SCP transfer.").initCause(e);
-        }
-        finally {
-            if (sess != null)
-                sess.close();
-        }
-    }
-
-    /**
-     * Download a file from the remote server to a local directory.
-     *
-     * @param remoteFile
-     *            Path and name of the remote file.
-     * @param localTargetDirectory
-     *            Local directory to put the downloaded file.
-     *
-     * @throws IOException
-     */
-    public void get(String remoteFile, String localTargetDirectory) throws IOException {
-        get(new String[] { remoteFile }, localTargetDirectory);
-    }
-
-    /**
-     * Download a file from the remote server and pipe its contents into an
-     * <code>OutputStream</code>. Please note that, to enable flexible usage
-     * of this method, the <code>OutputStream</code> will not be closed nor
-     * flushed.
-     *
-     * @param remoteFile
-     *            Path and name of the remote file.
-     * @param target
-     *            OutputStream where the contents of the file will be sent to.
-     * @throws IOException
-     */
-    public void get(String remoteFile, OutputStream target) throws IOException {
-        get(new String[] { remoteFile }, new OutputStream[] { target });
-    }
-
-    private void get(String remoteFiles[], OutputStream[] targets) throws IOException {
-        Session sess = null;
-
-        if ((remoteFiles == null) || (targets == null))
-            throw new IllegalArgumentException("Null argument.");
-
-        if (remoteFiles.length != targets.length)
-            throw new IllegalArgumentException("Length of arguments does not match.");
-
-        if (remoteFiles.length == 0)
-            return;
-
-        String cmd = "scp -f";
-
-        for (int i = 0; i < remoteFiles.length; i++) {
-            if (remoteFiles[i] == null)
-                throw new IllegalArgumentException("Cannot accept null filename.");
-
-            String tmp = remoteFiles[i].trim();
-
-            if (tmp.length() == 0)
-                throw new IllegalArgumentException("Cannot accept empty filename.");
-
-            cmd += (" " + tmp);
-        }
-
-        try {
-            sess = conn.openSession();
-            sess.execCommand(cmd);
-            receiveFiles(sess, targets);
-        }
-        catch (IOException e) {
-            throw(IOException) new IOException("Error during SCP transfer.").initCause(e);
-        }
-        finally {
-            if (sess != null)
-                sess.close();
-        }
-    }
-
-    /**
-     * Download a set of files from the remote server to a local directory.
-     *
-     * @param remoteFiles
-     *            Paths and names of the remote files.
-     * @param localTargetDirectory
-     *            Local directory to put the downloaded files.
-     *
-     * @throws IOException
-     */
-    public void get(String remoteFiles[], String localTargetDirectory) throws IOException {
-        Session sess = null;
-
-        if ((remoteFiles == null) || (localTargetDirectory == null))
-            throw new IllegalArgumentException("Null argument.");
-
-        if (remoteFiles.length == 0)
-            return;
-
-        String cmd = "scp -f";
-
-        for (int i = 0; i < remoteFiles.length; i++) {
-            if (remoteFiles[i] == null)
-                throw new IllegalArgumentException("Cannot accept null filename.");
-
-            String tmp = remoteFiles[i].trim();
-
-            if (tmp.length() == 0)
-                throw new IllegalArgumentException("Cannot accept empty filename.");
-
-            cmd += (" " + tmp);
-        }
-
-        try {
-            sess = conn.openSession();
-            sess.execCommand(cmd);
-            receiveFiles(sess, remoteFiles, localTargetDirectory);
-        }
-        catch (IOException e) {
-            throw(IOException) new IOException("Error during SCP transfer.").initCause(e);
-        }
-        finally {
-            if (sess != null)
-                sess.close();
-        }
-    }
-}
--- a/src/com/trilead/ssh2/SFTPException.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-
-import com.trilead.ssh2.sftp.ErrorCodes;
-
-
-/**
- * Used in combination with the SFTPv3Client. This exception wraps
- * error messages sent by the SFTP server.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SFTPException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class SFTPException extends IOException {
-    private static final long serialVersionUID = 578654644222421811L;
-
-    private final String sftpErrorMessage;
-    private final int sftpErrorCode;
-
-    private static String constructMessage(String s, int errorCode) {
-        String[] detail = ErrorCodes.getDescription(errorCode);
-
-        if (detail == null)
-            return s + " (UNKNOW SFTP ERROR CODE)";
-
-        return s + " (" + detail[0] + ": " + detail[1] + ")";
-    }
-
-    SFTPException(String msg, int errorCode) {
-        super(constructMessage(msg, errorCode));
-        sftpErrorMessage = msg;
-        sftpErrorCode = errorCode;
-    }
-
-    /**
-     * Get the error message sent by the server. Often, this
-     * message does not help a lot (e.g., "failure").
-     *
-     * @return the plain string as sent by the server.
-     */
-    public String getServerErrorMessage() {
-        return sftpErrorMessage;
-    }
-
-    /**
-     * Get the error code sent by the server.
-     *
-     * @return an error code as defined in the SFTP specs.
-     */
-    public int getServerErrorCode() {
-        return sftpErrorCode;
-    }
-
-    /**
-     * Get the symbolic name of the error code as given in the SFTP specs.
-     *
-     * @return e.g., "SSH_FX_INVALID_FILENAME".
-     */
-    public String getServerErrorCodeSymbol() {
-        String[] detail = ErrorCodes.getDescription(sftpErrorCode);
-
-        if (detail == null)
-            return "UNKNOW SFTP ERROR CODE " + sftpErrorCode;
-
-        return detail[0];
-    }
-
-    /**
-     * Get the description of the error code as given in the SFTP specs.
-     *
-     * @return e.g., "The filename is not valid."
-     */
-    public String getServerErrorCodeVerbose() {
-        String[] detail = ErrorCodes.getDescription(sftpErrorCode);
-
-        if (detail == null)
-            return "The error code " + sftpErrorCode + " is unknown.";
-
-        return detail[1];
-    }
-}
--- a/src/com/trilead/ssh2/SFTPv3Client.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1215 +0,0 @@
-
-package com.trilead.ssh2;
-
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.nio.charset.Charset;
-import java.util.HashMap;
-import java.util.Vector;
-
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-import com.trilead.ssh2.sftp.AttribFlags;
-import com.trilead.ssh2.sftp.ErrorCodes;
-import com.trilead.ssh2.sftp.Packet;
-
-
-/**
- * A <code>SFTPv3Client</code> represents a SFTP (protocol version 3)
- * client connection tunnelled over a SSH-2 connection. This is a very simple
- * (synchronous) implementation.
- * <p>
- * Basically, most methods in this class map directly to one of
- * the packet types described in draft-ietf-secsh-filexfer-02.txt.
- * <p>
- * Note: this is experimental code.
- * <p>
- * Error handling: the methods of this class throw IOExceptions. However, unless
- * there is catastrophic failure, exceptions of the type {@link SFTPv3Client} will
- * be thrown (a subclass of IOException). Therefore, you can implement more verbose
- * behavior by checking if a thrown exception if of this type. If yes, then you
- * can cast the exception and access detailed information about the failure.
- * <p>
- * Notes about file names, directory names and paths, copy-pasted
- * from the specs:
- * <ul>
- * <li>SFTP v3 represents file names as strings. File names are
- * assumed to use the slash ('/') character as a directory separator.</li>
- * <li>File names starting with a slash are "absolute", and are relative to
- * the root of the file system.  Names starting with any other character
- * are relative to the user's default directory (home directory).</li>
- * <li>Servers SHOULD interpret a path name component ".." as referring to
- * the parent directory, and "." as referring to the current directory.
- * If the server implementation limits access to certain parts of the
- * file system, it must be extra careful in parsing file names when
- * enforcing such restrictions.  There have been numerous reported
- * security bugs where a ".." in a path name has allowed access outside
- * the intended area.</li>
- * <li>An empty path name is valid, and it refers to the user's default
- * directory (usually the user's home directory).</li>
- * </ul>
- * <p>
- * If you are still not tired then please go on and read the comment for
- * {@link #setCharset(String)}.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SFTPv3Client.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $
- */
-public class SFTPv3Client {
-    final Connection conn;
-    final Session sess;
-    final PrintStream debug;
-
-    boolean flag_closed = false;
-
-    InputStream is;
-    OutputStream os;
-
-    int protocol_version = 0;
-    HashMap server_extensions = new HashMap();
-
-    int next_request_id = 1000;
-
-    String charsetName = null;
-
-    /**
-     * Create a SFTP v3 client.
-     *
-     * @param conn The underlying SSH-2 connection to be used.
-     * @param debug
-     * @throws IOException
-     *
-     * @deprecated this constructor (debug version) will disappear in the future,
-     *             use {@link #SFTPv3Client(Connection)} instead.
-     */
-    @Deprecated
-    public SFTPv3Client(Connection conn, PrintStream debug) throws IOException {
-        if (conn == null)
-            throw new IllegalArgumentException("Cannot accept null argument!");
-
-        this.conn = conn;
-        this.debug = debug;
-
-        if (debug != null)
-            debug.println("Opening session and starting SFTP subsystem.");
-
-        sess = conn.openSession();
-        sess.startSubSystem("sftp");
-        is = sess.getStdout();
-        os = new BufferedOutputStream(sess.getStdin(), 2048);
-
-        if ((is == null) || (os == null))
-            throw new IOException("There is a problem with the streams of the underlying channel.");
-
-        init();
-    }
-
-    /**
-     * Create a SFTP v3 client.
-     *
-     * @param conn The underlying SSH-2 connection to be used.
-     * @throws IOException
-     */
-    public SFTPv3Client(Connection conn) throws IOException {
-        this(conn, null);
-    }
-
-    /**
-     * Set the charset used to convert between Java Unicode Strings and byte encodings
-     * used by the server for paths and file names. Unfortunately, the SFTP v3 draft
-     * says NOTHING about such conversions (well, with the exception of error messages
-     * which have to be in UTF-8). Newer drafts specify to use UTF-8 for file names
-     * (if I remember correctly). However, a quick test using OpenSSH serving a EXT-3
-     * filesystem has shown that UTF-8 seems to be a bad choice for SFTP v3 (tested with
-     * filenames containing german umlauts). "windows-1252" seems to work better for Europe.
-     * Luckily, "windows-1252" is the platform default in my case =).
-     * <p>
-     * If you don't set anything, then the platform default will be used (this is the default
-     * behavior).
-     *
-     * @see #getCharset()
-     *
-     * @param charset the name of the charset to be used or <code>null</code> to use the platform's
-     *        default encoding.
-     * @throws IOException
-     */
-    public void setCharset(String charset) throws IOException {
-        if (charset == null) {
-            charsetName = charset;
-            return;
-        }
-
-        try {
-            Charset.forName(charset);
-        }
-        catch (Exception e) {
-            throw(IOException) new IOException("This charset is not supported").initCause(e);
-        }
-
-        charsetName = charset;
-    }
-
-    /**
-     * The currently used charset for filename encoding/decoding.
-     *
-     * @see #setCharset(String)
-     *
-     * @return The name of the charset (<code>null</code> if the platform's default charset is being used)
-     */
-    public String getCharset() {
-        return charsetName;
-    }
-
-    private final void checkHandleValidAndOpen(SFTPv3FileHandle handle) throws IOException {
-        if (handle.client != this)
-            throw new IOException("The file handle was created with another SFTPv3FileHandle instance.");
-
-        if (handle.isClosed == true)
-            throw new IOException("The file handle is closed.");
-    }
-
-    private final void sendMessage(int type, int requestId, byte[] msg, int off, int len) throws IOException {
-        int msglen = len + 1;
-
-        if (type != Packet.SSH_FXP_INIT)
-            msglen += 4;
-
-        os.write(msglen >> 24);
-        os.write(msglen >> 16);
-        os.write(msglen >> 8);
-        os.write(msglen);
-        os.write(type);
-
-        if (type != Packet.SSH_FXP_INIT) {
-            os.write(requestId >> 24);
-            os.write(requestId >> 16);
-            os.write(requestId >> 8);
-            os.write(requestId);
-        }
-
-        os.write(msg, off, len);
-        os.flush();
-    }
-
-    private final void sendMessage(int type, int requestId, byte[] msg) throws IOException {
-        sendMessage(type, requestId, msg, 0, msg.length);
-    }
-
-    private final void readBytes(byte[] buff, int pos, int len) throws IOException {
-        while (len > 0) {
-            int count = is.read(buff, pos, len);
-
-            if (count < 0)
-                throw new IOException("Unexpected end of sftp stream.");
-
-            if ((count == 0) || (count > len))
-                throw new IOException("Underlying stream implementation is bogus!");
-
-            len -= count;
-            pos += count;
-        }
-    }
-
-    /**
-     * Read a message and guarantee that the <b>contents</b> is not larger than
-     * <code>maxlen</code> bytes.
-     * <p>
-     * Note: receiveMessage(34000) actually means that the message may be up to 34004
-     * bytes (the length attribute preceeding the contents is 4 bytes).
-     *
-     * @param maxlen
-     * @return the message contents
-     * @throws IOException
-     */
-    private final byte[] receiveMessage(int maxlen) throws IOException {
-        byte[] msglen = new byte[4];
-        readBytes(msglen, 0, 4);
-        int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff));
-
-        if ((len > maxlen) || (len <= 0))
-            throw new IOException("Illegal sftp packet len: " + len);
-
-        byte[] msg = new byte[len];
-        readBytes(msg, 0, len);
-        return msg;
-    }
-
-    private final int generateNextRequestID() {
-        synchronized (this) {
-            return next_request_id++;
-        }
-    }
-
-    private final void closeHandle(byte[] handle) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(handle, 0, handle.length);
-        sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes());
-        expectStatusOKMessage(req_id);
-    }
-
-    private SFTPv3FileAttributes readAttrs(TypesReader tr) throws IOException {
-        /*
-         * uint32   flags
-         * uint64   size           present only if flag SSH_FILEXFER_ATTR_SIZE
-         * uint32   uid            present only if flag SSH_FILEXFER_ATTR_V3_UIDGID
-         * uint32   gid            present only if flag SSH_FILEXFER_ATTR_V3_UIDGID
-         * uint32   permissions    present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
-         * uint32   atime          present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME
-         * uint32   mtime          present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME
-         * uint32   extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
-         * string   extended_type
-         * string   extended_data
-         * ...      more extended data (extended_type - extended_data pairs),
-         *          so that number of pairs equals extended_count
-         */
-        SFTPv3FileAttributes fa = new SFTPv3FileAttributes();
-        int flags = tr.readUINT32();
-
-        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SIZE) != 0) {
-            if (debug != null)
-                debug.println("SSH_FILEXFER_ATTR_SIZE");
-
-            fa.size = Long.valueOf(tr.readUINT64());
-        }
-
-        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0) {
-            if (debug != null)
-                debug.println("SSH_FILEXFER_ATTR_V3_UIDGID");
-
-            fa.uid = Integer.valueOf(tr.readUINT32());
-            fa.gid = Integer.valueOf(tr.readUINT32());
-        }
-
-        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
-            if (debug != null)
-                debug.println("SSH_FILEXFER_ATTR_PERMISSIONS");
-
-            fa.permissions = Integer.valueOf(tr.readUINT32());
-        }
-
-        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0) {
-            if (debug != null)
-                debug.println("SSH_FILEXFER_ATTR_V3_ACMODTIME");
-
-            fa.atime = Long.valueOf((tr.readUINT32()) & 0xffffffffl);
-            fa.mtime = Long.valueOf((tr.readUINT32()) & 0xffffffffl);
-        }
-
-        if ((flags & AttribFlags.SSH_FILEXFER_ATTR_EXTENDED) != 0) {
-            int count = tr.readUINT32();
-
-            if (debug != null)
-                debug.println("SSH_FILEXFER_ATTR_EXTENDED (" + count + ")");
-
-            /* Read it anyway to detect corrupt packets */
-
-            while (count > 0) {
-                tr.readByteString();
-                tr.readByteString();
-                count--;
-            }
-        }
-
-        return fa;
-    }
-
-    /**
-     * Retrieve the file attributes of an open file.
-     *
-     * @param handle a SFTPv3FileHandle handle.
-     * @return a SFTPv3FileAttributes object.
-     * @throws IOException
-     */
-    public SFTPv3FileAttributes fstat(SFTPv3FileHandle handle) throws IOException {
-        checkHandleValidAndOpen(handle);
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
-
-        if (debug != null) {
-            debug.println("Sending SSH_FXP_FSTAT...");
-            debug.flush();
-        }
-
-        sendMessage(Packet.SSH_FXP_FSTAT, req_id, tw.getBytes());
-        byte[] resp = receiveMessage(34000);
-
-        if (debug != null) {
-            debug.println("Got REPLY.");
-            debug.flush();
-        }
-
-        TypesReader tr = new TypesReader(resp);
-        int t = tr.readByte();
-        int rep_id = tr.readUINT32();
-
-        if (rep_id != req_id)
-            throw new IOException("The server sent an invalid id field.");
-
-        if (t == Packet.SSH_FXP_ATTRS) {
-            return readAttrs(tr);
-        }
-
-        if (t != Packet.SSH_FXP_STATUS)
-            throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
-        int errorCode = tr.readUINT32();
-        throw new SFTPException(tr.readString(), errorCode);
-    }
-
-    private SFTPv3FileAttributes statBoth(String path, int statMethod) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(path, charsetName);
-
-        if (debug != null) {
-            debug.println("Sending SSH_FXP_STAT/SSH_FXP_LSTAT...");
-            debug.flush();
-        }
-
-        sendMessage(statMethod, req_id, tw.getBytes());
-        byte[] resp = receiveMessage(34000);
-
-        if (debug != null) {
-            debug.println("Got REPLY.");
-            debug.flush();
-        }
-
-        TypesReader tr = new TypesReader(resp);
-        int t = tr.readByte();
-        int rep_id = tr.readUINT32();
-
-        if (rep_id != req_id)
-            throw new IOException("The server sent an invalid id field.");
-
-        if (t == Packet.SSH_FXP_ATTRS) {
-            return readAttrs(tr);
-        }
-
-        if (t != Packet.SSH_FXP_STATUS)
-            throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
-        int errorCode = tr.readUINT32();
-        throw new SFTPException(tr.readString(), errorCode);
-    }
-
-    /**
-     * Retrieve the file attributes of a file. This method
-     * follows symbolic links on the server.
-     *
-     * @see #lstat(String)
-     *
-     * @param path See the {@link SFTPv3Client comment} for the class for more details.
-     * @return a SFTPv3FileAttributes object.
-     * @throws IOException
-     */
-    public SFTPv3FileAttributes stat(String path) throws IOException {
-        return statBoth(path, Packet.SSH_FXP_STAT);
-    }
-
-    /**
-     * Retrieve the file attributes of a file. This method
-     * does NOT follow symbolic links on the server.
-     *
-     * @see #stat(String)
-     *
-     * @param path See the {@link SFTPv3Client comment} for the class for more details.
-     * @return a SFTPv3FileAttributes object.
-     * @throws IOException
-     */
-    public SFTPv3FileAttributes lstat(String path) throws IOException {
-        return statBoth(path, Packet.SSH_FXP_LSTAT);
-    }
-
-    /**
-     * Read the target of a symbolic link.
-     *
-     * @param path See the {@link SFTPv3Client comment} for the class for more details.
-     * @return The target of the link.
-     * @throws IOException
-     */
-    public String readLink(String path) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(path, charsetName);
-
-        if (debug != null) {
-            debug.println("Sending SSH_FXP_READLINK...");
-            debug.flush();
-        }
-
-        sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes());
-        byte[] resp = receiveMessage(34000);
-
-        if (debug != null) {
-            debug.println("Got REPLY.");
-            debug.flush();
-        }
-
-        TypesReader tr = new TypesReader(resp);
-        int t = tr.readByte();
-        int rep_id = tr.readUINT32();
-
-        if (rep_id != req_id)
-            throw new IOException("The server sent an invalid id field.");
-
-        if (t == Packet.SSH_FXP_NAME) {
-            int count = tr.readUINT32();
-
-            if (count != 1)
-                throw new IOException("The server sent an invalid SSH_FXP_NAME packet.");
-
-            return tr.readString(charsetName);
-        }
-
-        if (t != Packet.SSH_FXP_STATUS)
-            throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
-        int errorCode = tr.readUINT32();
-        throw new SFTPException(tr.readString(), errorCode);
-    }
-
-    private void expectStatusOKMessage(int id) throws IOException {
-        byte[] resp = receiveMessage(34000);
-
-        if (debug != null) {
-            debug.println("Got REPLY.");
-            debug.flush();
-        }
-
-        TypesReader tr = new TypesReader(resp);
-        int t = tr.readByte();
-        int rep_id = tr.readUINT32();
-
-        if (rep_id != id)
-            throw new IOException("The server sent an invalid id field.");
-
-        if (t != Packet.SSH_FXP_STATUS)
-            throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
-        int errorCode = tr.readUINT32();
-
-        if (errorCode == ErrorCodes.SSH_FX_OK)
-            return;
-
-        throw new SFTPException(tr.readString(), errorCode);
-    }
-
-    /**
-     *  Modify the attributes of a file. Used for operations such as changing
-     *  the ownership, permissions or access times, as well as for truncating a file.
-     *
-     * @param path See the {@link SFTPv3Client comment} for the class for more details.
-     * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be
-     *             made to the attributes of the file. Empty fields will be ignored.
-     * @throws IOException
-     */
-    public void setstat(String path, SFTPv3FileAttributes attr) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(path, charsetName);
-        tw.writeBytes(createAttrs(attr));
-
-        if (debug != null) {
-            debug.println("Sending SSH_FXP_SETSTAT...");
-            debug.flush();
-        }
-
-        sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes());
-        expectStatusOKMessage(req_id);
-    }
-
-    /**
-     *  Modify the attributes of a file. Used for operations such as changing
-     *  the ownership, permissions or access times, as well as for truncating a file.
-     *
-     * @param handle a SFTPv3FileHandle handle
-     * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be
-     *             made to the attributes of the file. Empty fields will be ignored.
-     * @throws IOException
-     */
-    public void fsetstat(SFTPv3FileHandle handle, SFTPv3FileAttributes attr) throws IOException {
-        checkHandleValidAndOpen(handle);
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
-        tw.writeBytes(createAttrs(attr));
-
-        if (debug != null) {
-            debug.println("Sending SSH_FXP_FSETSTAT...");
-            debug.flush();
-        }
-
-        sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes());
-        expectStatusOKMessage(req_id);
-    }
-
-    /**
-     * Create a symbolic link on the server. Creates a link "src" that points
-     * to "target".
-     *
-     * @param src See the {@link SFTPv3Client comment} for the class for more details.
-     * @param target See the {@link SFTPv3Client comment} for the class for more details.
-     * @throws IOException
-     */
-    public void createSymlink(String src, String target) throws IOException {
-        int req_id = generateNextRequestID();
-        /* Either I am too stupid to understand the SFTP draft
-         * or the OpenSSH guys changed the semantics of src and target.
-         */
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(target, charsetName);
-        tw.writeString(src, charsetName);
-
-        if (debug != null) {
-            debug.println("Sending SSH_FXP_SYMLINK...");
-            debug.flush();
-        }
-
-        sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes());
-        expectStatusOKMessage(req_id);
-    }
-
-    /**
-     * Have the server canonicalize any given path name to an absolute path.
-     * This is useful for converting path names containing ".." components or
-     * relative pathnames without a leading slash into absolute paths.
-     *
-     * @param path See the {@link SFTPv3Client comment} for the class for more details.
-     * @return An absolute path.
-     * @throws IOException
-     */
-    public String canonicalPath(String path) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(path, charsetName);
-
-        if (debug != null) {
-            debug.println("Sending SSH_FXP_REALPATH...");
-            debug.flush();
-        }
-
-        sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes());
-        byte[] resp = receiveMessage(34000);
-
-        if (debug != null) {
-            debug.println("Got REPLY.");
-            debug.flush();
-        }
-
-        TypesReader tr = new TypesReader(resp);
-        int t = tr.readByte();
-        int rep_id = tr.readUINT32();
-
-        if (rep_id != req_id)
-            throw new IOException("The server sent an invalid id field.");
-
-        if (t == Packet.SSH_FXP_NAME) {
-            int count = tr.readUINT32();
-
-            if (count != 1)
-                throw new IOException("The server sent an invalid SSH_FXP_NAME packet.");
-
-            return tr.readString(charsetName);
-        }
-
-        if (t != Packet.SSH_FXP_STATUS)
-            throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
-        int errorCode = tr.readUINT32();
-        throw new SFTPException(tr.readString(), errorCode);
-    }
-
-    private final Vector scanDirectory(byte[] handle) throws IOException {
-        Vector files = new Vector();
-
-        while (true) {
-            int req_id = generateNextRequestID();
-            TypesWriter tw = new TypesWriter();
-            tw.writeString(handle, 0, handle.length);
-
-            if (debug != null) {
-                debug.println("Sending SSH_FXP_READDIR...");
-                debug.flush();
-            }
-
-            sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes());
-            /* Some servers send here a packet with size > 34000 */
-            /* To whom it may concern: please learn to read the specs. */
-            byte[] resp = receiveMessage(65536);
-
-            if (debug != null) {
-                debug.println("Got REPLY.");
-                debug.flush();
-            }
-
-            TypesReader tr = new TypesReader(resp);
-            int t = tr.readByte();
-            int rep_id = tr.readUINT32();
-
-            if (rep_id != req_id)
-                throw new IOException("The server sent an invalid id field.");
-
-            if (t == Packet.SSH_FXP_NAME) {
-                int count = tr.readUINT32();
-
-                if (debug != null)
-                    debug.println("Parsing " + count + " name entries...");
-
-                while (count > 0) {
-                    SFTPv3DirectoryEntry dirEnt = new SFTPv3DirectoryEntry();
-                    dirEnt.filename = tr.readString(charsetName);
-                    dirEnt.longEntry = tr.readString(charsetName);
-                    dirEnt.attributes = readAttrs(tr);
-                    files.addElement(dirEnt);
-
-                    if (debug != null)
-                        debug.println("File: '" + dirEnt.filename + "'");
-
-                    count--;
-                }
-
-                continue;
-            }
-
-            if (t != Packet.SSH_FXP_STATUS)
-                throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
-            int errorCode = tr.readUINT32();
-
-            if (errorCode == ErrorCodes.SSH_FX_EOF)
-                return files;
-
-            throw new SFTPException(tr.readString(), errorCode);
-        }
-    }
-
-    private final byte[] openDirectory(String path) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(path, charsetName);
-
-        if (debug != null) {
-            debug.println("Sending SSH_FXP_OPENDIR...");
-            debug.flush();
-        }
-
-        sendMessage(Packet.SSH_FXP_OPENDIR, req_id, tw.getBytes());
-        byte[] resp = receiveMessage(34000);
-        TypesReader tr = new TypesReader(resp);
-        int t = tr.readByte();
-        int rep_id = tr.readUINT32();
-
-        if (rep_id != req_id)
-            throw new IOException("The server sent an invalid id field.");
-
-        if (t == Packet.SSH_FXP_HANDLE) {
-            if (debug != null) {
-                debug.println("Got SSH_FXP_HANDLE.");
-                debug.flush();
-            }
-
-            byte[] handle = tr.readByteString();
-            return handle;
-        }
-
-        if (t != Packet.SSH_FXP_STATUS)
-            throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
-        int errorCode = tr.readUINT32();
-        String errorMessage = tr.readString();
-        throw new SFTPException(errorMessage, errorCode);
-    }
-
-    private final String expandString(byte[] b, int off, int len) {
-        StringBuffer sb = new StringBuffer();
-
-        for (int i = 0; i < len; i++) {
-            int c = b[off + i] & 0xff;
-
-            if ((c >= 32) && (c <= 126)) {
-                sb.append((char) c);
-            }
-            else {
-                sb.append("{0x" + Integer.toHexString(c) + "}");
-            }
-        }
-
-        return sb.toString();
-    }
-
-    private void init() throws IOException {
-        /* Send SSH_FXP_INIT (version 3) */
-        final int client_version = 3;
-
-        if (debug != null)
-            debug.println("Sending SSH_FXP_INIT (" + client_version + ")...");
-
-        TypesWriter tw = new TypesWriter();
-        tw.writeUINT32(client_version);
-        sendMessage(Packet.SSH_FXP_INIT, 0, tw.getBytes());
-
-        /* Receive SSH_FXP_VERSION */
-
-        if (debug != null)
-            debug.println("Waiting for SSH_FXP_VERSION...");
-
-        TypesReader tr = new TypesReader(receiveMessage(34000)); /* Should be enough for any reasonable server */
-        int type = tr.readByte();
-
-        if (type != Packet.SSH_FXP_VERSION) {
-            throw new IOException("The server did not send a SSH_FXP_VERSION packet (got " + type + ")");
-        }
-
-        protocol_version = tr.readUINT32();
-
-        if (debug != null)
-            debug.println("SSH_FXP_VERSION: protocol_version = " + protocol_version);
-
-        if (protocol_version != 3)
-            throw new IOException("Server version " + protocol_version + " is currently not supported");
-
-        /* Read and save extensions (if any) for later use */
-
-        while (tr.remain() != 0) {
-            String name = tr.readString();
-            byte[] value = tr.readByteString();
-            server_extensions.put(name, value);
-
-            if (debug != null)
-                debug.println("SSH_FXP_VERSION: extension: " + name + " = '" + expandString(value, 0, value.length)
-                              + "'");
-        }
-    }
-
-    /**
-     * Returns the negotiated SFTP protocol version between the client and the server.
-     *
-     * @return SFTP protocol version, i.e., "3".
-     *
-     */
-    public int getProtocolVersion() {
-        return protocol_version;
-    }
-
-    /**
-     * Close this SFTP session. NEVER forget to call this method to free up
-     * resources - even if you got an exception from one of the other methods.
-     * Sometimes these other methods may throw an exception, saying that the
-     * underlying channel is closed (this can happen, e.g., if the other server
-     * sent a close message.) However, as long as you have not called the
-     * <code>close()</code> method, you are likely wasting resources.
-     *
-     */
-    public void close() {
-        sess.close();
-    }
-
-    /**
-     * List the contents of a directory.
-     *
-     * @param dirName See the {@link SFTPv3Client comment} for the class for more details.
-     * @return A Vector containing {@link SFTPv3DirectoryEntry} objects.
-     * @throws IOException
-     */
-    public Vector ls(String dirName) throws IOException {
-        byte[] handle = openDirectory(dirName);
-        Vector result = scanDirectory(handle);
-        closeHandle(handle);
-        return result;
-    }
-
-    /**
-     * Create a new directory.
-     *
-     * @param dirName See the {@link SFTPv3Client comment} for the class for more details.
-     * @param posixPermissions the permissions for this directory, e.g., "0700" (remember that
-     *                         this is octal noation). The server will likely apply a umask.
-     *
-     * @throws IOException
-     */
-    public void mkdir(String dirName, int posixPermissions) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(dirName, charsetName);
-        tw.writeUINT32(AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS);
-        tw.writeUINT32(posixPermissions);
-        sendMessage(Packet.SSH_FXP_MKDIR, req_id, tw.getBytes());
-        expectStatusOKMessage(req_id);
-    }
-
-    /**
-     * Remove a file.
-     *
-     * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
-     * @throws IOException
-     */
-    public void rm(String fileName) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(fileName, charsetName);
-        sendMessage(Packet.SSH_FXP_REMOVE, req_id, tw.getBytes());
-        expectStatusOKMessage(req_id);
-    }
-
-    /**
-     * Remove an empty directory.
-     *
-     * @param dirName See the {@link SFTPv3Client comment} for the class for more details.
-     * @throws IOException
-     */
-    public void rmdir(String dirName) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(dirName, charsetName);
-        sendMessage(Packet.SSH_FXP_RMDIR, req_id, tw.getBytes());
-        expectStatusOKMessage(req_id);
-    }
-
-    /**
-     * Move a file or directory.
-     *
-     * @param oldPath See the {@link SFTPv3Client comment} for the class for more details.
-     * @param newPath See the {@link SFTPv3Client comment} for the class for more details.
-     * @throws IOException
-     */
-    public void mv(String oldPath, String newPath) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(oldPath, charsetName);
-        tw.writeString(newPath, charsetName);
-        sendMessage(Packet.SSH_FXP_RENAME, req_id, tw.getBytes());
-        expectStatusOKMessage(req_id);
-    }
-
-    /**
-     * Open a file for reading.
-     *
-     * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
-     * @return a SFTPv3FileHandle handle
-     * @throws IOException
-     */
-    public SFTPv3FileHandle openFileRO(String fileName) throws IOException {
-        return openFile(fileName, 0x00000001, null); // SSH_FXF_READ
-    }
-
-    /**
-     * Open a file for reading and writing.
-     *
-     * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
-     * @return a SFTPv3FileHandle handle
-     * @throws IOException
-     */
-    public SFTPv3FileHandle openFileRW(String fileName) throws IOException {
-        return openFile(fileName, 0x00000003, null); // SSH_FXF_READ | SSH_FXF_WRITE
-    }
-
-    // Append is broken (already in the specification, because there is no way to
-    // send a write operation (what offset to use??))
-    //  public SFTPv3FileHandle openFileRWAppend(String fileName) throws IOException
-    //  {
-    //      return openFile(fileName, 0x00000007, null); // SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND
-    //  }
-
-    /**
-     * Create a file and open it for reading and writing.
-     * Same as {@link #createFile(String, SFTPv3FileAttributes) createFile(fileName, null)}.
-     *
-     * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
-     * @return a SFTPv3FileHandle handle
-     * @throws IOException
-     */
-    public SFTPv3FileHandle createFile(String fileName) throws IOException {
-        return createFile(fileName, null);
-    }
-
-    /**
-     * Create a file and open it for reading and writing.
-     * You can specify the default attributes of the file (the server may or may
-     * not respect your wishes).
-     *
-     * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
-     * @param attr may be <code>null</code> to use server defaults. Probably only
-     *             the <code>uid</code>, <code>gid</code> and <code>permissions</code>
-     *             (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle}
-     *             structure make sense. You need only to set those fields where you want
-     *             to override the server's defaults.
-     * @return a SFTPv3FileHandle handle
-     * @throws IOException
-     */
-    public SFTPv3FileHandle createFile(String fileName, SFTPv3FileAttributes attr) throws IOException {
-        return openFile(fileName, 0x00000008 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_READ | SSH_FXF_WRITE
-    }
-
-    /**
-     * Create a file (truncate it if it already exists) and open it for reading and writing.
-     * Same as {@link #createFileTruncate(String, SFTPv3FileAttributes) createFileTruncate(fileName, null)}.
-     *
-     * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
-     * @return a SFTPv3FileHandle handle
-     * @throws IOException
-     */
-    public SFTPv3FileHandle createFileTruncate(String fileName) throws IOException {
-        return createFileTruncate(fileName, null);
-    }
-
-    /**
-     * reate a file (truncate it if it already exists) and open it for reading and writing.
-     * You can specify the default attributes of the file (the server may or may
-     * not respect your wishes).
-     *
-     * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
-     * @param attr may be <code>null</code> to use server defaults. Probably only
-     *             the <code>uid</code>, <code>gid</code> and <code>permissions</code>
-     *             (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle}
-     *             structure make sense. You need only to set those fields where you want
-     *             to override the server's defaults.
-     * @return a SFTPv3FileHandle handle
-     * @throws IOException
-     */
-    public SFTPv3FileHandle createFileTruncate(String fileName, SFTPv3FileAttributes attr) throws IOException {
-        return openFile(fileName, 0x00000018 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_READ | SSH_FXF_WRITE
-    }
-
-    private byte[] createAttrs(SFTPv3FileAttributes attr) {
-        TypesWriter tw = new TypesWriter();
-        int attrFlags = 0;
-
-        if (attr == null) {
-            tw.writeUINT32(0);
-        }
-        else {
-            if (attr.size != null)
-                attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_SIZE;
-
-            if ((attr.uid != null) && (attr.gid != null))
-                attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID;
-
-            if (attr.permissions != null)
-                attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS;
-
-            if ((attr.atime != null) && (attr.mtime != null))
-                attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME;
-
-            tw.writeUINT32(attrFlags);
-
-            if (attr.size != null)
-                tw.writeUINT64(attr.size.longValue());
-
-            if ((attr.uid != null) && (attr.gid != null)) {
-                tw.writeUINT32(attr.uid.intValue());
-                tw.writeUINT32(attr.gid.intValue());
-            }
-
-            if (attr.permissions != null)
-                tw.writeUINT32(attr.permissions.intValue());
-
-            if ((attr.atime != null) && (attr.mtime != null)) {
-                tw.writeUINT32(attr.atime.intValue());
-                tw.writeUINT32(attr.mtime.intValue());
-            }
-        }
-
-        return tw.getBytes();
-    }
-
-    private SFTPv3FileHandle openFile(String fileName, int flags, SFTPv3FileAttributes attr) throws IOException {
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(fileName, charsetName);
-        tw.writeUINT32(flags);
-        tw.writeBytes(createAttrs(attr));
-
-        if (debug != null) {
-            debug.println("Sending SSH_FXP_OPEN...");
-            debug.flush();
-        }
-
-        sendMessage(Packet.SSH_FXP_OPEN, req_id, tw.getBytes());
-        byte[] resp = receiveMessage(34000);
-        TypesReader tr = new TypesReader(resp);
-        int t = tr.readByte();
-        int rep_id = tr.readUINT32();
-
-        if (rep_id != req_id)
-            throw new IOException("The server sent an invalid id field.");
-
-        if (t == Packet.SSH_FXP_HANDLE) {
-            if (debug != null) {
-                debug.println("Got SSH_FXP_HANDLE.");
-                debug.flush();
-            }
-
-            return new SFTPv3FileHandle(this, tr.readByteString());
-        }
-
-        if (t != Packet.SSH_FXP_STATUS)
-            throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
-        int errorCode = tr.readUINT32();
-        String errorMessage = tr.readString();
-        throw new SFTPException(errorMessage, errorCode);
-    }
-
-    /**
-     * Read bytes from a file. No more than 32768 bytes may be read at once.
-     * Be aware that the semantics of read() are different than for Java streams.
-     * <p>
-     * <ul>
-     * <li>The server will read as many bytes as it can from the file (up to <code>len</code>),
-     * and return them.</li>
-     * <li>If EOF is encountered before reading any data, <code>-1</code> is returned.
-     * <li>If an error occurs, an exception is thrown</li>.
-     * <li>For normal disk files, it is guaranteed that the server will return the specified
-     * number of bytes, or up to end of file. For, e.g., device files this may return
-     * fewer bytes than requested.</li>
-     * </ul>
-     *
-     * @param handle a SFTPv3FileHandle handle
-     * @param fileOffset offset (in bytes) in the file
-     * @param dst the destination byte array
-     * @param dstoff offset in the destination byte array
-     * @param len how many bytes to read, 0 &lt; len &lt;= 32768 bytes
-     * @return the number of bytes that could be read, may be less than requested if
-     *         the end of the file is reached, -1 is returned in case of <code>EOF</code>
-     * @throws IOException
-     */
-    public int read(SFTPv3FileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException {
-        checkHandleValidAndOpen(handle);
-
-        if ((len > 32768) || (len <= 0))
-            throw new IllegalArgumentException("invalid len argument");
-
-        int req_id = generateNextRequestID();
-        TypesWriter tw = new TypesWriter();
-        tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
-        tw.writeUINT64(fileOffset);
-        tw.writeUINT32(len);
-
-        if (debug != null) {
-            debug.println("Sending SSH_FXP_READ...");
-            debug.flush();
-        }
-
-        sendMessage(Packet.SSH_FXP_READ, req_id, tw.getBytes());
-        byte[] resp = receiveMessage(34000);
-        TypesReader tr = new TypesReader(resp);
-        int t = tr.readByte();
-        int rep_id = tr.readUINT32();
-
-        if (rep_id != req_id)
-            throw new IOException("The server sent an invalid id field.");
-
-        if (t == Packet.SSH_FXP_DATA) {
-            if (debug != null) {
-                debug.println("Got SSH_FXP_DATA...");
-                debug.flush();
-            }
-
-            int readLen = tr.readUINT32();
-
-            if ((readLen < 0) || (readLen > len))
-                throw new IOException("The server sent an invalid length field.");
-
-            tr.readBytes(dst, dstoff, readLen);
-            return readLen;
-        }
-
-        if (t != Packet.SSH_FXP_STATUS)
-            throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
-        int errorCode = tr.readUINT32();
-
-        if (errorCode == ErrorCodes.SSH_FX_EOF) {
-            if (debug != null) {
-                debug.println("Got SSH_FX_EOF.");
-                debug.flush();
-            }
-
-            return -1;
-        }
-
-        String errorMessage = tr.readString();
-        throw new SFTPException(errorMessage, errorCode);
-    }
-
-    /**
-     * Write bytes to a file. If <code>len</code> &gt; 32768, then the write operation will
-     * be split into multiple writes.
-     *
-     * @param handle a SFTPv3FileHandle handle.
-     * @param fileOffset offset (in bytes) in the file.
-     * @param src the source byte array.
-     * @param srcoff offset in the source byte array.
-     * @param len how many bytes to write.
-     * @throws IOException
-     */
-    public void write(SFTPv3FileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException {
-        checkHandleValidAndOpen(handle);
-
-        while (len > 0) {
-            int writeRequestLen = len;
-
-            if (writeRequestLen > 32768)
-                writeRequestLen = 32768;
-
-            int req_id = generateNextRequestID();
-            TypesWriter tw = new TypesWriter();
-            tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
-            tw.writeUINT64(fileOffset);
-            tw.writeString(src, srcoff, writeRequestLen);
-
-            if (debug != null) {
-                debug.println("Sending SSH_FXP_WRITE...");
-                debug.flush();
-            }
-
-            sendMessage(Packet.SSH_FXP_WRITE, req_id, tw.getBytes());
-            fileOffset += writeRequestLen;
-            srcoff += writeRequestLen;
-            len -= writeRequestLen;
-            byte[] resp = receiveMessage(34000);
-            TypesReader tr = new TypesReader(resp);
-            int t = tr.readByte();
-            int rep_id = tr.readUINT32();
-
-            if (rep_id != req_id)
-                throw new IOException("The server sent an invalid id field.");
-
-            if (t != Packet.SSH_FXP_STATUS)
-                throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
-            int errorCode = tr.readUINT32();
-
-            if (errorCode == ErrorCodes.SSH_FX_OK)
-                continue;
-
-            String errorMessage = tr.readString();
-            throw new SFTPException(errorMessage, errorCode);
-        }
-    }
-
-    /**
-     * Close a file.
-     *
-     * @param handle a SFTPv3FileHandle handle
-     * @throws IOException
-     */
-    public void closeFile(SFTPv3FileHandle handle) throws IOException {
-        if (handle == null)
-            throw new IllegalArgumentException("the handle argument may not be null");
-
-        try {
-            if (handle.isClosed == false) {
-                closeHandle(handle.fileHandle);
-            }
-        }
-        finally {
-            handle.isClosed = true;
-        }
-    }
-}
--- a/src/com/trilead/ssh2/SFTPv3DirectoryEntry.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>SFTPv3DirectoryEntry</code> as returned by {@link SFTPv3Client#ls(String)}.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SFTPv3DirectoryEntry.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class SFTPv3DirectoryEntry {
-    /**
-     *  A relative name within the directory, without any path components.
-     */
-    public String filename;
-
-    /**
-     * An expanded format for the file name, similar to what is returned by
-     * "ls -l" on Un*x systems.
-     * <p>
-     * The format of this field is unspecified by the SFTP v3 protocol.
-     * It MUST be suitable for use in the output of a directory listing
-     * command (in fact, the recommended operation for a directory listing
-     * command is to simply display this data).  However, clients SHOULD NOT
-     * attempt to parse the longname field for file attributes; they SHOULD
-     * use the attrs field instead.
-     * <p>
-     * The recommended format for the longname field is as follows:<br>
-     * <code>-rwxr-xr-x   1 mjos     staff      348911 Mar 25 14:29 t-filexfer</code>
-     */
-    public String longEntry;
-
-    /**
-     * The attributes of this entry.
-     */
-    public SFTPv3FileAttributes attributes;
-}
--- a/src/com/trilead/ssh2/SFTPv3FileAttributes.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,136 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>SFTPv3FileAttributes</code> object represents detail information
- * about a file on the server. Not all fields may/must be present.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SFTPv3FileAttributes.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-
-public class SFTPv3FileAttributes {
-    /**
-     * The SIZE attribute. <code>NULL</code> if not present.
-     */
-    public Long size = null;
-
-    /**
-     * The UID attribute. <code>NULL</code> if not present.
-     */
-    public Integer uid = null;
-
-    /**
-     * The GID attribute. <code>NULL</code> if not present.
-     */
-    public Integer gid = null;
-
-    /**
-     * The POSIX permissions. <code>NULL</code> if not present.
-     * <p>
-     * Here is a list:
-     * <p>
-     * <pre>Note: these numbers are all OCTAL.
-     *
-     *  S_IFMT     0170000   bitmask for the file type bitfields
-     *  S_IFSOCK   0140000   socket
-     *  S_IFLNK    0120000   symbolic link
-     *  S_IFREG    0100000   regular file
-     *  S_IFBLK    0060000   block device
-     *  S_IFDIR    0040000   directory
-     *  S_IFCHR    0020000   character device
-     *  S_IFIFO    0010000   fifo
-     *  S_ISUID    0004000   set UID bit
-     *  S_ISGID    0002000   set GID bit
-     *  S_ISVTX    0001000   sticky bit
-     *
-     *  S_IRWXU    00700     mask for file owner permissions
-     *  S_IRUSR    00400     owner has read permission
-     *  S_IWUSR    00200     owner has write permission
-     *  S_IXUSR    00100     owner has execute permission
-     *  S_IRWXG    00070     mask for group permissions
-     *  S_IRGRP    00040     group has read permission
-     *  S_IWGRP    00020     group has write permission
-     *  S_IXGRP    00010     group has execute permission
-     *  S_IRWXO    00007     mask for permissions for others (not in group)
-     *  S_IROTH    00004     others have read permission
-     *  S_IWOTH    00002     others have write permisson
-     *  S_IXOTH    00001     others have execute permission
-     * </pre>
-     */
-    public Integer permissions = null;
-
-    /**
-     * The ATIME attribute. Represented as seconds from Jan 1, 1970 in UTC.
-     * <code>NULL</code> if not present.
-     */
-    public Long atime = null;
-
-    /**
-     * The MTIME attribute. Represented as seconds from Jan 1, 1970 in UTC.
-     * <code>NULL</code> if not present.
-     */
-    public Long mtime = null;
-
-    /**
-     * Checks if this entry is a directory.
-     *
-     * @return Returns true if permissions are available and they indicate
-     *         that this entry represents a directory.
-     */
-    public boolean isDirectory() {
-        if (permissions == null)
-            return false;
-
-        return ((permissions.intValue() & 0040000) != 0);
-    }
-
-    /**
-     * Checks if this entry is a regular file.
-     *
-     * @return Returns true if permissions are available and they indicate
-     *         that this entry represents a regular file.
-     */
-    public boolean isRegularFile() {
-        if (permissions == null)
-            return false;
-
-        return ((permissions.intValue() & 0100000) != 0);
-    }
-
-    /**
-     * Checks if this entry is a a symlink.
-     *
-     * @return Returns true if permissions are available and they indicate
-     *         that this entry represents a symlink.
-     */
-    public boolean isSymlink() {
-        if (permissions == null)
-            return false;
-
-        return ((permissions.intValue() & 0120000) != 0);
-    }
-
-    /**
-     * Turn the POSIX permissions into a 7 digit octal representation.
-     * Note: the returned value is first masked with <code>0177777</code>.
-     *
-     * @return <code>NULL</code> if permissions are not available.
-     */
-    public String getOctalPermissions() {
-        if (permissions == null)
-            return null;
-
-        String res = Integer.toString(permissions.intValue() & 0177777, 8);
-        StringBuffer sb = new StringBuffer();
-        int leadingZeros = 7 - res.length();
-
-        while (leadingZeros > 0) {
-            sb.append('0');
-            leadingZeros--;
-        }
-
-        sb.append(res);
-        return sb.toString();
-    }
-}
--- a/src/com/trilead/ssh2/SFTPv3FileHandle.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>SFTPv3FileHandle</code>.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SFTPv3FileHandle.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class SFTPv3FileHandle {
-    final SFTPv3Client client;
-    final byte[] fileHandle;
-    boolean isClosed = false;
-
-    /* The constructor is NOT public */
-
-    SFTPv3FileHandle(SFTPv3Client client, byte[] h) {
-        this.client = client;
-        this.fileHandle = h;
-    }
-
-    /**
-     * Get the SFTPv3Client instance which created this handle.
-     *
-     * @return A SFTPv3Client instance.
-     */
-    public SFTPv3Client getClient() {
-        return client;
-    }
-
-    /**
-     * Check if this handle was closed with the {@link SFTPv3Client#closeFile(SFTPv3FileHandle)} method
-     * of the <code>SFTPv3Client</code> instance which created the handle.
-     *
-     * @return if the handle is closed.
-     */
-    public boolean isClosed() {
-        return isClosed;
-    }
-}
--- a/src/com/trilead/ssh2/ServerHostKeyVerifier.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-
-package com.trilead.ssh2;
-
-/**
- * A callback interface used to implement a client specific method of checking
- * server host keys.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ServerHostKeyVerifier.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public interface ServerHostKeyVerifier {
-    /**
-     * The actual verifier method, it will be called by the key exchange code
-     * on EVERY key exchange - this can happen several times during the lifetime
-     * of a connection.
-     * <p>
-     * Note: SSH-2 servers are allowed to change their hostkey at ANY time.
-     *
-     * @param hostname the hostname used to create the {@link Connection} object
-     * @param port the remote TCP port
-     * @param serverHostKeyAlgorithm the public key algorithm (<code>ssh-rsa</code> or <code>ssh-dss</code>)
-     * @param serverHostKey the server's public key blob
-     * @return if the client wants to accept the server's host key - if not, the
-     *         connection will be closed.
-     * @throws Exception Will be wrapped with an IOException, extended version of returning false =)
-     */
-    public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey)
-    throws Exception;
-}
--- a/src/com/trilead/ssh2/Session.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,491 +0,0 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.channel.Channel;
-import com.trilead.ssh2.channel.ChannelManager;
-import com.trilead.ssh2.channel.X11ServerData;
-
-
-/**
- * A <code>Session</code> is a remote execution of a program. "Program" means
- * in this context either a shell, an application or a system command. The
- * program may or may not have a tty. Only one single program can be started on
- * a session. However, multiple sessions can be active simultaneously.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Session.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
- */
-public class Session {
-    ChannelManager cm;
-    Channel cn;
-
-    boolean flag_pty_requested = false;
-    boolean flag_x11_requested = false;
-    boolean flag_execution_started = false;
-    boolean flag_closed = false;
-
-    String x11FakeCookie = null;
-
-    final SecureRandom rnd;
-
-    Session(ChannelManager cm, SecureRandom rnd) throws IOException {
-        this.cm = cm;
-        this.cn = cm.openSessionChannel();
-        this.rnd = rnd;
-    }
-
-    /**
-     * Basically just a wrapper for lazy people - identical to calling
-     * <code>requestPTY("dumb", 0, 0, 0, 0, null)</code>.
-     *
-     * @throws IOException
-     */
-    public void requestDumbPTY() throws IOException {
-        requestPTY("dumb", 0, 0, 0, 0, null);
-    }
-
-    /**
-     * Basically just another wrapper for lazy people - identical to calling
-     * <code>requestPTY(term, 0, 0, 0, 0, null)</code>.
-     *
-     * @throws IOException
-     */
-    public void requestPTY(String term) throws IOException {
-        requestPTY(term, 0, 0, 0, 0, null);
-    }
-
-    /**
-     * Allocate a pseudo-terminal for this session.
-     * <p>
-     * This method may only be called before a program or shell is started in
-     * this session.
-     * <p>
-     * Different aspects can be specified:
-     * <p>
-     * <ul>
-     * <li>The TERM environment variable value (e.g., vt100)</li>
-     * <li>The terminal's dimensions.</li>
-     * <li>The encoded terminal modes.</li>
-     * </ul>
-     * Zero dimension parameters are ignored. The character/row dimensions
-     * override the pixel dimensions (when nonzero). Pixel dimensions refer to
-     * the drawable area of the window. The dimension parameters are only
-     * informational. The encoding of terminal modes (parameter
-     * <code>terminal_modes</code>) is described in RFC4254.
-     *
-     * @param term
-     *            The TERM environment variable value (e.g., vt100)
-     * @param term_width_characters
-     *            terminal width, characters (e.g., 80)
-     * @param term_height_characters
-     *            terminal height, rows (e.g., 24)
-     * @param term_width_pixels
-     *            terminal width, pixels (e.g., 640)
-     * @param term_height_pixels
-     *            terminal height, pixels (e.g., 480)
-     * @param terminal_modes
-     *            encoded terminal modes (may be <code>null</code>)
-     * @throws IOException
-     */
-    public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels,
-                           int term_height_pixels, byte[] terminal_modes) throws IOException {
-        if (term == null)
-            throw new IllegalArgumentException("TERM cannot be null.");
-
-        if ((terminal_modes != null) && (terminal_modes.length > 0)) {
-            if (terminal_modes[terminal_modes.length - 1] != 0)
-                throw new IOException("Illegal terminal modes description, does not end in zero byte");
-        }
-        else
-            terminal_modes = new byte[] { 0 };
-
-        synchronized (this) {
-            /* The following is just a nicer error, we would catch it anyway later in the channel code */
-            if (flag_closed)
-                throw new IOException("This session is closed.");
-
-            if (flag_pty_requested)
-                throw new IOException("A PTY was already requested.");
-
-            if (flag_execution_started)
-                throw new IOException(
-                    "Cannot request PTY at this stage anymore, a remote execution has already started.");
-
-            flag_pty_requested = true;
-        }
-
-        cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels,
-                      terminal_modes);
-    }
-
-    /**
-     * Inform other side of connection that our PTY has resized.
-     * <p>
-     * Zero dimension parameters are ignored. The character/row dimensions
-     * override the pixel dimensions (when nonzero). Pixel dimensions refer to
-     * the drawable area of the window. The dimension parameters are only
-     * informational.
-     *
-     * @param term_width_characters
-     *            terminal width, characters (e.g., 80)
-     * @param term_height_characters
-     *            terminal height, rows (e.g., 24)
-     * @param term_width_pixels
-     *            terminal width, pixels (e.g., 640)
-     * @param term_height_pixels
-     *            terminal height, pixels (e.g., 480)
-     * @throws IOException
-     */
-    public void resizePTY(int term_width_characters, int term_height_characters, int term_width_pixels,
-                          int term_height_pixels) throws IOException {
-        synchronized (this) {
-            /* The following is just a nicer error, we would catch it anyway later in the channel code */
-            if (flag_closed)
-                throw new IOException("This session is closed.");
-        }
-
-        cm.resizePTY(cn, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels);
-    }
-
-    /**
-     * Request X11 forwarding for the current session.
-     * <p>
-     * You have to supply the name and port of your X-server.
-     * <p>
-     * This method may only be called before a program or shell is started in
-     * this session.
-     *
-     * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1)
-     * @param port the port of the real (target) X11 server (e.g., 6010)
-     * @param cookie if non-null, then present this cookie to the real X11 server
-     * @param singleConnection if true, then the server is instructed to only forward one single
-     *        connection, no more connections shall be forwarded after first, or after the session
-     *        channel has been closed
-     * @throws IOException
-     */
-    public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection)
-    throws IOException {
-        if (hostname == null)
-            throw new IllegalArgumentException("hostname argument may not be null");
-
-        synchronized (this) {
-            /* The following is just a nicer error, we would catch it anyway later in the channel code */
-            if (flag_closed)
-                throw new IOException("This session is closed.");
-
-            if (flag_x11_requested)
-                throw new IOException("X11 forwarding was already requested.");
-
-            if (flag_execution_started)
-                throw new IOException(
-                    "Cannot request X11 forwarding at this stage anymore, a remote execution has already started.");
-
-            flag_x11_requested = true;
-        }
-
-        /* X11ServerData - used to store data about the target X11 server */
-        X11ServerData x11data = new X11ServerData();
-        x11data.hostname = hostname;
-        x11data.port = port;
-        x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */
-        /* Generate fake cookie - this one is used between remote clients and our proxy */
-        byte[] fakeCookie = new byte[16];
-        String hexEncodedFakeCookie;
-
-        /* Make sure that this fake cookie is unique for this connection */
-
-        while (true) {
-            rnd.nextBytes(fakeCookie);
-            /* Generate also hex representation of fake cookie */
-            StringBuffer tmp = new StringBuffer(32);
-
-            for (int i = 0; i < fakeCookie.length; i++) {
-                String digit2 = Integer.toHexString(fakeCookie[i] & 0xff);
-                tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
-            }
-
-            hexEncodedFakeCookie = tmp.toString();
-
-            /* Well, yes, chances are low, but we want to be on the safe side */
-
-            if (cm.checkX11Cookie(hexEncodedFakeCookie) == null)
-                break;
-        }
-
-        /* Ask for X11 forwarding */
-        cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0);
-
-        /* OK, that went fine, get ready to accept X11 connections... */
-        /* ... but only if the user has not called close() in the meantime =) */
-
-        synchronized (this) {
-            if (flag_closed == false) {
-                this.x11FakeCookie = hexEncodedFakeCookie;
-                cm.registerX11Cookie(hexEncodedFakeCookie, x11data);
-            }
-        }
-
-        /* Now it is safe to start remote X11 programs */
-    }
-
-    /**
-     * Execute a command on the remote machine.
-     *
-     * @param cmd
-     *            The command to execute on the remote host.
-     * @throws IOException
-     */
-    public void execCommand(String cmd) throws IOException {
-        if (cmd == null)
-            throw new IllegalArgumentException("cmd argument may not be null");
-
-        synchronized (this) {
-            /* The following is just a nicer error, we would catch it anyway later in the channel code */
-            if (flag_closed)
-                throw new IOException("This session is closed.");
-
-            if (flag_execution_started)
-                throw new IOException("A remote execution has already started.");
-
-            flag_execution_started = true;
-        }
-
-        cm.requestExecCommand(cn, cmd);
-    }
-
-    /**
-     * Start a shell on the remote machine.
-     *
-     * @throws IOException
-     */
-    public void startShell() throws IOException {
-        synchronized (this) {
-            /* The following is just a nicer error, we would catch it anyway later in the channel code */
-            if (flag_closed)
-                throw new IOException("This session is closed.");
-
-            if (flag_execution_started)
-                throw new IOException("A remote execution has already started.");
-
-            flag_execution_started = true;
-        }
-
-        cm.requestShell(cn);
-    }
-
-    /**
-     * Start a subsystem on the remote machine.
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param name the name of the subsystem.
-     * @throws IOException
-     */
-    public void startSubSystem(String name) throws IOException {
-        if (name == null)
-            throw new IllegalArgumentException("name argument may not be null");
-
-        synchronized (this) {
-            /* The following is just a nicer error, we would catch it anyway later in the channel code */
-            if (flag_closed)
-                throw new IOException("This session is closed.");
-
-            if (flag_execution_started)
-                throw new IOException("A remote execution has already started.");
-
-            flag_execution_started = true;
-        }
-
-        cm.requestSubSystem(cn, name);
-    }
-
-    /**
-     * This method can be used to perform end-to-end session (i.e., SSH channel)
-     * testing. It sends a 'ping' message to the server and waits for the 'pong'
-     * from the server.
-     * <p>
-     * Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request
-     * ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply
-     * packet.
-     *
-     * @throws IOException in case of any problem or when the session is closed
-     */
-    public void ping() throws IOException {
-        synchronized (this) {
-            /*
-             * The following is just a nicer error, we would catch it anyway
-             * later in the channel code
-             */
-            if (flag_closed)
-                throw new IOException("This session is closed.");
-        }
-
-        cm.requestChannelTrileadPing(cn);
-    }
-
-    /**
-     * Request authentication agent forwarding.
-     * @param agent object that implements the callbacks
-     *
-     * @throws IOException in case of any problem or when the session is closed
-     */
-
-    public synchronized boolean requestAuthAgentForwarding(AuthAgentCallback agent) throws IOException {
-        synchronized (this) {
-            /*
-             * The following is just a nicer error, we would catch it anyway
-             * later in the channel code
-             */
-            if (flag_closed)
-                throw new IOException("This session is closed.");
-        }
-
-        return cm.requestChannelAgentForwarding(cn, agent);
-    }
-
-    public InputStream getStdout() {
-        return cn.getStdoutStream();
-    }
-
-    public InputStream getStderr() {
-        return cn.getStderrStream();
-    }
-
-    public OutputStream getStdin() {
-        return cn.getStdinStream();
-    }
-
-    /**
-     * This method blocks until there is more data available on either the
-     * stdout or stderr InputStream of this <code>Session</code>. Very useful
-     * if you do not want to use two parallel threads for reading from the two
-     * InputStreams. One can also specify a timeout. NOTE: do NOT call this
-     * method if you use concurrent threads that operate on either of the two
-     * InputStreams of this <code>Session</code> (otherwise this method may
-     * block, even though more data is available).
-     *
-     * @param timeout
-     *            The (non-negative) timeout in <code>ms</code>. <code>0</code> means no
-     *            timeout, the call may block forever.
-     * @return
-     *            <ul>
-     *            <li><code>0</code> if no more data will arrive.</li>
-     *            <li><code>1</code> if more data is available.</li>
-     *            <li><code>-1</code> if a timeout occurred.</li>
-     *            </ul>
-     *
-     * @throws    IOException
-     * @deprecated This method has been replaced with a much more powerful wait-for-condition
-     *             interface and therefore acts only as a wrapper.
-     *
-     */
-    public int waitUntilDataAvailable(long timeout) throws IOException {
-        if (timeout < 0)
-            throw new IllegalArgumentException("timeout must not be negative!");
-
-        int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA
-                                             | ChannelCondition.EOF);
-
-        if ((conditions & ChannelCondition.TIMEOUT) != 0)
-            return -1;
-
-        if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0)
-            return 1;
-
-        /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */
-
-        if ((conditions & ChannelCondition.EOF) != 0)
-            return 0;
-
-        throw new IllegalStateException("Unexpected condition result (" + conditions + ")");
-    }
-
-    /**
-     * This method blocks until certain conditions hold true on the underlying SSH-2 channel.
-     * <p>
-     * This method returns as soon as one of the following happens:
-     * <ul>
-     * <li>at least of the specified conditions (see {@link ChannelCondition}) holds true</li>
-     * <li>timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions)</a>
-     * <li>the underlying channel was closed (CLOSED will be set in result conditions)</a>
-     * </ul>
-     * <p>
-     * In any case, the result value contains ALL current conditions, which may be more
-     * than the specified condition set (i.e., never use the "==" operator to test for conditions
-     * in the bitmask, see also comments in {@link ChannelCondition}).
-     * <p>
-     * Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and
-     * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two
-     * InputStreams of this <code>Session</code> (otherwise this method may
-     * block, even though more data is available in the StreamGobblers).
-     *
-     * @param condition_set a bitmask based on {@link ChannelCondition} values
-     * @param timeout non-negative timeout in ms, <code>0</code> means no timeout
-     * @return all bitmask specifying all current conditions that are true
-     */
-
-    public int waitForCondition(int condition_set, long timeout) {
-        if (timeout < 0)
-            throw new IllegalArgumentException("timeout must be non-negative!");
-
-        return cm.waitForCondition(cn, timeout, condition_set);
-    }
-
-    /**
-     * Get the exit code/status from the remote command - if available. Be
-     * careful - not all server implementations return this value. It is
-     * generally a good idea to call this method only when all data from the
-     * remote side has been consumed (see also the <code<WaitForCondition</code> method).
-     *
-     * @return An <code>Integer</code> holding the exit code, or
-     *         <code>null</code> if no exit code is (yet) available.
-     */
-    public Integer getExitStatus() {
-        return cn.getExitStatus();
-    }
-
-    /**
-     * Get the name of the signal by which the process on the remote side was
-     * stopped - if available and applicable. Be careful - not all server
-     * implementations return this value.
-     *
-     * @return An <code>String</code> holding the name of the signal, or
-     *         <code>null</code> if the process exited normally or is still
-     *         running (or if the server forgot to send this information).
-     */
-    public String getExitSignal() {
-        return cn.getExitSignal();
-    }
-
-    /**
-     * Close this session. NEVER forget to call this method to free up resources -
-     * even if you got an exception from one of the other methods (or when
-     * getting an Exception on the Input- or OutputStreams). Sometimes these other
-     * methods may throw an exception, saying that the underlying channel is
-     * closed (this can happen, e.g., if the other server sent a close message.)
-     * However, as long as you have not called the <code>close()</code>
-     * method, you may be wasting (local) resources.
-     *
-     */
-    public void close() {
-        synchronized (this) {
-            if (flag_closed)
-                return;
-
-            flag_closed = true;
-
-            if (x11FakeCookie != null)
-                cm.unRegisterX11Cookie(x11FakeCookie, true);
-
-            try {
-                cm.closeChannel(cn, "Closed due to user request", true);
-            }
-            catch (IOException ignored) {
-            }
-        }
-    }
-}
--- a/src/com/trilead/ssh2/StreamGobbler.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * A <code>StreamGobbler</code> is an InputStream that uses an internal worker
- * thread to constantly consume input from another InputStream. It uses a buffer
- * to store the consumed data. The buffer size is automatically adjusted, if needed.
- * <p>
- * This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR
- * InputStreams with instances of this class, then you don't have to bother about
- * the shared window of STDOUT and STDERR in the low level SSH-2 protocol,
- * since all arriving data will be immediatelly consumed by the worker threads.
- * Also, as a side effect, the streams will be buffered (e.g., single byte
- * read() operations are faster).
- * <p>
- * Other SSH for Java libraries include this functionality by default in
- * their STDOUT and STDERR InputStream implementations, however, please be aware
- * that this approach has also a downside:
- * <p>
- * If you do not call the StreamGobbler's <code>read()</code> method often enough
- * and the peer is constantly sending huge amounts of data, then you will sooner or later
- * encounter a low memory situation due to the aggregated data (well, it also depends on the Java heap size).
- * Joe Average will like this class anyway - a paranoid programmer would never use such an approach.
- * <p>
- * The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't",
- * see http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: StreamGobbler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class StreamGobbler extends InputStream {
-    class GobblerThread extends Thread {
-        public void run() {
-            byte[] buff = new byte[8192];
-
-            while (true) {
-                try {
-                    int avail = is.read(buff);
-
-                    synchronized (synchronizer) {
-                        if (avail <= 0) {
-                            isEOF = true;
-                            synchronizer.notifyAll();
-                            break;
-                        }
-
-                        int space_available = buffer.length - write_pos;
-
-                        if (space_available < avail) {
-                            /* compact/resize buffer */
-                            int unread_size = write_pos - read_pos;
-                            int need_space = unread_size + avail;
-                            byte[] new_buffer = buffer;
-
-                            if (need_space > buffer.length) {
-                                int inc = need_space / 3;
-                                inc = (inc < 256) ? 256 : inc;
-                                inc = (inc > 8192) ? 8192 : inc;
-                                new_buffer = new byte[need_space + inc];
-                            }
-
-                            if (unread_size > 0)
-                                System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size);
-
-                            buffer = new_buffer;
-                            read_pos = 0;
-                            write_pos = unread_size;
-                        }
-
-                        System.arraycopy(buff, 0, buffer, write_pos, avail);
-                        write_pos += avail;
-                        synchronizer.notifyAll();
-                    }
-                }
-                catch (IOException e) {
-                    synchronized (synchronizer) {
-                        exception = e;
-                        synchronizer.notifyAll();
-                        break;
-                    }
-                }
-            }
-        }
-    }
-
-    private InputStream is;
-    private GobblerThread t;
-
-    private Object synchronizer = new Object();
-
-    private boolean isEOF = false;
-    private boolean isClosed = false;
-    private IOException exception = null;
-
-    private byte[] buffer = new byte[2048];
-    private int read_pos = 0;
-    private int write_pos = 0;
-
-    public StreamGobbler(InputStream is) {
-        this.is = is;
-        t = new GobblerThread();
-        t.setDaemon(true);
-        t.start();
-    }
-
-    public int read() throws IOException {
-        synchronized (synchronizer) {
-            if (isClosed)
-                throw new IOException("This StreamGobbler is closed.");
-
-            while (read_pos == write_pos) {
-                if (exception != null)
-                    throw exception;
-
-                if (isEOF)
-                    return -1;
-
-                try {
-                    synchronizer.wait();
-                }
-                catch (InterruptedException e) {
-                }
-            }
-
-            int b = buffer[read_pos++] & 0xff;
-            return b;
-        }
-    }
-
-    public int available() throws IOException {
-        synchronized (synchronizer) {
-            if (isClosed)
-                throw new IOException("This StreamGobbler is closed.");
-
-            return write_pos - read_pos;
-        }
-    }
-
-    public int read(byte[] b) throws IOException {
-        return read(b, 0, b.length);
-    }
-
-    public void close() throws IOException {
-        synchronized (synchronizer) {
-            if (isClosed)
-                return;
-
-            isClosed = true;
-            isEOF = true;
-            synchronizer.notifyAll();
-            is.close();
-        }
-    }
-
-    public int read(byte[] b, int off, int len) throws IOException {
-        if (b == null)
-            throw new NullPointerException();
-
-        if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
-            throw new IndexOutOfBoundsException();
-
-        if (len == 0)
-            return 0;
-
-        synchronized (synchronizer) {
-            if (isClosed)
-                throw new IOException("This StreamGobbler is closed.");
-
-            while (read_pos == write_pos) {
-                if (exception != null)
-                    throw exception;
-
-                if (isEOF)
-                    return -1;
-
-                try {
-                    synchronizer.wait();
-                }
-                catch (InterruptedException e) {
-                }
-            }
-
-            int avail = write_pos - read_pos;
-            avail = (avail > len) ? len : avail;
-            System.arraycopy(buffer, read_pos, b, off, avail);
-            read_pos += avail;
-            return avail;
-        }
-    }
-}
--- a/src/com/trilead/ssh2/auth/AuthenticationManager.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,378 +0,0 @@
-
-package com.trilead.ssh2.auth;
-
-import java.io.IOException;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.SecureRandom;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.util.Vector;
-
-import com.trilead.ssh2.InteractiveCallback;
-import com.trilead.ssh2.crypto.PEMDecoder;
-import com.trilead.ssh2.packets.PacketServiceAccept;
-import com.trilead.ssh2.packets.PacketServiceRequest;
-import com.trilead.ssh2.packets.PacketUserauthBanner;
-import com.trilead.ssh2.packets.PacketUserauthFailure;
-import com.trilead.ssh2.packets.PacketUserauthInfoRequest;
-import com.trilead.ssh2.packets.PacketUserauthInfoResponse;
-import com.trilead.ssh2.packets.PacketUserauthRequestInteractive;
-import com.trilead.ssh2.packets.PacketUserauthRequestNone;
-import com.trilead.ssh2.packets.PacketUserauthRequestPassword;
-import com.trilead.ssh2.packets.PacketUserauthRequestPublicKey;
-import com.trilead.ssh2.packets.Packets;
-import com.trilead.ssh2.packets.TypesWriter;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
-import com.trilead.ssh2.signature.RSASHA1Verify;
-import com.trilead.ssh2.transport.MessageHandler;
-import com.trilead.ssh2.transport.TransportManager;
-
-
-/**
- * AuthenticationManager.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class AuthenticationManager implements MessageHandler {
-    TransportManager tm;
-
-    Vector packets = new Vector();
-    boolean connectionClosed = false;
-
-    String banner;
-
-    String[] remainingMethods = new String[0];
-    boolean isPartialSuccess = false;
-
-    boolean authenticated = false;
-    boolean initDone = false;
-
-    public AuthenticationManager(TransportManager tm) {
-        this.tm = tm;
-    }
-
-    boolean methodPossible(String methName) {
-        if (remainingMethods == null)
-            return false;
-
-        for (int i = 0; i < remainingMethods.length; i++) {
-            if (remainingMethods[i].compareTo(methName) == 0)
-                return true;
-        }
-
-        return false;
-    }
-
-    byte[] deQueue() throws IOException {
-        synchronized (packets) {
-            while (packets.size() == 0) {
-                if (connectionClosed)
-                    throw(IOException) new IOException("The connection is closed.").initCause(tm
-                            .getReasonClosedCause());
-
-                try {
-                    packets.wait();
-                }
-                catch (InterruptedException ign) {
-                }
-            }
-
-            /* This sequence works with J2ME */
-            byte[] res = (byte[]) packets.firstElement();
-            packets.removeElementAt(0);
-            return res;
-        }
-    }
-
-    byte[] getNextMessage() throws IOException {
-        while (true) {
-            byte[] msg = deQueue();
-
-            if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
-                return msg;
-
-            PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);
-            banner = sb.getBanner();
-        }
-    }
-
-    public String[] getRemainingMethods(String user) throws IOException {
-        initialize(user);
-        return remainingMethods;
-    }
-
-    public boolean getPartialSuccess() {
-        return isPartialSuccess;
-    }
-
-    private boolean initialize(String user) throws IOException {
-        if (initDone == false) {
-            tm.registerMessageHandler(this, 0, 255);
-            PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
-            tm.sendMessage(sr.getPayload());
-            PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
-            tm.sendMessage(urn.getPayload());
-            byte[] msg = getNextMessage();
-            new PacketServiceAccept(msg, 0, msg.length);
-            msg = getNextMessage();
-            initDone = true;
-
-            if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) {
-                authenticated = true;
-                tm.removeMessageHandler(this, 0, 255);
-                return true;
-            }
-
-            if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE) {
-                PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);
-                remainingMethods = puf.getAuthThatCanContinue();
-                isPartialSuccess = puf.isPartialSuccess();
-                return false;
-            }
-
-            throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
-        }
-
-        return authenticated;
-    }
-
-    public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
-    throws IOException {
-        KeyPair pair = PEMDecoder.decode(PEMPrivateKey, password);
-        return authenticatePublicKey(user, pair, rnd);
-    }
-
-    public boolean authenticatePublicKey(String user, KeyPair pair, SecureRandom rnd)
-    throws IOException {
-        PrivateKey key = pair.getPrivate();
-
-        try {
-            initialize(user);
-
-            if (methodPossible("publickey") == false)
-                throw new IOException("Authentication method publickey not supported by the server at this stage.");
-
-            if (key instanceof DSAPrivateKey) {
-                DSAPrivateKey pk = (DSAPrivateKey) key;
-                byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey) pair.getPublic());
-                TypesWriter tw = new TypesWriter();
-                byte[] H = tm.getSessionIdentifier();
-                tw.writeString(H, 0, H.length);
-                tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
-                tw.writeString(user);
-                tw.writeString("ssh-connection");
-                tw.writeString("publickey");
-                tw.writeBoolean(true);
-                tw.writeString("ssh-dss");
-                tw.writeString(pk_enc, 0, pk_enc.length);
-                byte[] msg = tw.getBytes();
-                byte[] ds = DSASHA1Verify.generateSignature(msg, pk, rnd);
-                byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);
-                PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
-                        "ssh-dss", pk_enc, ds_enc);
-                tm.sendMessage(ua.getPayload());
-            }
-            else if (key instanceof RSAPrivateKey) {
-                RSAPrivateKey pk = (RSAPrivateKey) key;
-                byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey((RSAPublicKey) pair.getPublic());
-                TypesWriter tw = new TypesWriter();
-                {
-                    byte[] H = tm.getSessionIdentifier();
-                    tw.writeString(H, 0, H.length);
-                    tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
-                    tw.writeString(user);
-                    tw.writeString("ssh-connection");
-                    tw.writeString("publickey");
-                    tw.writeBoolean(true);
-                    tw.writeString("ssh-rsa");
-                    tw.writeString(pk_enc, 0, pk_enc.length);
-                }
-                byte[] msg = tw.getBytes();
-                byte[] ds = RSASHA1Verify.generateSignature(msg, pk);
-                byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
-                PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
-                        "ssh-rsa", pk_enc, rsa_sig_enc);
-                tm.sendMessage(ua.getPayload());
-            }
-            else if (key instanceof ECPrivateKey) {
-                ECPrivateKey pk = (ECPrivateKey) key;
-                final String algo = ECDSASHA2Verify.ECDSA_SHA2_PREFIX
-                                    + ECDSASHA2Verify.getCurveName(pk.getParams());
-                byte[] pk_enc = ECDSASHA2Verify.encodeSSHECDSAPublicKey((ECPublicKey) pair.getPublic());
-                TypesWriter tw = new TypesWriter();
-                {
-                    byte[] H = tm.getSessionIdentifier();
-                    tw.writeString(H, 0, H.length);
-                    tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
-                    tw.writeString(user);
-                    tw.writeString("ssh-connection");
-                    tw.writeString("publickey");
-                    tw.writeBoolean(true);
-                    tw.writeString(algo);
-                    tw.writeString(pk_enc, 0, pk_enc.length);
-                }
-                byte[] msg = tw.getBytes();
-                byte[] ds = ECDSASHA2Verify.generateSignature(msg, pk);
-                byte[] ec_sig_enc = ECDSASHA2Verify.encodeSSHECDSASignature(ds, pk.getParams());
-                PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
-                        algo, pk_enc, ec_sig_enc);
-                tm.sendMessage(ua.getPayload());
-            }
-            else {
-                throw new IOException("Unknown private key type returned by the PEM decoder.");
-            }
-
-            byte[] ar = getNextMessage();
-
-            if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) {
-                authenticated = true;
-                tm.removeMessageHandler(this, 0, 255);
-                return true;
-            }
-
-            if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) {
-                PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
-                remainingMethods = puf.getAuthThatCanContinue();
-                isPartialSuccess = puf.isPartialSuccess();
-                return false;
-            }
-
-            throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
-        }
-        catch (IOException e) {
-            e.printStackTrace();
-            tm.close(e, false);
-            throw(IOException) new IOException("Publickey authentication failed.").initCause(e);
-        }
-    }
-
-    public boolean authenticateNone(String user) throws IOException {
-        try {
-            initialize(user);
-            return authenticated;
-        }
-        catch (IOException e) {
-            tm.close(e, false);
-            throw(IOException) new IOException("None authentication failed.").initCause(e);
-        }
-    }
-
-    public boolean authenticatePassword(String user, String pass) throws IOException {
-        try {
-            initialize(user);
-
-            if (methodPossible("password") == false)
-                throw new IOException("Authentication method password not supported by the server at this stage.");
-
-            PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
-            tm.sendMessage(ua.getPayload());
-            byte[] ar = getNextMessage();
-
-            if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) {
-                authenticated = true;
-                tm.removeMessageHandler(this, 0, 255);
-                return true;
-            }
-
-            if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) {
-                PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
-                remainingMethods = puf.getAuthThatCanContinue();
-                isPartialSuccess = puf.isPartialSuccess();
-                return false;
-            }
-
-            throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
-        }
-        catch (IOException e) {
-            tm.close(e, false);
-            throw(IOException) new IOException("Password authentication failed.").initCause(e);
-        }
-    }
-
-    public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException {
-        try {
-            initialize(user);
-
-            if (methodPossible("keyboard-interactive") == false)
-                throw new IOException(
-                    "Authentication method keyboard-interactive not supported by the server at this stage.");
-
-            if (submethods == null)
-                submethods = new String[0];
-
-            PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
-                    submethods);
-            tm.sendMessage(ua.getPayload());
-
-            while (true) {
-                byte[] ar = getNextMessage();
-
-                if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) {
-                    authenticated = true;
-                    tm.removeMessageHandler(this, 0, 255);
-                    return true;
-                }
-
-                if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) {
-                    PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
-                    remainingMethods = puf.getAuthThatCanContinue();
-                    isPartialSuccess = puf.isPartialSuccess();
-                    return false;
-                }
-
-                if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST) {
-                    PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);
-                    String[] responses;
-
-                    try {
-                        responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
-                                                        .getPrompt(), pui.getEcho());
-                    }
-                    catch (Exception e) {
-                        throw(IOException) new IOException("Exception in callback.").initCause(e);
-                    }
-
-                    if (responses == null)
-                        throw new IOException("Your callback may not return NULL!");
-
-                    PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
-                    tm.sendMessage(puir.getPayload());
-                    continue;
-                }
-
-                throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
-            }
-        }
-        catch (IOException e) {
-            tm.close(e, false);
-            throw(IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
-        }
-    }
-
-    public void handleMessage(byte[] msg, int msglen) throws IOException {
-        synchronized (packets) {
-            if (msg == null) {
-                connectionClosed = true;
-            }
-            else {
-                byte[] tmp = new byte[msglen];
-                System.arraycopy(msg, 0, tmp, 0, msglen);
-                packets.addElement(tmp);
-            }
-
-            packets.notifyAll();
-
-            if (packets.size() > 5) {
-                connectionClosed = true;
-                throw new IOException("Error, peer is flooding us with authentication packets.");
-            }
-        }
-    }
-}
--- a/src/com/trilead/ssh2/channel/AuthAgentForwardThread.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,542 +0,0 @@
-/*
- * ConnectBot: simple, powerful, open-source SSH client for Android
- * Copyright 2007 Kenny Root, Jeffrey Sharkey
- *
- * Licensed 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 com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import com.trilead.ssh2.AuthAgentCallback;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
-import com.trilead.ssh2.signature.RSASHA1Verify;
-
-/**
- * AuthAgentForwardThread.
- *
- * @author Kenny Root
- * @version $Id$
- */
-public class AuthAgentForwardThread extends Thread implements IChannelWorkerThread {
-    private static final byte[] SSH_AGENT_FAILURE = {0, 0, 0, 1, 5}; // 5
-    private static final byte[] SSH_AGENT_SUCCESS = {0, 0, 0, 1, 6}; // 6
-
-    private static final int SSH2_AGENTC_REQUEST_IDENTITIES = 11;
-    private static final int SSH2_AGENT_IDENTITIES_ANSWER = 12;
-
-    private static final int SSH2_AGENTC_SIGN_REQUEST = 13;
-    private static final int SSH2_AGENT_SIGN_RESPONSE = 14;
-
-    private static final int SSH2_AGENTC_ADD_IDENTITY = 17;
-    private static final int SSH2_AGENTC_REMOVE_IDENTITY = 18;
-    private static final int SSH2_AGENTC_REMOVE_ALL_IDENTITIES = 19;
-
-//  private static final int SSH_AGENTC_ADD_SMARTCARD_KEY = 20;
-//  private static final int SSH_AGENTC_REMOVE_SMARTCARD_KEY = 21;
-
-    private static final int SSH_AGENTC_LOCK = 22;
-    private static final int SSH_AGENTC_UNLOCK = 23;
-
-    private static final int SSH2_AGENTC_ADD_ID_CONSTRAINED = 25;
-//  private static final int SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED = 26;
-
-    // Constraints for adding keys
-    private static final int SSH_AGENT_CONSTRAIN_LIFETIME = 1;
-    private static final int SSH_AGENT_CONSTRAIN_CONFIRM = 2;
-
-    // Flags for signature requests
-//  private static final int SSH_AGENT_OLD_SIGNATURE = 1;
-
-    private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
-
-    AuthAgentCallback authAgent;
-    OutputStream os;
-    InputStream is;
-    Channel c;
-
-    byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
-
-    public AuthAgentForwardThread(Channel c, AuthAgentCallback authAgent) {
-        this.c = c;
-        this.authAgent = authAgent;
-
-        if (log.isEnabled())
-            log.log(20, "AuthAgentForwardThread started");
-    }
-
-    @Override
-    public void run() {
-        try {
-            c.cm.registerThread(this);
-        }
-        catch (IOException e) {
-            stopWorking();
-            return;
-        }
-
-        try {
-            c.cm.sendOpenConfirmation(c);
-            is = c.getStdoutStream();
-            os = c.getStdinStream();
-            int totalSize = 4;
-            int readSoFar = 0;
-
-            while (true) {
-                int len;
-
-                try {
-                    len = is.read(buffer, readSoFar, buffer.length - readSoFar);
-                }
-                catch (IOException e) {
-                    stopWorking();
-                    return;
-                }
-
-                if (len <= 0)
-                    break;
-
-                readSoFar += len;
-
-                if (readSoFar >= 4) {
-                    TypesReader tr = new TypesReader(buffer, 0, 4);
-                    totalSize = tr.readUINT32() + 4;
-                }
-
-                if (totalSize == readSoFar) {
-                    TypesReader tr = new TypesReader(buffer, 4, readSoFar - 4);
-                    int messageType = tr.readByte();
-
-                    switch (messageType) {
-                        case SSH2_AGENTC_REQUEST_IDENTITIES:
-                            sendIdentities();
-                            break;
-
-                        case SSH2_AGENTC_ADD_IDENTITY:
-                            addIdentity(tr, false);
-                            break;
-
-                        case SSH2_AGENTC_ADD_ID_CONSTRAINED:
-                            addIdentity(tr, true);
-                            break;
-
-                        case SSH2_AGENTC_REMOVE_IDENTITY:
-                            removeIdentity(tr);
-                            break;
-
-                        case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
-                            removeAllIdentities(tr);
-                            break;
-
-                        case SSH2_AGENTC_SIGN_REQUEST:
-                            processSignRequest(tr);
-                            break;
-
-                        case SSH_AGENTC_LOCK:
-                            processLockRequest(tr);
-                            break;
-
-                        case SSH_AGENTC_UNLOCK:
-                            processUnlockRequest(tr);
-                            break;
-
-                        default:
-                            os.write(SSH_AGENT_FAILURE);
-                            break;
-                    }
-
-                    readSoFar = 0;
-                }
-            }
-
-            c.cm.closeChannel(c, "EOF on both streams reached.", true);
-        }
-        catch (IOException e) {
-            log.log(50, "IOException in agent forwarder: " + e.getMessage());
-
-            try {
-                is.close();
-            }
-            catch (IOException e1) {
-            }
-
-            try {
-                os.close();
-            }
-            catch (IOException e2) {
-            }
-
-            try {
-                c.cm.closeChannel(c, "IOException in agent forwarder (" + e.getMessage() + ")", true);
-            }
-            catch (IOException e3) {
-            }
-        }
-    }
-
-    public void stopWorking() {
-        try {
-            /* This will lead to an IOException in the is.read() call */
-            is.close();
-        }
-        catch (IOException e) {
-        }
-    }
-
-    /**
-     * @return whether the agent is locked
-     */
-    private boolean failWhenLocked() throws IOException {
-        if (authAgent.isAgentLocked()) {
-            os.write(SSH_AGENT_FAILURE);
-            return true;
-        }
-        else
-            return false;
-    }
-
-    private void sendIdentities() throws IOException {
-        Map<String, byte[]> keys = null;
-        TypesWriter tw = new TypesWriter();
-        tw.writeByte(SSH2_AGENT_IDENTITIES_ANSWER);
-        int numKeys = 0;
-
-        if (!authAgent.isAgentLocked())
-            keys = authAgent.retrieveIdentities();
-
-        if (keys != null)
-            numKeys = keys.size();
-
-        tw.writeUINT32(numKeys);
-
-        if (keys != null) {
-            for (Entry<String, byte[]> entry : keys.entrySet()) {
-                byte[] keyBytes = entry.getValue();
-                tw.writeString(keyBytes, 0, keyBytes.length);
-                tw.writeString(entry.getKey());
-            }
-        }
-
-        sendPacket(tw.getBytes());
-    }
-
-    /**
-     * @param tr
-     */
-    private void addIdentity(TypesReader tr, boolean checkConstraints) {
-        try {
-            if (failWhenLocked())
-                return;
-
-            String type = tr.readString();
-            String comment;
-            String keyType;
-            KeySpec pubSpec;
-            KeySpec privSpec;
-
-            if (type.equals("ssh-rsa")) {
-                keyType = "RSA";
-                BigInteger n = tr.readMPINT();
-                BigInteger e = tr.readMPINT();
-                BigInteger d = tr.readMPINT();
-                BigInteger iqmp = tr.readMPINT();
-                BigInteger p = tr.readMPINT();
-                BigInteger q = tr.readMPINT();
-                comment = tr.readString();
-                // Derive the extra values Java needs.
-                BigInteger dmp1 = d.mod(p.subtract(BigInteger.ONE));
-                BigInteger dmq1 = d.mod(q.subtract(BigInteger.ONE));
-                pubSpec = new RSAPublicKeySpec(n, e);
-                privSpec = new RSAPrivateCrtKeySpec(n, e, d, p, q, dmp1, dmq1, iqmp);
-            }
-            else if (type.equals("ssh-dss")) {
-                keyType = "DSA";
-                BigInteger p = tr.readMPINT();
-                BigInteger q = tr.readMPINT();
-                BigInteger g = tr.readMPINT();
-                BigInteger y = tr.readMPINT();
-                BigInteger x = tr.readMPINT();
-                comment = tr.readString();
-                pubSpec = new DSAPublicKeySpec(y, p, q, g);
-                privSpec = new DSAPrivateKeySpec(x, p, q, g);
-            }
-            else if (type.equals("ecdsa-sha2-nistp256")) {
-                keyType = "EC";
-                String curveName = tr.readString();
-                byte[] groupBytes = tr.readByteString();
-                BigInteger exponent = tr.readMPINT();
-                comment = tr.readString();
-
-                if (!"nistp256".equals(curveName)) {
-                    log.log(2, "Invalid curve name for ecdsa-sha2-nistp256: " + curveName);
-                    os.write(SSH_AGENT_FAILURE);
-                    return;
-                }
-
-                ECParameterSpec nistp256 = ECDSASHA2Verify.EllipticCurves.nistp256;
-                ECPoint group = ECDSASHA2Verify.decodeECPoint(groupBytes, nistp256.getCurve());
-
-                if (group == null) {
-                    // TODO log error
-                    os.write(SSH_AGENT_FAILURE);
-                    return;
-                }
-
-                pubSpec = new ECPublicKeySpec(group, nistp256);
-                privSpec = new ECPrivateKeySpec(exponent, nistp256);
-            }
-            else {
-                log.log(2, "Unknown key type: " + type);
-                os.write(SSH_AGENT_FAILURE);
-                return;
-            }
-
-            PublicKey pubKey;
-            PrivateKey privKey;
-
-            try {
-                KeyFactory kf = KeyFactory.getInstance(keyType);
-                pubKey = kf.generatePublic(pubSpec);
-                privKey = kf.generatePrivate(privSpec);
-            }
-            catch (NoSuchAlgorithmException ex) {
-                // TODO: log error
-                os.write(SSH_AGENT_FAILURE);
-                return;
-            }
-            catch (InvalidKeySpecException ex) {
-                // TODO: log error
-                os.write(SSH_AGENT_FAILURE);
-                return;
-            }
-
-            KeyPair pair = new KeyPair(pubKey, privKey);
-            boolean confirmUse = false;
-            int lifetime = 0;
-
-            if (checkConstraints) {
-                while (tr.remain() > 0) {
-                    int constraint = tr.readByte();
-
-                    if (constraint == SSH_AGENT_CONSTRAIN_CONFIRM)
-                        confirmUse = true;
-                    else if (constraint == SSH_AGENT_CONSTRAIN_LIFETIME)
-                        lifetime = tr.readUINT32();
-                    else {
-                        // Unknown constraint. Bail.
-                        os.write(SSH_AGENT_FAILURE);
-                        return;
-                    }
-                }
-            }
-
-            if (authAgent.addIdentity(pair, comment, confirmUse, lifetime))
-                os.write(SSH_AGENT_SUCCESS);
-            else
-                os.write(SSH_AGENT_FAILURE);
-        }
-        catch (IOException e) {
-            try {
-                os.write(SSH_AGENT_FAILURE);
-            }
-            catch (IOException e1) {
-            }
-        }
-    }
-
-    /**
-     * @param tr
-     */
-    private void removeIdentity(TypesReader tr) {
-        try {
-            if (failWhenLocked())
-                return;
-
-            byte[] publicKey = tr.readByteString();
-
-            if (authAgent.removeIdentity(publicKey))
-                os.write(SSH_AGENT_SUCCESS);
-            else
-                os.write(SSH_AGENT_FAILURE);
-        }
-        catch (IOException e) {
-            try {
-                os.write(SSH_AGENT_FAILURE);
-            }
-            catch (IOException e1) {
-            }
-        }
-    }
-
-    /**
-     * @param tr
-     */
-    private void removeAllIdentities(TypesReader tr) {
-        try {
-            if (failWhenLocked())
-                return;
-
-            if (authAgent.removeAllIdentities())
-                os.write(SSH_AGENT_SUCCESS);
-            else
-                os.write(SSH_AGENT_FAILURE);
-        }
-        catch (IOException e) {
-            try {
-                os.write(SSH_AGENT_FAILURE);
-            }
-            catch (IOException e1) {
-            }
-        }
-    }
-
-    private void processSignRequest(TypesReader tr) {
-        try {
-            if (failWhenLocked())
-                return;
-
-            byte[] publicKeyBytes = tr.readByteString();
-            byte[] challenge = tr.readByteString();
-            int flags = tr.readUINT32();
-
-            if (flags != 0) {
-                // We don't understand any flags; abort!
-                os.write(SSH_AGENT_FAILURE);
-                return;
-            }
-
-            KeyPair pair = authAgent.getKeyPair(publicKeyBytes);
-
-            if (pair == null) {
-                os.write(SSH_AGENT_FAILURE);
-                return;
-            }
-
-            byte[] response;
-            PrivateKey privKey = pair.getPrivate();
-
-            if (privKey instanceof RSAPrivateKey) {
-                byte[] signature = RSASHA1Verify.generateSignature(challenge,
-                                   (RSAPrivateKey) privKey);
-                response = RSASHA1Verify.encodeSSHRSASignature(signature);
-            }
-            else if (privKey instanceof DSAPrivateKey) {
-                byte[] signature = DSASHA1Verify.generateSignature(challenge,
-                                   (DSAPrivateKey) privKey, new SecureRandom());
-                response = DSASHA1Verify.encodeSSHDSASignature(signature);
-            }
-            else {
-                os.write(SSH_AGENT_FAILURE);
-                return;
-            }
-
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(SSH2_AGENT_SIGN_RESPONSE);
-            tw.writeString(response, 0, response.length);
-            sendPacket(tw.getBytes());
-        }
-        catch (IOException e) {
-            try {
-                os.write(SSH_AGENT_FAILURE);
-            }
-            catch (IOException e1) {
-            }
-        }
-    }
-
-    /**
-     * @param tr
-     */
-    private void processLockRequest(TypesReader tr) {
-        try {
-            if (failWhenLocked())
-                return;
-
-            String lockPassphrase = tr.readString();
-
-            if (!authAgent.setAgentLock(lockPassphrase)) {
-                os.write(SSH_AGENT_FAILURE);
-                return;
-            }
-            else
-                os.write(SSH_AGENT_SUCCESS);
-        }
-        catch (IOException e) {
-            try {
-                os.write(SSH_AGENT_FAILURE);
-            }
-            catch (IOException e1) {
-            }
-        }
-    }
-
-    /**
-     * @param tr
-     */
-    private void processUnlockRequest(TypesReader tr) {
-        try {
-            String unlockPassphrase = tr.readString();
-
-            if (authAgent.requestAgentUnlock(unlockPassphrase))
-                os.write(SSH_AGENT_SUCCESS);
-            else
-                os.write(SSH_AGENT_FAILURE);
-        }
-        catch (IOException e) {
-            try {
-                os.write(SSH_AGENT_FAILURE);
-            }
-            catch (IOException e1) {
-            }
-        }
-    }
-
-    /**
-     * @param tw
-     * @throws IOException
-     */
-    private void sendPacket(byte[] message) throws IOException {
-        TypesWriter packet = new TypesWriter();
-        packet.writeUINT32(message.length);
-        packet.writeBytes(message);
-        os.write(packet.getBytes());
-    }
-}
--- a/src/com/trilead/ssh2/channel/Channel.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,192 +0,0 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * Channel.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class Channel {
-    /*
-     * OK. Here is an important part of the JVM Specification:
-     * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
-     *
-     * Any association between locks and variables is purely conventional.
-     * Locking any lock conceptually flushes all variables from a thread's
-     * working memory, and unlocking any lock forces the writing out to main
-     * memory of all variables that the thread has assigned. That a lock may be
-     * associated with a particular object or a class is purely a convention.
-     * (...)
-     *
-     * If a thread uses a particular shared variable only after locking a
-     * particular lock and before the corresponding unlocking of that same lock,
-     * then the thread will read the shared value of that variable from main
-     * memory after the lock operation, if necessary, and will copy back to main
-     * memory the value most recently assigned to that variable before the
-     * unlock operation.
-     *
-     * This, in conjunction with the mutual exclusion rules for locks, suffices
-     * to guarantee that values are correctly transmitted from one thread to
-     * another through shared variables.
-     *
-     * ====> Always keep that in mind when modifying the Channel/ChannelManger
-     * code.
-     *
-     */
-
-    static final int STATE_OPENING = 1;
-    static final int STATE_OPEN = 2;
-    static final int STATE_CLOSED = 4;
-
-    static final int CHANNEL_BUFFER_SIZE = 30000;
-
-    /*
-     * To achieve correctness, the following rules have to be respected when
-     * accessing this object:
-     */
-
-    // These fields can always be read
-    final ChannelManager cm;
-    final ChannelOutputStream stdinStream;
-    final ChannelInputStream stdoutStream;
-    final ChannelInputStream stderrStream;
-
-    // These two fields will only be written while the Channel is in state
-    // STATE_OPENING.
-    // The code makes sure that the two fields are written out when the state is
-    // changing to STATE_OPEN.
-    // Therefore, if you know that the Channel is in state STATE_OPEN, then you
-    // can read these two fields without synchronizing on the Channel. However, make
-    // sure that you get the latest values (e.g., flush caches by synchronizing on any
-    // object). However, to be on the safe side, you can lock the channel.
-
-    int localID = -1;
-    int remoteID = -1;
-
-    /*
-     * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
-     * msg.
-     *
-     * This is a little bit complicated, but we have to do it in that way, since
-     * we cannot keep a lock on the Channel during the send operation (this
-     * would block sometimes the receiver thread, and, in extreme cases, can
-     * lead to a deadlock on both sides of the connection (senders are blocked
-     * since the receive buffers on the other side are full, and receiver
-     * threads wait for the senders to finish). It all depends on the
-     * implementation on the other side. But we cannot make any assumptions, we
-     * have to assume the worst case. Confused? Just believe me.
-     */
-
-    /*
-     * If you send a message on a channel, then you have to aquire the
-     * "channelSendLock" and check the "closeMessageSent" flag (this variable
-     * may only be accessed while holding the "channelSendLock" !!!
-     *
-     * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
-     * above.
-     */
-
-    final Object channelSendLock = new Object();
-    boolean closeMessageSent = false;
-
-    /*
-     * Stop memory fragmentation by allocating this often used buffer.
-     * May only be used while holding the channelSendLock
-     */
-
-    final byte[] msgWindowAdjust = new byte[9];
-
-    // If you access (read or write) any of the following fields, then you have
-    // to synchronize on the channel.
-
-    int state = STATE_OPENING;
-
-    boolean closeMessageRecv = false;
-
-    /* This is a stupid implementation. At the moment we can only wait
-     * for one pending request per channel.
-     */
-    int successCounter = 0;
-    int failedCounter = 0;
-
-    int localWindow = 0; /* locally, we use a small window, < 2^31 */
-    long remoteWindow = 0; /* long for readable  2^32 - 1 window support */
-
-    int localMaxPacketSize = -1;
-    int remoteMaxPacketSize = -1;
-
-    final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
-    final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];
-
-    int stdoutReadpos = 0;
-    int stdoutWritepos = 0;
-    int stderrReadpos = 0;
-    int stderrWritepos = 0;
-
-    boolean EOF = false;
-
-    Integer exit_status;
-
-    String exit_signal;
-
-    // we keep the x11 cookie so that this channel can be closed when this
-    // specific x11 forwarding gets stopped
-
-    String hexX11FakeCookie;
-
-    // reasonClosed is special, since we sometimes need to access it
-    // while holding the channelSendLock.
-    // We protect it with a private short term lock.
-
-    private final Object reasonClosedLock = new Object();
-    private String reasonClosed = null;
-
-    public Channel(ChannelManager cm) {
-        this.cm = cm;
-        this.localWindow = CHANNEL_BUFFER_SIZE;
-        this.localMaxPacketSize = 35000 - 1024; // leave enough slack
-        this.stdinStream = new ChannelOutputStream(this);
-        this.stdoutStream = new ChannelInputStream(this, false);
-        this.stderrStream = new ChannelInputStream(this, true);
-    }
-
-    /* Methods to allow access from classes outside of this package */
-
-    public ChannelInputStream getStderrStream() {
-        return stderrStream;
-    }
-
-    public ChannelOutputStream getStdinStream() {
-        return stdinStream;
-    }
-
-    public ChannelInputStream getStdoutStream() {
-        return stdoutStream;
-    }
-
-    public String getExitSignal() {
-        synchronized (this) {
-            return exit_signal;
-        }
-    }
-
-    public Integer getExitStatus() {
-        synchronized (this) {
-            return exit_status;
-        }
-    }
-
-    public String getReasonClosed() {
-        synchronized (reasonClosedLock) {
-            return reasonClosed;
-        }
-    }
-
-    public void setReasonClosed(String reasonClosed) {
-        synchronized (reasonClosedLock) {
-            if (this.reasonClosed == null)
-                this.reasonClosed = reasonClosed;
-        }
-    }
-}
--- a/src/com/trilead/ssh2/channel/ChannelInputStream.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * ChannelInputStream.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ChannelInputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public final class ChannelInputStream extends InputStream {
-    Channel c;
-
-    boolean isClosed = false;
-    boolean isEOF = false;
-    boolean extendedFlag = false;
-
-    ChannelInputStream(Channel c, boolean isExtended) {
-        this.c = c;
-        this.extendedFlag = isExtended;
-    }
-
-    public int available() throws IOException {
-        if (isEOF)
-            return 0;
-
-        int avail = c.cm.getAvailable(c, extendedFlag);
-        /* We must not return -1 on EOF */
-        return (avail > 0) ? avail : 0;
-    }
-
-    public void close() throws IOException {
-        isClosed = true;
-    }
-
-    public int read(byte[] b, int off, int len) throws IOException {
-        if (b == null)
-            throw new NullPointerException();
-
-        if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
-            throw new IndexOutOfBoundsException();
-
-        if (len == 0)
-            return 0;
-
-        if (isEOF)
-            return -1;
-
-        int ret = c.cm.getChannelData(c, extendedFlag, b, off, len);
-
-        if (ret == -1) {
-            isEOF = true;
-        }
-
-        return ret;
-    }
-
-    public int read(byte[] b) throws IOException {
-        return read(b, 0, b.length);
-    }
-
-    public int read() throws IOException {
-        /* Yes, this stream is pure and unbuffered, a single byte read() is slow */
-        final byte b[] = new byte[1];
-        int ret = read(b, 0, 1);
-
-        if (ret != 1)
-            return -1;
-
-        return b[0] & 0xff;
-    }
-}
--- a/src/com/trilead/ssh2/channel/ChannelManager.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1535 +0,0 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Vector;
-
-import com.trilead.ssh2.AuthAgentCallback;
-import com.trilead.ssh2.ChannelCondition;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.PacketChannelAuthAgentReq;
-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.PacketSessionPtyResize;
-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 $
- */
-public class ChannelManager implements MessageHandler {
-    private static final Logger log = Logger.getLogger(ChannelManager.class);
-
-    private HashMap<String, X11ServerData> x11_magic_cookies = new HashMap<String, X11ServerData>();
-
-    private TransportManager tm;
-
-    private Vector<Channel> channels = new Vector<Channel>();
-    private int nextLocalChannel = 100;
-    private boolean shutdown = false;
-    private int globalSuccessCounter = 0;
-    private int globalFailedCounter = 0;
-
-    private HashMap<Integer, RemoteForwardingData> remoteForwardings = new HashMap<Integer, RemoteForwardingData>();
-
-    private AuthAgentCallback authAgent;
-
-    private Vector<IChannelWorkerThread> listenerThreads = new Vector<IChannelWorkerThread>();
-
-    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 = 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 = 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) {
-                }
-            }
-
-            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) {
-                }
-            }
-
-            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) {
-                }
-            }
-
-            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> channel_copy;
-
-        synchronized (channels) {
-            channel_copy = (Vector<Channel>) channels.clone();
-        }
-
-        for (int i = 0; i < channel_copy.size(); i++) {
-            Channel c = 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 x11_magic_cookies.get(hexFakeCookie);
-        }
-
-        return null;
-    }
-
-    public void closeAllChannels() {
-        if (log.isEnabled())
-            log.log(50, "Closing all channels");
-
-        Vector<Channel> channel_copy;
-
-        synchronized (channels) {
-            channel_copy = (Vector<Channel>) channels.clone();
-        }
-
-        for (int i = 0; i < channel_copy.size(); i++) {
-            Channel c = 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) {
-                    }
-                }
-
-                /* 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 = Integer.valueOf(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 = remoteForwardings.get(Integer.valueOf(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);
-            }
-        }
-    }
-
-    /**
-     * @param agent
-     * @throws IOException
-     */
-    public boolean requestChannelAgentForwarding(Channel c, AuthAgentCallback authAgent) throws IOException {
-        synchronized (this) {
-            if (this.authAgent != null)
-                throw new IllegalStateException("Auth agent already exists");
-
-            this.authAgent = authAgent;
-        }
-
-        synchronized (channels) {
-            globalSuccessCounter = globalFailedCounter = 0;
-        }
-
-        if (log.isEnabled())
-            log.log(50, "Requesting agent forwarding");
-
-        PacketChannelAuthAgentReq aar = new PacketChannelAuthAgentReq(c.remoteID);
-        tm.sendMessage(aar.getPayload());
-
-        if (waitForChannelRequestResult(c) == false) {
-            authAgent = null;
-            return false;
-        }
-
-        return true;
-    }
-
-    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 resizePTY(Channel c, int term_width_characters, int term_height_characters,
-                          int term_width_pixels, int term_height_pixels) throws IOException {
-        PacketSessionPtyResize spr;
-
-        synchronized (c) {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot request PTY on this channel ("
-                                      + c.getReasonClosed() + ")");
-
-            spr = new PacketSessionPtyResize(c.remoteID, term_width_characters, term_height_characters,
-                                             term_width_pixels, term_height_pixels);
-            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());
-        }
-    }
-
-
-    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
-     *
-     */
-    public int waitForCondition(Channel c, long timeout, int condition_mask) {
-        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;
-                    }
-                }
-
-                try {
-                    if (timeout > 0)
-                        c.wait(timeout);
-                    else
-                        c.wait();
-                }
-                catch (InterruptedException e) {
-                }
-            }
-        }
-    }
-
-    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) {
-                }
-            }
-
-            /* 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 = remoteForwardings.get(Integer.valueOf(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;
-        }
-
-        if ("auth-agent@openssh.com".equals(channelType)) {
-            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);
-            }
-
-            AuthAgentForwardThread aat = new AuthAgentForwardThread(c, authAgent);
-            aat.setDaemon(true);
-            aat.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 = Integer.valueOf(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 = listenerThreads.elementAt(i);
-                    lat.stopWorking();
-                }
-
-                listenerThreadsAllowed = false;
-            }
-
-            synchronized (channels) {
-                shutdown = true;
-
-                for (int i = 0; i < channels.size(); i++) {
-                    Channel c = 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));
-        }
-    }
-}
--- a/src/com/trilead/ssh2/channel/ChannelOutputStream.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * ChannelOutputStream.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ChannelOutputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public final class ChannelOutputStream extends OutputStream {
-    Channel c;
-
-    private byte[] writeBuffer;
-
-    boolean isClosed = false;
-
-    ChannelOutputStream(Channel c) {
-        this.c = c;
-        writeBuffer = new byte[1];
-    }
-
-    public void write(int b) throws IOException {
-        writeBuffer[0] = (byte) b;
-        write(writeBuffer, 0, 1);
-    }
-
-    public void close() throws IOException {
-        if (isClosed == false) {
-            isClosed = true;
-            c.cm.sendEOF(c);
-        }
-    }
-
-    public void flush() throws IOException {
-        if (isClosed)
-            throw new IOException("This OutputStream is closed.");
-
-        /* This is a no-op, since this stream is unbuffered */
-    }
-
-    public void write(byte[] b, int off, int len) throws IOException {
-        if (isClosed)
-            throw new IOException("This OutputStream is closed.");
-
-        if (b == null)
-            throw new NullPointerException();
-
-        if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
-            throw new IndexOutOfBoundsException();
-
-        if (len == 0)
-            return;
-
-        c.cm.sendData(c, b, off, len);
-    }
-
-    public void write(byte[] b) throws IOException {
-        write(b, 0, b.length);
-    }
-}
--- a/src/com/trilead/ssh2/channel/DynamicAcceptThread.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,295 +0,0 @@
-/*
- * ConnectBot: simple, powerful, open-source SSH client for Android
- * Copyright 2007 Kenny Root, Jeffrey Sharkey
- *
- * Licensed 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 com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.io.PushbackInputStream;
-import java.net.ConnectException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.NoRouteToHostException;
-import java.net.ServerSocket;
-import java.net.Socket;
-
-import net.sourceforge.jsocks.Proxy;
-import net.sourceforge.jsocks.ProxyMessage;
-import net.sourceforge.jsocks.Socks4Message;
-import net.sourceforge.jsocks.Socks5Message;
-import net.sourceforge.jsocks.SocksException;
-import net.sourceforge.jsocks.server.ServerAuthenticator;
-import net.sourceforge.jsocks.server.ServerAuthenticatorNone;
-
-/**
- * DynamicAcceptThread.
- *
- * @author Kenny Root
- * @version $Id$
- */
-public class DynamicAcceptThread extends Thread implements IChannelWorkerThread {
-    private ChannelManager cm;
-    private ServerSocket ss;
-
-    class DynamicAcceptRunnable implements Runnable {
-        private static final int idleTimeout    = 180000; //3 minutes
-
-        private ServerAuthenticator auth;
-        private Socket sock;
-        private InputStream in;
-        private OutputStream out;
-        private ProxyMessage msg;
-
-        public DynamicAcceptRunnable(ServerAuthenticator auth, Socket sock) {
-            this.auth = auth;
-            this.sock = sock;
-            setName("DynamicAcceptRunnable");
-        }
-
-        public void run() {
-            try {
-                startSession();
-            }
-            catch (IOException ioe) {
-                int error_code = Proxy.SOCKS_FAILURE;
-
-                if (ioe instanceof SocksException)
-                    error_code = ((SocksException) ioe).errCode;
-                else if (ioe instanceof NoRouteToHostException)
-                    error_code = Proxy.SOCKS_HOST_UNREACHABLE;
-                else if (ioe instanceof ConnectException)
-                    error_code = Proxy.SOCKS_CONNECTION_REFUSED;
-                else if (ioe instanceof InterruptedIOException)
-                    error_code = Proxy.SOCKS_TTL_EXPIRE;
-
-                if (error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED
-                        || error_code < 0) {
-                    error_code = Proxy.SOCKS_FAILURE;
-                }
-
-                sendErrorMessage(error_code);
-            }
-            finally {
-                if (auth != null)
-                    auth.endSession();
-            }
-        }
-
-        private ProxyMessage readMsg(InputStream in) throws IOException {
-            PushbackInputStream push_in;
-
-            if (in instanceof PushbackInputStream)
-                push_in = (PushbackInputStream) in;
-            else
-                push_in = new PushbackInputStream(in);
-
-            int version = push_in.read();
-            push_in.unread(version);
-            ProxyMessage msg;
-
-            if (version == 5) {
-                msg = new Socks5Message(push_in, false);
-            }
-            else if (version == 4) {
-                msg = new Socks4Message(push_in, false);
-            }
-            else {
-                throw new SocksException(Proxy.SOCKS_FAILURE);
-            }
-
-            return msg;
-        }
-
-        private void sendErrorMessage(int error_code) {
-            ProxyMessage err_msg;
-
-            if (msg instanceof Socks4Message)
-                err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED);
-            else
-                err_msg = new Socks5Message(error_code);
-
-            try {
-                err_msg.write(out);
-            }
-            catch (IOException ioe) {
-            }
-        }
-
-        private void handleRequest(ProxyMessage msg) throws IOException {
-            if (!auth.checkRequest(msg))
-                throw new SocksException(Proxy.SOCKS_FAILURE);
-
-            switch (msg.command) {
-                case Proxy.SOCKS_CMD_CONNECT:
-                    onConnect(msg);
-                    break;
-
-                default:
-                    throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED);
-            }
-        }
-
-        private void startSession() throws IOException {
-            sock.setSoTimeout(idleTimeout);
-
-            try {
-                auth = auth.startSession(sock);
-            }
-            catch (IOException ioe) {
-                System.out.println("Could not start SOCKS session");
-                ioe.printStackTrace();
-                auth = null;
-                return;
-            }
-
-            if (auth == null) { // Authentication failed
-                System.out.println("SOCKS auth failed");
-                return;
-            }
-
-            in = auth.getInputStream();
-            out = auth.getOutputStream();
-            msg = readMsg(in);
-            handleRequest(msg);
-        }
-
-        private void onConnect(ProxyMessage msg) throws IOException {
-            ProxyMessage response = null;
-            Channel cn = null;
-            StreamForwarder r2l = null;
-            StreamForwarder l2r = null;
-
-            if (msg instanceof Socks5Message) {
-                response = new Socks5Message(Proxy.SOCKS_SUCCESS, (InetAddress)null, 0);
-            }
-            else {
-                response = new Socks4Message(Socks4Message.REPLY_OK, (InetAddress)null, 0);
-            }
-
-            response.write(out);
-            String destHost = msg.host;
-
-            if (msg.ip != null)
-                destHost = msg.ip.getHostAddress();
-
-            try {
-                /*
-                 * This may fail, e.g., if the remote port is closed (in
-                 * optimistic terms: not open yet)
-                 */
-                cn = cm.openDirectTCPIPChannel(destHost, msg.port,
-                                               "127.0.0.1", 0);
-            }
-            catch (IOException e) {
-                /*
-                 * Simply close the local socket and wait for the next incoming
-                 * connection
-                 */
-                try {
-                    sock.close();
-                }
-                catch (IOException ignore) {
-                }
-
-                return;
-            }
-
-            try {
-                r2l = new StreamForwarder(cn, null, sock, cn.stdoutStream, out, "RemoteToLocal");
-                l2r = new StreamForwarder(cn, r2l, sock, in, cn.stdinStream, "LocalToRemote");
-            }
-            catch (IOException e) {
-                try {
-                    /*
-                     * This message is only visible during debugging, since we
-                     * discard the channel immediatelly
-                     */
-                    cn.cm.closeChannel(cn,
-                                       "Weird error during creation of StreamForwarder ("
-                                       + e.getMessage() + ")", true);
-                }
-                catch (IOException ignore) {
-                }
-
-                return;
-            }
-
-            r2l.setDaemon(true);
-            l2r.setDaemon(true);
-            r2l.start();
-            l2r.start();
-        }
-    }
-
-    public DynamicAcceptThread(ChannelManager cm, int local_port)
-    throws IOException {
-        this.cm = cm;
-        setName("DynamicAcceptThread");
-        ss = new ServerSocket(local_port);
-    }
-
-    public DynamicAcceptThread(ChannelManager cm, InetSocketAddress localAddress)
-    throws IOException {
-        this.cm = cm;
-        ss = new ServerSocket();
-        ss.bind(localAddress);
-    }
-
-    @Override
-    public void run() {
-        try {
-            cm.registerThread(this);
-        }
-        catch (IOException e) {
-            stopWorking();
-            return;
-        }
-
-        while (true) {
-            Socket sock = null;
-
-            try {
-                sock = ss.accept();
-            }
-            catch (IOException e) {
-                stopWorking();
-                return;
-            }
-
-            DynamicAcceptRunnable dar = new DynamicAcceptRunnable(new ServerAuthenticatorNone(), sock);
-            Thread t = new Thread(dar);
-            t.setDaemon(true);
-            t.start();
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see com.trilead.ssh2.channel.IChannelWorkerThread#stopWorking()
-     */
-    public void stopWorking() {
-        try {
-            /* This will lead to an IOException in the ss.accept() call */
-            ss.close();
-        }
-        catch (IOException e) {
-        }
-    }
-}
--- a/src/com/trilead/ssh2/channel/IChannelWorkerThread.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * IChannelWorkerThread.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: IChannelWorkerThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-interface IChannelWorkerThread {
-    public void stopWorking();
-}
--- a/src/com/trilead/ssh2/channel/LocalAcceptThread.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-
-/**
- * LocalAcceptThread.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class LocalAcceptThread extends Thread implements IChannelWorkerThread {
-    ChannelManager cm;
-    String host_to_connect;
-    int port_to_connect;
-
-    final ServerSocket ss;
-
-    public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
-    throws IOException {
-        this.cm = cm;
-        this.host_to_connect = host_to_connect;
-        this.port_to_connect = port_to_connect;
-        ss = new ServerSocket(local_port);
-    }
-
-    public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
-                             int port_to_connect) throws IOException {
-        this.cm = cm;
-        this.host_to_connect = host_to_connect;
-        this.port_to_connect = port_to_connect;
-        ss = new ServerSocket();
-        ss.bind(localAddress);
-    }
-
-    public void run() {
-        try {
-            cm.registerThread(this);
-        }
-        catch (IOException e) {
-            stopWorking();
-            return;
-        }
-
-        while (true) {
-            Socket s = null;
-
-            try {
-                s = ss.accept();
-            }
-            catch (IOException e) {
-                stopWorking();
-                return;
-            }
-
-            Channel cn = null;
-            StreamForwarder r2l = null;
-            StreamForwarder l2r = null;
-
-            try {
-                /* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */
-                cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
-                                               .getPort());
-            }
-            catch (IOException e) {
-                /* Simply close the local socket and wait for the next incoming connection */
-                try {
-                    s.close();
-                }
-                catch (IOException ignore) {
-                }
-
-                continue;
-            }
-
-            try {
-                r2l = new StreamForwarder(cn, null, s, cn.stdoutStream, s.getOutputStream(), "RemoteToLocal");
-                l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
-            }
-            catch (IOException e) {
-                try {
-                    /* This message is only visible during debugging, since we discard the channel immediatelly */
-                    cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
-                                       true);
-                }
-                catch (IOException ignore) {
-                }
-
-                continue;
-            }
-
-            r2l.setDaemon(true);
-            l2r.setDaemon(true);
-            r2l.start();
-            l2r.start();
-        }
-    }
-
-    public void stopWorking() {
-        try {
-            /* This will lead to an IOException in the ss.accept() call */
-            ss.close();
-        }
-        catch (IOException e) {
-        }
-    }
-}
--- a/src/com/trilead/ssh2/channel/RemoteAcceptThread.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.net.Socket;
-
-import com.trilead.ssh2.log.Logger;
-
-
-/**
- * RemoteAcceptThread.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class RemoteAcceptThread extends Thread {
-    private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
-
-    Channel c;
-
-    String remoteConnectedAddress;
-    int remoteConnectedPort;
-    String remoteOriginatorAddress;
-    int remoteOriginatorPort;
-    String targetAddress;
-    int targetPort;
-
-    Socket s;
-
-    public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
-                              String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort) {
-        this.c = c;
-        this.remoteConnectedAddress = remoteConnectedAddress;
-        this.remoteConnectedPort = remoteConnectedPort;
-        this.remoteOriginatorAddress = remoteOriginatorAddress;
-        this.remoteOriginatorPort = remoteOriginatorPort;
-        this.targetAddress = targetAddress;
-        this.targetPort = targetPort;
-
-        if (log.isEnabled())
-            log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
-                    + remoteOriginatorAddress + "/" + remoteOriginatorPort);
-    }
-
-    public void run() {
-        try {
-            c.cm.sendOpenConfirmation(c);
-            s = new Socket(targetAddress, targetPort);
-            StreamForwarder r2l = new StreamForwarder(c, null, s, c.getStdoutStream(), s.getOutputStream(),
-                    "RemoteToLocal");
-            StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
-                    "LocalToRemote");
-            /* No need to start two threads, one can be executed in the current thread */
-            r2l.setDaemon(true);
-            r2l.start();
-            l2r.run();
-
-            while (r2l.isAlive()) {
-                try {
-                    r2l.join();
-                }
-                catch (InterruptedException e) {
-                }
-            }
-
-            /* If the channel is already closed, then this is a no-op */
-            c.cm.closeChannel(c, "EOF on both streams reached.", true);
-            s.close();
-        }
-        catch (IOException e) {
-            log.log(50, "IOException in proxy code: " + e.getMessage());
-
-            try {
-                c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
-            }
-            catch (IOException e1) {
-            }
-
-            try {
-                if (s != null)
-                    s.close();
-            }
-            catch (IOException e1) {
-            }
-        }
-    }
-}
--- a/src/com/trilead/ssh2/channel/RemoteForwardingData.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * RemoteForwardingData. Data about a requested remote forwarding.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RemoteForwardingData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class RemoteForwardingData {
-    public String bindAddress;
-    public int bindPort;
-
-    String targetAddress;
-    int targetPort;
-}
--- a/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,205 +0,0 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import com.trilead.ssh2.log.Logger;
-
-
-/**
- * RemoteX11AcceptThread.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RemoteX11AcceptThread.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class RemoteX11AcceptThread extends Thread {
-    private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class);
-
-    Channel c;
-
-    String remoteOriginatorAddress;
-    int remoteOriginatorPort;
-
-    Socket s;
-
-    public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort) {
-        this.c = c;
-        this.remoteOriginatorAddress = remoteOriginatorAddress;
-        this.remoteOriginatorPort = remoteOriginatorPort;
-    }
-
-    public void run() {
-        try {
-            /* Send Open Confirmation */
-            c.cm.sendOpenConfirmation(c);
-            /* Read startup packet from client */
-            OutputStream remote_os = c.getStdinStream();
-            InputStream remote_is = c.getStdoutStream();
-            /* The following code is based on the protocol description given in:
-             * Scheifler/Gettys,
-             * X Windows System: Core and Extension Protocols:
-             * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X
-             */
-            /*
-             * Client startup:
-             *
-             * 1 0X42 MSB first/0x6c lSB first - byteorder
-             * 1 - unused
-             * 2 card16 - protocol-major-version
-             * 2 card16 - protocol-minor-version
-             * 2 n - lenght of authorization-protocol-name
-             * 2 d - lenght of authorization-protocol-data
-             * 2 - unused
-             * string8 - authorization-protocol-name
-             * p - unused, p=pad(n)
-             * string8 - authorization-protocol-data
-             * q - unused, q=pad(d)
-             *
-             * pad(X) = (4 - (X mod 4)) mod 4
-             *
-             * Server response:
-             *
-             * 1 (0 failed, 2 authenticate, 1 success)
-             * ...
-             *
-             */
-            /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */
-            byte[] header = new byte[6];
-
-            if (remote_is.read(header) != 6)
-                throw new IOException("Unexpected EOF on X11 startup!");
-
-            if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first
-                throw new IOException("Unknown endian format in X11 message!");
-
-            /* Yes, I came up with this myself - shall I file an application for a patent? =) */
-            int idxMSB = (header[0] == 0x42) ? 0 : 1;
-            /* Read authorization data header */
-            byte[] auth_buff = new byte[6];
-
-            if (remote_is.read(auth_buff) != 6)
-                throw new IOException("Unexpected EOF on X11 startup!");
-
-            int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8) | (auth_buff[1 - idxMSB] & 0xff);
-            int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8) | (auth_buff[3 - idxMSB] & 0xff);
-
-            if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256))
-                throw new IOException("Buggy X11 authorization data");
-
-            int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);
-            int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);
-            byte[] authProtocolName = new byte[authProtocolNameLength];
-            byte[] authProtocolData = new byte[authProtocolDataLength];
-            byte[] paddingBuffer = new byte[4];
-
-            if (remote_is.read(authProtocolName) != authProtocolNameLength)
-                throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)");
-
-            if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding)
-                throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)");
-
-            if (remote_is.read(authProtocolData) != authProtocolDataLength)
-                throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)");
-
-            if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)
-                throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)");
-
-            if ("MIT-MAGIC-COOKIE-1".equals(new String(authProtocolName, "ISO-8859-1")) == false)
-                throw new IOException("Unknown X11 authorization protocol!");
-
-            if (authProtocolDataLength != 16)
-                throw new IOException("Wrong data length for X11 authorization data!");
-
-            StringBuffer tmp = new StringBuffer(32);
-
-            for (int i = 0; i < authProtocolData.length; i++) {
-                String digit2 = Integer.toHexString(authProtocolData[i] & 0xff);
-                tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
-            }
-
-            String hexEncodedFakeCookie = tmp.toString();
-
-            /* Order is very important here - it may be that a certain x11 forwarding
-             * gets disabled right in the moment when we check and register our connection
-             * */
-
-            synchronized (c) {
-                /* Please read the comment in Channel.java */
-                c.hexX11FakeCookie = hexEncodedFakeCookie;
-            }
-
-            /* Now check our fake cookie directory to see if we produced this cookie */
-            X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie);
-
-            if (sd == null)
-                throw new IOException("Invalid X11 cookie received.");
-
-            /* If the session which corresponds to this cookie is closed then we will
-             * detect this: the session's close code will close all channels
-             * with the session's assigned x11 fake cookie.
-             */
-            s = new Socket(sd.hostname, sd.port);
-            OutputStream x11_os = s.getOutputStream();
-            InputStream x11_is = s.getInputStream();
-            /* Now we are sending the startup packet to the real X11 server */
-            x11_os.write(header);
-
-            if (sd.x11_magic_cookie == null) {
-                byte[] emptyAuthData = new byte[6];
-                /* empty auth data, hopefully you are connecting to localhost =) */
-                x11_os.write(emptyAuthData);
-            }
-            else {
-                if (sd.x11_magic_cookie.length != 16)
-                    throw new IOException("The real X11 cookie has an invalid length!");
-
-                /* send X11 cookie specified by client */
-                x11_os.write(auth_buff);
-                x11_os.write(authProtocolName); /* re-use */
-                x11_os.write(paddingBuffer, 0, authProtocolNamePadding);
-                x11_os.write(sd.x11_magic_cookie);
-                x11_os.write(paddingBuffer, 0, authProtocolDataPadding);
-            }
-
-            x11_os.flush();
-            /* Start forwarding traffic */
-            StreamForwarder r2l = new StreamForwarder(c, null, s, remote_is, x11_os, "RemoteToX11");
-            StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote");
-            /* No need to start two threads, one can be executed in the current thread */
-            r2l.setDaemon(true);
-            r2l.start();
-            l2r.run();
-
-            while (r2l.isAlive()) {
-                try {
-                    r2l.join();
-                }
-                catch (InterruptedException e) {
-                }
-            }
-
-            /* If the channel is already closed, then this is a no-op */
-            c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);
-            s.close();
-        }
-        catch (IOException e) {
-            log.log(50, "IOException in X11 proxy code: " + e.getMessage());
-
-            try {
-                c.cm.closeChannel(c, "IOException in X11 proxy code (" + e.getMessage() + ")", true);
-            }
-            catch (IOException e1) {
-            }
-
-            try {
-                if (s != null)
-                    s.close();
-            }
-            catch (IOException e1) {
-            }
-        }
-    }
-}
--- a/src/com/trilead/ssh2/channel/StreamForwarder.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-/**
- * A StreamForwarder forwards data between two given streams.
- * If two StreamForwarder threads are used (one for each direction)
- * then one can be configured to shutdown the underlying channel/socket
- * if both threads have finished forwarding (EOF).
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class StreamForwarder extends Thread {
-    final OutputStream os;
-    final InputStream is;
-    final byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
-    final Channel c;
-    final StreamForwarder sibling;
-    final Socket s;
-    final String mode;
-
-    StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
-    throws IOException {
-        this.is = is;
-        this.os = os;
-        this.mode = mode;
-        this.c = c;
-        this.sibling = sibling;
-        this.s = s;
-    }
-
-    public void run() {
-        try {
-            while (true) {
-                int len = is.read(buffer);
-
-                if (len <= 0)
-                    break;
-
-                os.write(buffer, 0, len);
-                os.flush();
-            }
-        }
-        catch (IOException ignore) {
-            try {
-                c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
-                                  + ignore.getMessage(), true);
-            }
-            catch (IOException e) {
-            }
-        }
-        finally {
-            try {
-                os.close();
-            }
-            catch (IOException e1) {
-            }
-
-            try {
-                is.close();
-            }
-            catch (IOException e2) {
-            }
-
-            if (sibling != null) {
-                while (sibling.isAlive()) {
-                    try {
-                        sibling.join();
-                    }
-                    catch (InterruptedException e) {
-                    }
-                }
-
-                try {
-                    c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
-                }
-                catch (IOException e3) {
-                }
-            }
-
-            if (s != null) {
-                try {
-                    s.close();
-                }
-                catch (IOException e1) {
-                }
-            }
-        }
-    }
-}
--- a/src/com/trilead/ssh2/channel/X11ServerData.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * X11ServerData. Data regarding an x11 forwarding target.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: X11ServerData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- *
- */
-public class X11ServerData {
-    public String hostname;
-    public int port;
-    public byte[] x11_magic_cookie; /* not the remote (fake) one, the local (real) one */
-}
--- a/src/com/trilead/ssh2/compression/CompressionFactory.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * ConnectBot: simple, powerful, open-source SSH client for Android
- * Copyright 2007 Kenny Root, Jeffrey Sharkey
- *
- * Licensed 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 com.trilead.ssh2.compression;
-
-import java.util.Vector;
-
-/**
- * @author Kenny Root
- *
- */
-public class CompressionFactory {
-    static class CompressorEntry {
-        String type;
-        String compressorClass;
-
-        public CompressorEntry(String type, String compressorClass) {
-            this.type = type;
-            this.compressorClass = compressorClass;
-        }
-    }
-
-    static Vector<CompressorEntry> compressors = new Vector<CompressorEntry>();
-
-    static {
-        /* Higher Priority First */
-        compressors.addElement(new CompressorEntry("zlib", "com.trilead.ssh2.compression.Zlib"));
-        compressors.addElement(new CompressorEntry("zlib@openssh.com", "com.trilead.ssh2.compression.ZlibOpenSSH"));
-        compressors.addElement(new CompressorEntry("none", ""));
-    }
-
-    public static String[] getDefaultCompressorList() {
-        String list[] = new String[compressors.size()];
-
-        for (int i = 0; i < compressors.size(); i++) {
-            CompressorEntry ce = compressors.elementAt(i);
-            list[i] = new String(ce.type);
-        }
-
-        return list;
-    }
-
-    public static void checkCompressorList(String[] compressorCandidates) {
-        for (int i = 0; i < compressorCandidates.length; i++)
-            getEntry(compressorCandidates[i]);
-    }
-
-    public static ICompressor createCompressor(String type) {
-        try {
-            CompressorEntry ce = getEntry(type);
-
-            if ("".equals(ce.compressorClass))
-                return null;
-
-            Class<?> cc = Class.forName(ce.compressorClass);
-            ICompressor cmp = (ICompressor) cc.newInstance();
-            return cmp;
-        }
-        catch (Exception e) {
-            throw new IllegalArgumentException("Cannot instantiate " + type);
-        }
-    }
-
-    private static CompressorEntry getEntry(String type) {
-        for (int i = 0; i < compressors.size(); i++) {
-            CompressorEntry ce = compressors.elementAt(i);
-
-            if (ce.type.equals(type))
-                return ce;
-        }
-
-        throw new IllegalArgumentException("Unkown algorithm " + type);
-    }
-}
--- a/src/com/trilead/ssh2/compression/ICompressor.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * ConnectBot: simple, powerful, open-source SSH client for Android
- * Copyright 2007 Kenny Root, Jeffrey Sharkey
- *
- * Licensed 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 com.trilead.ssh2.compression;
-
-/**
- * @author Kenny Root
- *
- */
-public interface ICompressor {
-    int getBufferSize();
-
-    int compress(byte[] buf, int start, int len, byte[] output);
-
-    byte[] uncompress(byte[] buf, int start, int[] len);
-
-    boolean canCompressPreauth();
-}
--- a/src/com/trilead/ssh2/compression/Zlib.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/*
- * ConnectBot: simple, powerful, open-source SSH client for Android
- * Copyright 2007 Kenny Root, Jeffrey Sharkey
- *
- * Licensed 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 com.trilead.ssh2.compression;
-
-import com.jcraft.jzlib.JZlib;
-import com.jcraft.jzlib.ZStream;
-
-/**
- * @author Kenny Root
- *
- */
-public class Zlib implements ICompressor {
-    static private final int DEFAULT_BUF_SIZE = 4096;
-    static private final int LEVEL = 5;
-
-    private ZStream deflate;
-    private byte[] deflate_tmpbuf;
-
-    private ZStream inflate;
-    private byte[] inflate_tmpbuf;
-    private byte[] inflated_buf;
-
-    public Zlib() {
-        deflate = new ZStream();
-        inflate = new ZStream();
-        deflate.deflateInit(LEVEL);
-        inflate.inflateInit();
-        deflate_tmpbuf = new byte[DEFAULT_BUF_SIZE];
-        inflate_tmpbuf = new byte[DEFAULT_BUF_SIZE];
-        inflated_buf = new byte[DEFAULT_BUF_SIZE];
-    }
-
-    public boolean canCompressPreauth() {
-        return true;
-    }
-
-    public int getBufferSize() {
-        return DEFAULT_BUF_SIZE;
-    }
-
-    public int compress(byte[] buf, int start, int len, byte[] output) {
-        deflate.next_in = buf;
-        deflate.next_in_index = start;
-        deflate.avail_in = len - start;
-
-        if ((buf.length + 1024) > deflate_tmpbuf.length) {
-            deflate_tmpbuf = new byte[buf.length + 1024];
-        }
-
-        deflate.next_out = deflate_tmpbuf;
-        deflate.next_out_index = 0;
-        deflate.avail_out = output.length;
-
-        if (deflate.deflate(JZlib.Z_PARTIAL_FLUSH) != JZlib.Z_OK) {
-            System.err.println("compress: compression failure");
-        }
-
-        if (deflate.avail_in > 0) {
-            System.err.println("compress: deflated data too large");
-        }
-
-        int outputlen = output.length - deflate.avail_out;
-        System.arraycopy(deflate_tmpbuf, 0, output, 0, outputlen);
-        return outputlen;
-    }
-
-    public byte[] uncompress(byte[] buffer, int start, int[] length) {
-        int inflated_end = 0;
-        inflate.next_in = buffer;
-        inflate.next_in_index = start;
-        inflate.avail_in = length[0];
-
-        while (true) {
-            inflate.next_out = inflate_tmpbuf;
-            inflate.next_out_index = 0;
-            inflate.avail_out = DEFAULT_BUF_SIZE;
-            int status = inflate.inflate(JZlib.Z_PARTIAL_FLUSH);
-
-            switch (status) {
-                case JZlib.Z_OK:
-                    if (inflated_buf.length < inflated_end + DEFAULT_BUF_SIZE
-                            - inflate.avail_out) {
-                        byte[] foo = new byte[inflated_end + DEFAULT_BUF_SIZE
-                                              - inflate.avail_out];
-                        System.arraycopy(inflated_buf, 0, foo, 0, inflated_end);
-                        inflated_buf = foo;
-                    }
-
-                    System.arraycopy(inflate_tmpbuf, 0, inflated_buf, inflated_end,
-                                     DEFAULT_BUF_SIZE - inflate.avail_out);
-                    inflated_end += (DEFAULT_BUF_SIZE - inflate.avail_out);
-                    length[0] = inflated_end;
-                    break;
-
-                case JZlib.Z_BUF_ERROR:
-                    if (inflated_end > buffer.length - start) {
-                        byte[] foo = new byte[inflated_end + start];
-                        System.arraycopy(buffer, 0, foo, 0, start);
-                        System.arraycopy(inflated_buf, 0, foo, start, inflated_end);
-                        buffer = foo;
-                    }
-                    else {
-                        System.arraycopy(inflated_buf, 0, buffer, start,
-                                         inflated_end);
-                    }
-
-                    length[0] = inflated_end;
-                    return buffer;
-
-                default:
-                    System.err.println("uncompress: inflate returnd " + status);
-                    return null;
-            }
-        }
-    }
-}
--- a/src/com/trilead/ssh2/compression/ZlibOpenSSH.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * ConnectBot: simple, powerful, open-source SSH client for Android
- * Copyright 2007 Kenny Root, Jeffrey Sharkey
- *
- * Licensed 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 com.trilead.ssh2.compression;
-
-/**
- * Defines how zlib@openssh.org compression works.
- * See
- * http://www.openssh.org/txt/draft-miller-secsh-compression-delayed-00.txt
- * compression is disabled until userauth has occurred.
- *
- * @author Matt Johnston
- *
- */
-public class ZlibOpenSSH extends Zlib {
-
-    public boolean canCompressPreauth() {
-        return false;
-    }
-
-}
--- a/src/com/trilead/ssh2/crypto/Base64.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-
-package com.trilead.ssh2.crypto;
-
-import java.io.CharArrayWriter;
-import java.io.IOException;
-
-/**
- * Basic Base64 Support.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Base64.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class Base64 {
-    static final char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
-
-    public static char[] encode(byte[] content) {
-        CharArrayWriter cw = new CharArrayWriter((4 * content.length) / 3);
-        int idx = 0;
-        int x = 0;
-
-        for (int i = 0; i < content.length; i++) {
-            if (idx == 0)
-                x = (content[i] & 0xff) << 16;
-            else if (idx == 1)
-                x = x | ((content[i] & 0xff) << 8);
-            else
-                x = x | (content[i] & 0xff);
-
-            idx++;
-
-            if (idx == 3) {
-                cw.write(alphabet[x >> 18]);
-                cw.write(alphabet[(x >> 12) & 0x3f]);
-                cw.write(alphabet[(x >> 6) & 0x3f]);
-                cw.write(alphabet[x & 0x3f]);
-                idx = 0;
-            }
-        }
-
-        if (idx == 1) {
-            cw.write(alphabet[x >> 18]);
-            cw.write(alphabet[(x >> 12) & 0x3f]);
-            cw.write('=');
-            cw.write('=');
-        }
-
-        if (idx == 2) {
-            cw.write(alphabet[x >> 18]);
-            cw.write(alphabet[(x >> 12) & 0x3f]);
-            cw.write(alphabet[(x >> 6) & 0x3f]);
-            cw.write('=');
-        }
-
-        return cw.toCharArray();
-    }
-
-    public static byte[] decode(char[] message) throws IOException {
-        byte buff[] = new byte[4];
-        byte dest[] = new byte[message.length];
-        int bpos = 0;
-        int destpos = 0;
-
-        for (int i = 0; i < message.length; i++) {
-            int c = message[i];
-
-            if ((c == '\n') || (c == '\r') || (c == ' ') || (c == '\t'))
-                continue;
-
-            if ((c >= 'A') && (c <= 'Z')) {
-                buff[bpos++] = (byte)(c - 'A');
-            }
-            else if ((c >= 'a') && (c <= 'z')) {
-                buff[bpos++] = (byte)((c - 'a') + 26);
-            }
-            else if ((c >= '0') && (c <= '9')) {
-                buff[bpos++] = (byte)((c - '0') + 52);
-            }
-            else if (c == '+') {
-                buff[bpos++] = 62;
-            }
-            else if (c == '/') {
-                buff[bpos++] = 63;
-            }
-            else if (c == '=') {
-                buff[bpos++] = 64;
-            }
-            else {
-                throw new IOException("Illegal char in base64 code.");
-            }
-
-            if (bpos == 4) {
-                bpos = 0;
-
-                if (buff[0] == 64)
-                    break;
-
-                if (buff[1] == 64)
-                    throw new IOException("Unexpected '=' in base64 code.");
-
-                if (buff[2] == 64) {
-                    int v = (((buff[0] & 0x3f) << 6) | ((buff[1] & 0x3f)));
-                    dest[destpos++] = (byte)(v >> 4);
-                    break;
-                }
-                else if (buff[3] == 64) {
-                    int v = (((buff[0] & 0x3f) << 12) | ((buff[1] & 0x3f) << 6) | ((buff[2] & 0x3f)));
-                    dest[destpos++] = (byte)(v >> 10);
-                    dest[destpos++] = (byte)(v >> 2);
-                    break;
-                }
-                else {
-                    int v = (((buff[0] & 0x3f) << 18) | ((buff[1] & 0x3f) << 12) | ((buff[2] & 0x3f) << 6) | ((buff[3] & 0x3f)));
-                    dest[destpos++] = (byte)(v >> 16);
-                    dest[destpos++] = (byte)(v >> 8);
-                    dest[destpos++] = (byte)(v);
-                }
-            }
-        }
-
-        byte[] res = new byte[destpos];
-        System.arraycopy(dest, 0, res, 0, destpos);
-        return res;
-    }
-}
--- a/src/com/trilead/ssh2/crypto/CryptoWishList.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-
-package com.trilead.ssh2.crypto;
-
-import com.trilead.ssh2.compression.CompressionFactory;
-import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
-import com.trilead.ssh2.crypto.digest.MAC;
-import com.trilead.ssh2.transport.KexManager;
-
-
-/**
- * CryptoWishList.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: CryptoWishList.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class CryptoWishList {
-    public String[] kexAlgorithms = KexManager.getDefaultKexAlgorithmList();
-    public String[] serverHostKeyAlgorithms = KexManager.getDefaultServerHostkeyAlgorithmList();
-    public String[] c2s_enc_algos = BlockCipherFactory.getDefaultCipherList();
-    public String[] s2c_enc_algos = BlockCipherFactory.getDefaultCipherList();
-    public String[] c2s_mac_algos = MAC.getMacList();
-    public String[] s2c_mac_algos = MAC.getMacList();
-    public String[] c2s_comp_algos = CompressionFactory.getDefaultCompressorList();
-    public String[] s2c_comp_algos = CompressionFactory.getDefaultCompressorList();
-}
--- a/src/com/trilead/ssh2/crypto/KeyMaterial.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-
-package com.trilead.ssh2.crypto;
-
-
-import java.math.BigInteger;
-
-import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
-
-/**
- * Establishes key material for iv/key/mac (both directions).
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: KeyMaterial.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class KeyMaterial {
-    public byte[] initial_iv_client_to_server;
-    public byte[] initial_iv_server_to_client;
-    public byte[] enc_key_client_to_server;
-    public byte[] enc_key_server_to_client;
-    public byte[] integrity_key_client_to_server;
-    public byte[] integrity_key_server_to_client;
-
-    private static byte[] calculateKey(HashForSSH2Types sh, BigInteger K, byte[] H, byte type, byte[] SessionID,
-                                       int keyLength) {
-        byte[] res = new byte[keyLength];
-        int dglen = sh.getDigestLength();
-        int numRounds = (keyLength + dglen - 1) / dglen;
-        byte[][] tmp = new byte[numRounds][];
-        sh.reset();
-        sh.updateBigInt(K);
-        sh.updateBytes(H);
-        sh.updateByte(type);
-        sh.updateBytes(SessionID);
-        tmp[0] = sh.getDigest();
-        int off = 0;
-        int produced = Math.min(dglen, keyLength);
-        System.arraycopy(tmp[0], 0, res, off, produced);
-        keyLength -= produced;
-        off += produced;
-
-        for (int i = 1; i < numRounds; i++) {
-            sh.updateBigInt(K);
-            sh.updateBytes(H);
-
-            for (int j = 0; j < i; j++)
-                sh.updateBytes(tmp[j]);
-
-            tmp[i] = sh.getDigest();
-            produced = Math.min(dglen, keyLength);
-            System.arraycopy(tmp[i], 0, res, off, produced);
-            keyLength -= produced;
-            off += produced;
-        }
-
-        return res;
-    }
-
-    public static KeyMaterial create(String hashAlgo, byte[] H, BigInteger K, byte[] SessionID, int keyLengthCS,
-                                     int blockSizeCS, int macLengthCS, int keyLengthSC, int blockSizeSC, int macLengthSC)
-    throws IllegalArgumentException {
-        KeyMaterial km = new KeyMaterial();
-        HashForSSH2Types sh = new HashForSSH2Types(hashAlgo);
-        km.initial_iv_client_to_server = calculateKey(sh, K, H, (byte) 'A', SessionID, blockSizeCS);
-        km.initial_iv_server_to_client = calculateKey(sh, K, H, (byte) 'B', SessionID, blockSizeSC);
-        km.enc_key_client_to_server = calculateKey(sh, K, H, (byte) 'C', SessionID, keyLengthCS);
-        km.enc_key_server_to_client = calculateKey(sh, K, H, (byte) 'D', SessionID, keyLengthSC);
-        km.integrity_key_client_to_server = calculateKey(sh, K, H, (byte) 'E', SessionID, macLengthCS);
-        km.integrity_key_server_to_client = calculateKey(sh, K, H, (byte) 'F', SessionID, macLengthSC);
-        return km;
-    }
-}
--- a/src/com/trilead/ssh2/crypto/PEMDecoder.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,444 +0,0 @@
-
-package com.trilead.ssh2.crypto;
-
-import java.io.BufferedReader;
-import java.io.CharArrayReader;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.DigestException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPrivateKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-
-import com.trilead.ssh2.crypto.cipher.AES;
-import com.trilead.ssh2.crypto.cipher.BlockCipher;
-import com.trilead.ssh2.crypto.cipher.CBCMode;
-import com.trilead.ssh2.crypto.cipher.DES;
-import com.trilead.ssh2.crypto.cipher.DESede;
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
-
-/**
- * PEM Support.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PEMDecoder.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class PEMDecoder {
-    public static final int PEM_RSA_PRIVATE_KEY = 1;
-    public static final int PEM_DSA_PRIVATE_KEY = 2;
-    public static final int PEM_EC_PRIVATE_KEY = 3;
-
-    private static final int hexToInt(char c) {
-        if ((c >= 'a') && (c <= 'f')) {
-            return (c - 'a') + 10;
-        }
-
-        if ((c >= 'A') && (c <= 'F')) {
-            return (c - 'A') + 10;
-        }
-
-        if ((c >= '0') && (c <= '9')) {
-            return (c - '0');
-        }
-
-        throw new IllegalArgumentException("Need hex char");
-    }
-
-    private static byte[] hexToByteArray(String hex) {
-        if (hex == null)
-            throw new IllegalArgumentException("null argument");
-
-        if ((hex.length() % 2) != 0)
-            throw new IllegalArgumentException("Uneven string length in hex encoding.");
-
-        byte decoded[] = new byte[hex.length() / 2];
-
-        for (int i = 0; i < decoded.length; i++) {
-            int hi = hexToInt(hex.charAt(i * 2));
-            int lo = hexToInt(hex.charAt((i * 2) + 1));
-            decoded[i] = (byte)(hi * 16 + lo);
-        }
-
-        return decoded;
-    }
-
-    private static byte[] generateKeyFromPasswordSaltWithMD5(byte[] password, byte[] salt, int keyLen)
-    throws IOException {
-        if (salt.length < 8)
-            throw new IllegalArgumentException("Salt needs to be at least 8 bytes for key generation.");
-
-        MessageDigest md5;
-
-        try {
-            md5 = MessageDigest.getInstance("MD5");
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw new IllegalArgumentException("VM does not support MD5", e);
-        }
-
-        byte[] key = new byte[keyLen];
-        byte[] tmp = new byte[md5.getDigestLength()];
-
-        while (true) {
-            md5.update(password, 0, password.length);
-            md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the
-            // salt in this step.
-            // This took me two hours until I got AES-xxx running.
-            int copy = (keyLen < tmp.length) ? keyLen : tmp.length;
-
-            try {
-                md5.digest(tmp, 0, tmp.length);
-            }
-            catch (DigestException e) {
-                IOException ex = new IOException("could not digest password");
-                ex.initCause(e);
-                throw ex;
-            }
-
-            System.arraycopy(tmp, 0, key, key.length - keyLen, copy);
-            keyLen -= copy;
-
-            if (keyLen == 0)
-                return key;
-
-            md5.update(tmp, 0, tmp.length);
-        }
-    }
-
-    private static byte[] removePadding(byte[] buff, int blockSize) throws IOException {
-        /* Removes RFC 1423/PKCS #7 padding */
-        int rfc_1423_padding = buff[buff.length - 1] & 0xff;
-
-        if ((rfc_1423_padding < 1) || (rfc_1423_padding > blockSize))
-            throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
-
-        for (int i = 2; i <= rfc_1423_padding; i++) {
-            if (buff[buff.length - i] != rfc_1423_padding)
-                throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
-        }
-
-        byte[] tmp = new byte[buff.length - rfc_1423_padding];
-        System.arraycopy(buff, 0, tmp, 0, buff.length - rfc_1423_padding);
-        return tmp;
-    }
-
-    public static final PEMStructure parsePEM(char[] pem) throws IOException {
-        PEMStructure ps = new PEMStructure();
-        String line = null;
-        BufferedReader br = new BufferedReader(new CharArrayReader(pem));
-        String endLine = null;
-
-        while (true) {
-            line = br.readLine();
-
-            if (line == null)
-                throw new IOException("Invalid PEM structure, '-----BEGIN...' missing");
-
-            line = line.trim();
-
-            if (line.startsWith("-----BEGIN DSA PRIVATE KEY-----")) {
-                endLine = "-----END DSA PRIVATE KEY-----";
-                ps.pemType = PEM_DSA_PRIVATE_KEY;
-                break;
-            }
-
-            if (line.startsWith("-----BEGIN RSA PRIVATE KEY-----")) {
-                endLine = "-----END RSA PRIVATE KEY-----";
-                ps.pemType = PEM_RSA_PRIVATE_KEY;
-                break;
-            }
-
-            if (line.startsWith("-----BEGIN EC PRIVATE KEY-----")) {
-                endLine = "-----END EC PRIVATE KEY-----";
-                ps.pemType = PEM_EC_PRIVATE_KEY;
-                break;
-            }
-        }
-
-        while (true) {
-            line = br.readLine();
-
-            if (line == null)
-                throw new IOException("Invalid PEM structure, " + endLine + " missing");
-
-            line = line.trim();
-            int sem_idx = line.indexOf(':');
-
-            if (sem_idx == -1)
-                break;
-
-            String name = line.substring(0, sem_idx + 1);
-            String value = line.substring(sem_idx + 1);
-            String values[] = value.split(",");
-
-            for (int i = 0; i < values.length; i++)
-                values[i] = values[i].trim();
-
-            // Proc-Type: 4,ENCRYPTED
-            // DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483
-
-            if ("Proc-Type:".equals(name)) {
-                ps.procType = values;
-                continue;
-            }
-
-            if ("DEK-Info:".equals(name)) {
-                ps.dekInfo = values;
-                continue;
-            }
-
-            /* Ignore line */
-        }
-
-        StringBuffer keyData = new StringBuffer();
-
-        while (true) {
-            if (line == null)
-                throw new IOException("Invalid PEM structure, " + endLine + " missing");
-
-            line = line.trim();
-
-            if (line.startsWith(endLine))
-                break;
-
-            keyData.append(line);
-            line = br.readLine();
-        }
-
-        char[] pem_chars = new char[keyData.length()];
-        keyData.getChars(0, pem_chars.length, pem_chars, 0);
-        ps.data = Base64.decode(pem_chars);
-
-        if (ps.data.length == 0)
-            throw new IOException("Invalid PEM structure, no data available");
-
-        return ps;
-    }
-
-    private static final void decryptPEM(PEMStructure ps, byte[] pw) throws IOException {
-        if (ps.dekInfo == null)
-            throw new IOException("Broken PEM, no mode and salt given, but encryption enabled");
-
-        if (ps.dekInfo.length != 2)
-            throw new IOException("Broken PEM, DEK-Info is incomplete!");
-
-        String algo = ps.dekInfo[0];
-        byte[] salt = hexToByteArray(ps.dekInfo[1]);
-        BlockCipher bc = null;
-
-        if (algo.equals("DES-EDE3-CBC")) {
-            DESede des3 = new DESede();
-            des3.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
-            bc = new CBCMode(des3, salt, false);
-        }
-        else if (algo.equals("DES-CBC")) {
-            DES des = new DES();
-            des.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 8));
-            bc = new CBCMode(des, salt, false);
-        }
-        else if (algo.equals("AES-128-CBC")) {
-            AES aes = new AES();
-            aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 16));
-            bc = new CBCMode(aes, salt, false);
-        }
-        else if (algo.equals("AES-192-CBC")) {
-            AES aes = new AES();
-            aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
-            bc = new CBCMode(aes, salt, false);
-        }
-        else if (algo.equals("AES-256-CBC")) {
-            AES aes = new AES();
-            aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 32));
-            bc = new CBCMode(aes, salt, false);
-        }
-        else {
-            throw new IOException("Cannot decrypt PEM structure, unknown cipher " + algo);
-        }
-
-        if ((ps.data.length % bc.getBlockSize()) != 0)
-            throw new IOException("Invalid PEM structure, size of encrypted block is not a multiple of "
-                                  + bc.getBlockSize());
-
-        /* Now decrypt the content */
-        byte[] dz = new byte[ps.data.length];
-
-        for (int i = 0; i < ps.data.length / bc.getBlockSize(); i++) {
-            bc.transformBlock(ps.data, i * bc.getBlockSize(), dz, i * bc.getBlockSize());
-        }
-
-        /* Now check and remove RFC 1423/PKCS #7 padding */
-        dz = removePadding(dz, bc.getBlockSize());
-        ps.data = dz;
-        ps.dekInfo = null;
-        ps.procType = null;
-    }
-
-    public static final boolean isPEMEncrypted(PEMStructure ps) throws IOException {
-        if (ps.procType == null)
-            return false;
-
-        if (ps.procType.length != 2)
-            throw new IOException("Unknown Proc-Type field.");
-
-        if ("4".equals(ps.procType[0]) == false)
-            throw new IOException("Unknown Proc-Type field (" + ps.procType[0] + ")");
-
-        if ("ENCRYPTED".equals(ps.procType[1]))
-            return true;
-
-        return false;
-    }
-
-    public static KeyPair decode(char[] pem, String password) throws IOException {
-        PEMStructure ps = parsePEM(pem);
-        return decode(ps, password);
-    }
-
-    public static KeyPair decode(PEMStructure ps, String password) throws IOException {
-        if (isPEMEncrypted(ps)) {
-            if (password == null)
-                throw new IOException("PEM is encrypted, but no password was specified");
-
-            decryptPEM(ps, password.getBytes("ISO-8859-1"));
-        }
-
-        if (ps.pemType == PEM_DSA_PRIVATE_KEY) {
-            SimpleDERReader dr = new SimpleDERReader(ps.data);
-            byte[] seq = dr.readSequenceAsByteArray();
-
-            if (dr.available() != 0)
-                throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
-
-            dr.resetInput(seq);
-            BigInteger version = dr.readInt();
-
-            if (version.compareTo(BigInteger.ZERO) != 0)
-                throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream.");
-
-            BigInteger p = dr.readInt();
-            BigInteger q = dr.readInt();
-            BigInteger g = dr.readInt();
-            BigInteger y = dr.readInt();
-            BigInteger x = dr.readInt();
-
-            if (dr.available() != 0)
-                throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
-
-            DSAPrivateKeySpec privSpec = new DSAPrivateKeySpec(x, p, q, g);
-            DSAPublicKeySpec pubSpec = new DSAPublicKeySpec(y, p, q, g);
-            return generateKeyPair("DSA", privSpec, pubSpec);
-        }
-
-        if (ps.pemType == PEM_RSA_PRIVATE_KEY) {
-            SimpleDERReader dr = new SimpleDERReader(ps.data);
-            byte[] seq = dr.readSequenceAsByteArray();
-
-            if (dr.available() != 0)
-                throw new IOException("Padding in RSA PRIVATE KEY DER stream.");
-
-            dr.resetInput(seq);
-            BigInteger version = dr.readInt();
-
-            if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0))
-                throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream.");
-
-            BigInteger n = dr.readInt();
-            BigInteger e = dr.readInt();
-            BigInteger d = dr.readInt();
-            // TODO: is this right?
-            BigInteger primeP = dr.readInt();
-            BigInteger primeQ = dr.readInt();
-            BigInteger expP = dr.readInt();
-            BigInteger expQ = dr.readInt();
-            BigInteger coeff = dr.readInt();
-            RSAPrivateKeySpec privSpec = new RSAPrivateCrtKeySpec(n, e, d, primeP, primeQ, expP, expQ, coeff);
-            RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(n, e);
-            return generateKeyPair("RSA", privSpec, pubSpec);
-        }
-
-        if (ps.pemType == PEM_EC_PRIVATE_KEY) {
-            SimpleDERReader dr = new SimpleDERReader(ps.data);
-            byte[] seq = dr.readSequenceAsByteArray();
-
-            if (dr.available() != 0)
-                throw new IOException("Padding in EC PRIVATE KEY DER stream.");
-
-            dr.resetInput(seq);
-            BigInteger version = dr.readInt();
-
-            if ((version.compareTo(BigInteger.ONE) != 0))
-                throw new IOException("Wrong version (" + version + ") in EC PRIVATE KEY DER stream.");
-
-            byte[] privateBytes = dr.readOctetString();
-            String curveOid = null;
-            byte[] publicBytes = null;
-
-            while (dr.available() > 0) {
-                int type = dr.readConstructedType();
-                SimpleDERReader cr = dr.readConstructed();
-
-                switch (type) {
-                    case 0:
-                        curveOid = cr.readOid();
-                        break;
-
-                    case 1:
-                        publicBytes = cr.readOctetString();
-                        break;
-                }
-            }
-
-            ECParameterSpec params = ECDSASHA2Verify.getCurveForOID(curveOid);
-
-            if (params == null)
-                throw new IOException("invalid OID");
-
-            BigInteger s = new BigInteger(privateBytes);
-            byte[] publicBytesSlice = new byte[publicBytes.length - 1];
-            System.arraycopy(publicBytes, 1, publicBytesSlice, 0, publicBytesSlice.length);
-            ECPoint w = ECDSASHA2Verify.decodeECPoint(publicBytesSlice, params.getCurve());
-            ECPrivateKeySpec privSpec = new ECPrivateKeySpec(s, params);
-            ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, params);
-            return generateKeyPair("EC", privSpec, pubSpec);
-        }
-
-        throw new IOException("PEM problem: it is of unknown type");
-    }
-
-    /**
-     * Generate a {@code KeyPair} given an {@code algorithm} and {@code KeySpec}.
-     */
-    private static KeyPair generateKeyPair(String algorithm, KeySpec privSpec, KeySpec pubSpec)
-    throws IOException {
-        try {
-            final KeyFactory kf = KeyFactory.getInstance(algorithm);
-            final PublicKey pubKey = kf.generatePublic(pubSpec);
-            final PrivateKey privKey = kf.generatePrivate(privSpec);
-            return new KeyPair(pubKey, privKey);
-        }
-        catch (NoSuchAlgorithmException ex) {
-            IOException ioex = new IOException();
-            ioex.initCause(ex);
-            throw ioex;
-        }
-        catch (InvalidKeySpecException ex) {
-            IOException ioex = new IOException("invalid keyspec");
-            ioex.initCause(ex);
-            throw ioex;
-        }
-    }
-}
--- a/src/com/trilead/ssh2/crypto/PEMStructure.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-
-package com.trilead.ssh2.crypto;
-
-/**
- * Parsed PEM structure.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PEMStructure.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class PEMStructure {
-    public int pemType;
-    String dekInfo[];
-    String procType[];
-    public byte[] data;
-}
\ No newline at end of file
--- a/src/com/trilead/ssh2/crypto/SimpleDERReader.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,205 +0,0 @@
-package com.trilead.ssh2.crypto;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * SimpleDERReader.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SimpleDERReader.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class SimpleDERReader {
-    private static final int CONSTRUCTED = 0x20;
-
-    byte[] buffer;
-    int pos;
-    int count;
-
-    public SimpleDERReader(byte[] b) {
-        resetInput(b);
-    }
-
-    public SimpleDERReader(byte[] b, int off, int len) {
-        resetInput(b, off, len);
-    }
-
-    public void resetInput(byte[] b) {
-        resetInput(b, 0, b.length);
-    }
-
-    public void resetInput(byte[] b, int off, int len) {
-        buffer = b;
-        pos = off;
-        count = len;
-    }
-
-    private byte readByte() throws IOException {
-        if (count <= 0)
-            throw new IOException("DER byte array: out of data");
-
-        count--;
-        return buffer[pos++];
-    }
-
-    private byte[] readBytes(int len) throws IOException {
-        if (len > count)
-            throw new IOException("DER byte array: out of data");
-
-        byte[] b = new byte[len];
-        System.arraycopy(buffer, pos, b, 0, len);
-        pos += len;
-        count -= len;
-        return b;
-    }
-
-    public int available() {
-        return count;
-    }
-
-    private int readLength() throws IOException {
-        int len = readByte() & 0xff;
-
-        if ((len & 0x80) == 0)
-            return len;
-
-        int remain = len & 0x7F;
-
-        if (remain == 0)
-            return -1;
-
-        len = 0;
-
-        while (remain > 0) {
-            len = len << 8;
-            len = len | (readByte() & 0xff);
-            remain--;
-        }
-
-        return len;
-    }
-
-    public int ignoreNextObject() throws IOException {
-        int type = readByte() & 0xff;
-        int len = readLength();
-
-        if ((len < 0) || len > available())
-            throw new IOException("Illegal len in DER object (" + len  + ")");
-
-        readBytes(len);
-        return type;
-    }
-
-    public BigInteger readInt() throws IOException {
-        int type = readByte() & 0xff;
-
-        if (type != 0x02)
-            throw new IOException("Expected DER Integer, but found type " + type);
-
-        int len = readLength();
-
-        if ((len < 0) || len > available())
-            throw new IOException("Illegal len in DER object (" + len  + ")");
-
-        byte[] b = readBytes(len);
-        BigInteger bi = new BigInteger(b);
-        return bi;
-    }
-
-    public int readConstructedType() throws IOException {
-        int type = readByte() & 0xff;
-
-        if ((type & CONSTRUCTED) != CONSTRUCTED)
-            throw new IOException("Expected constructed type, but was " + type);
-
-        return type & 0x1f;
-    }
-
-    public SimpleDERReader readConstructed() throws IOException {
-        int len = readLength();
-
-        if ((len < 0) || len > available())
-            throw new IOException("Illegal len in DER object (" + len  + ")");
-
-        SimpleDERReader cr = new SimpleDERReader(buffer, pos, len);
-        pos += len;
-        count -= len;
-        return cr;
-    }
-
-    public byte[] readSequenceAsByteArray() throws IOException {
-        int type = readByte() & 0xff;
-
-        if (type != 0x30)
-            throw new IOException("Expected DER Sequence, but found type " + type);
-
-        int len = readLength();
-
-        if ((len < 0) || len > available())
-            throw new IOException("Illegal len in DER object (" + len  + ")");
-
-        byte[] b = readBytes(len);
-        return b;
-    }
-
-    public String readOid() throws IOException {
-        int type = readByte() & 0xff;
-
-        if (type != 0x06)
-            throw new IOException("Expected DER OID, but found type " + type);
-
-        int len = readLength();
-
-        if ((len < 1) || len > available())
-            throw new IOException("Illegal len in DER object (" + len  + ")");
-
-        byte[] b = readBytes(len);
-        long value = 0;
-        StringBuilder sb = new StringBuilder(64);
-
-        switch (b[0] / 40) {
-            case 0:
-                sb.append('0');
-                break;
-
-            case 1:
-                sb.append('1');
-                b[0] -= 40;
-                break;
-
-            default:
-                sb.append('2');
-                b[0] -= 80;
-                break;
-        }
-
-        for (int i = 0; i < len; i++) {
-            value = (value << 7) + (b[i] & 0x7F);
-
-            if ((b[i] & 0x80) == 0) {
-                sb.append('.');
-                sb.append(value);
-                value = 0;
-            }
-        }
-
-        return sb.toString();
-    }
-
-    public byte[] readOctetString() throws IOException {
-        int type = readByte() & 0xff;
-
-        if (type != 0x04 && type != 0x03)
-            throw new IOException("Expected DER Octetstring, but found type " + type);
-
-        int len = readLength();
-
-        if ((len < 0) || len > available())
-            throw new IOException("Illegal len in DER object (" + len  + ")");
-
-        byte[] b = readBytes(len);
-        return b;
-    }
-
-}
--- a/src/com/trilead/ssh2/crypto/cipher/AES.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,662 +0,0 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/*
- This file was shamelessly taken from the Bouncy Castle Crypto package.
- Their licence file states the following:
-
- Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
- (http://www.bouncycastle.org)
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
-
-/**
- * An implementation of the AES (Rijndael), from FIPS-197.
- * <p>
- * For further details see: <a
- * href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/
- * </a>.
- *
- * This implementation is based on optimizations from Dr. Brian Gladman's paper
- * and C code at <a
- * href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/
- * </a>
- *
- * There are three levels of tradeoff of speed vs memory Because java has no
- * preprocessor, they are written as three separate classes from which to choose
- *
- * The fastest uses 8Kbytes of static tables to precompute round calculations, 4
- * 256 word tables for encryption and 4 for decryption.
- *
- * The middle performance version uses only one 256 word table for each, for a
- * total of 2Kbytes, adding 12 rotate operations per round to compute the values
- * contained in the other tables from the contents of the first
- *
- * The slowest version uses no static tables at all and computes the values in
- * each round
- * <p>
- * This file contains the fast version with 8Kbytes of static tables for round
- * precomputation
- *
- * @author See comments in the source file
- * @version $Id: AES.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class AES implements BlockCipher {
-    // The S box
-    private static final byte[] S = { (byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107,
-                                      (byte) 111, (byte) 197, (byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171,
-                                      (byte) 118, (byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240,
-                                      (byte) 173, (byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192, (byte) 183,
-                                      (byte) 253, (byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204, (byte) 52, (byte) 165,
-                                      (byte) 229, (byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21, (byte) 4, (byte) 199, (byte) 35,
-                                      (byte) 195, (byte) 24, (byte) 150, (byte) 5, (byte) 154, (byte) 7, (byte) 18, (byte) 128, (byte) 226,
-                                      (byte) 235, (byte) 39, (byte) 178, (byte) 117, (byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27,
-                                      (byte) 110, (byte) 90, (byte) 160, (byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227,
-                                      (byte) 47, (byte) 132, (byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177,
-                                      (byte) 91, (byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207,
-                                      (byte) 208, (byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133, (byte) 69,
-                                      (byte) 249, (byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168, (byte) 81, (byte) 163,
-                                      (byte) 64, (byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245, (byte) 188, (byte) 182, (byte) 218,
-                                      (byte) 33, (byte) 16, (byte) 255, (byte) 243, (byte) 210, (byte) 205, (byte) 12, (byte) 19, (byte) 236,
-                                      (byte) 95, (byte) 151, (byte) 68, (byte) 23, (byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100,
-                                      (byte) 93, (byte) 25, (byte) 115, (byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42,
-                                      (byte) 144, (byte) 136, (byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11,
-                                      (byte) 219, (byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92,
-                                      (byte) 194, (byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121, (byte) 231,
-                                      (byte) 200, (byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169, (byte) 108, (byte) 86,
-                                      (byte) 244, (byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8, (byte) 186, (byte) 120, (byte) 37,
-                                      (byte) 46, (byte) 28, (byte) 166, (byte) 180, (byte) 198, (byte) 232, (byte) 221, (byte) 116, (byte) 31,
-                                      (byte) 75, (byte) 189, (byte) 139, (byte) 138, (byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72,
-                                      (byte) 3, (byte) 246, (byte) 14, (byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193,
-                                      (byte) 29, (byte) 158, (byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142,
-                                      (byte) 148, (byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223,
-                                      (byte) 140, (byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104, (byte) 65,
-                                      (byte) 153, (byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22,
-                                    };
-
-    // The inverse S-box
-    private static final byte[] Si = { (byte) 82, (byte) 9, (byte) 106, (byte) 213, (byte) 48, (byte) 54, (byte) 165,
-                                       (byte) 56, (byte) 191, (byte) 64, (byte) 163, (byte) 158, (byte) 129, (byte) 243, (byte) 215, (byte) 251,
-                                       (byte) 124, (byte) 227, (byte) 57, (byte) 130, (byte) 155, (byte) 47, (byte) 255, (byte) 135, (byte) 52,
-                                       (byte) 142, (byte) 67, (byte) 68, (byte) 196, (byte) 222, (byte) 233, (byte) 203, (byte) 84, (byte) 123,
-                                       (byte) 148, (byte) 50, (byte) 166, (byte) 194, (byte) 35, (byte) 61, (byte) 238, (byte) 76, (byte) 149,
-                                       (byte) 11, (byte) 66, (byte) 250, (byte) 195, (byte) 78, (byte) 8, (byte) 46, (byte) 161, (byte) 102,
-                                       (byte) 40, (byte) 217, (byte) 36, (byte) 178, (byte) 118, (byte) 91, (byte) 162, (byte) 73, (byte) 109,
-                                       (byte) 139, (byte) 209, (byte) 37, (byte) 114, (byte) 248, (byte) 246, (byte) 100, (byte) 134, (byte) 104,
-                                       (byte) 152, (byte) 22, (byte) 212, (byte) 164, (byte) 92, (byte) 204, (byte) 93, (byte) 101, (byte) 182,
-                                       (byte) 146, (byte) 108, (byte) 112, (byte) 72, (byte) 80, (byte) 253, (byte) 237, (byte) 185, (byte) 218,
-                                       (byte) 94, (byte) 21, (byte) 70, (byte) 87, (byte) 167, (byte) 141, (byte) 157, (byte) 132, (byte) 144,
-                                       (byte) 216, (byte) 171, (byte) 0, (byte) 140, (byte) 188, (byte) 211, (byte) 10, (byte) 247, (byte) 228,
-                                       (byte) 88, (byte) 5, (byte) 184, (byte) 179, (byte) 69, (byte) 6, (byte) 208, (byte) 44, (byte) 30,
-                                       (byte) 143, (byte) 202, (byte) 63, (byte) 15, (byte) 2, (byte) 193, (byte) 175, (byte) 189, (byte) 3,
-                                       (byte) 1, (byte) 19, (byte) 138, (byte) 107, (byte) 58, (byte) 145, (byte) 17, (byte) 65, (byte) 79,
-                                       (byte) 103, (byte) 220, (byte) 234, (byte) 151, (byte) 242, (byte) 207, (byte) 206, (byte) 240, (byte) 180,
-                                       (byte) 230, (byte) 115, (byte) 150, (byte) 172, (byte) 116, (byte) 34, (byte) 231, (byte) 173, (byte) 53,
-                                       (byte) 133, (byte) 226, (byte) 249, (byte) 55, (byte) 232, (byte) 28, (byte) 117, (byte) 223, (byte) 110,
-                                       (byte) 71, (byte) 241, (byte) 26, (byte) 113, (byte) 29, (byte) 41, (byte) 197, (byte) 137, (byte) 111,
-                                       (byte) 183, (byte) 98, (byte) 14, (byte) 170, (byte) 24, (byte) 190, (byte) 27, (byte) 252, (byte) 86,
-                                       (byte) 62, (byte) 75, (byte) 198, (byte) 210, (byte) 121, (byte) 32, (byte) 154, (byte) 219, (byte) 192,
-                                       (byte) 254, (byte) 120, (byte) 205, (byte) 90, (byte) 244, (byte) 31, (byte) 221, (byte) 168, (byte) 51,
-                                       (byte) 136, (byte) 7, (byte) 199, (byte) 49, (byte) 177, (byte) 18, (byte) 16, (byte) 89, (byte) 39,
-                                       (byte) 128, (byte) 236, (byte) 95, (byte) 96, (byte) 81, (byte) 127, (byte) 169, (byte) 25, (byte) 181,
-                                       (byte) 74, (byte) 13, (byte) 45, (byte) 229, (byte) 122, (byte) 159, (byte) 147, (byte) 201, (byte) 156,
-                                       (byte) 239, (byte) 160, (byte) 224, (byte) 59, (byte) 77, (byte) 174, (byte) 42, (byte) 245, (byte) 176,
-                                       (byte) 200, (byte) 235, (byte) 187, (byte) 60, (byte) 131, (byte) 83, (byte) 153, (byte) 97, (byte) 23,
-                                       (byte) 43, (byte) 4, (byte) 126, (byte) 186, (byte) 119, (byte) 214, (byte) 38, (byte) 225, (byte) 105,
-                                       (byte) 20, (byte) 99, (byte) 85, (byte) 33, (byte) 12, (byte) 125,
-                                     };
-
-    // vector used in calculating key schedule (powers of x in GF(256))
-    private static final int[] rcon = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab,
-                                        0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91
-                                      };
-
-    // precomputation tables of calculations for rounds
-    private static final int[] T0 = { 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6,
-                                      0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
-                                      0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
-                                      0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775,
-                                      0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551,
-                                      0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, 0x65232346,
-                                      0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
-                                      0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36,
-                                      0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd,
-                                      0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179,
-                                      0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
-                                      0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
-                                      0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d,
-                                      0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf,
-                                      0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
-                                      0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8,
-                                      0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
-                                      0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16,
-                                      0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
-                                      0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5,
-                                      0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac,
-                                      0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
-                                      0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
-                                      0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890,
-                                      0x05030306, 0x01f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199,
-                                      0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07,
-                                      0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
-                                      0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182,
-                                      0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c
-                                    };
-
-    private static final int[] T1 = { 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd,
-                                      0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
-                                      0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
-                                      0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2,
-                                      0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4,
-                                      0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, 0x23234665,
-                                      0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
-                                      0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d,
-                                      0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e,
-                                      0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8,
-                                      0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
-                                      0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf,
-                                      0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe,
-                                      0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75,
-                                      0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f,
-                                      0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac,
-                                      0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
-                                      0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d,
-                                      0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
-                                      0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532,
-                                      0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa,
-                                      0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f,
-                                      0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21,
-                                      0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8,
-                                      0x03030605, 0xf6f6f701, 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958,
-                                      0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789,
-                                      0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
-                                      0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3,
-                                      0x999929b0, 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a
-                                    };
-
-    private static final int[] T2 = { 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b,
-                                      0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
-                                      0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
-                                      0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7,
-                                      0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5,
-                                      0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, 0x23466523,
-                                      0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
-                                      0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b,
-                                      0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3,
-                                      0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1,
-                                      0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
-                                      0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45,
-                                      0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3,
-                                      0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da,
-                                      0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec,
-                                      0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64,
-                                      0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
-                                      0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b,
-                                      0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
-                                      0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7,
-                                      0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56,
-                                      0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25,
-                                      0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f,
-                                      0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848,
-                                      0x03060503, 0xf6f701f6, 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1,
-                                      0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e,
-                                      0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
-                                      0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341,
-                                      0x9929b099, 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16
-                                    };
-
-    private static final int[] T3 = { 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b,
-                                      0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
-                                      0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
-                                      0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7,
-                                      0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5,
-                                      0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, 0x46652323,
-                                      0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
-                                      0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b,
-                                      0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3,
-                                      0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1,
-                                      0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
-                                      0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545,
-                                      0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3,
-                                      0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada,
-                                      0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec,
-                                      0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464,
-                                      0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
-                                      0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b,
-                                      0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
-                                      0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7,
-                                      0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656,
-                                      0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525,
-                                      0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f,
-                                      0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848,
-                                      0x06050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1,
-                                      0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x07898e8e,
-                                      0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
-                                      0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141,
-                                      0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616
-                                    };
-
-    private static final int[] Tinv0 = { 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f,
-                                         0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
-                                         0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b,
-                                         0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275,
-                                         0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5,
-                                         0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94,
-                                         0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
-                                         0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65,
-                                         0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040,
-                                         0x069f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x055dc471, 0x6fd40604,
-                                         0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879,
-                                         0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
-                                         0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793,
-                                         0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0x0aba93e2, 0xe52aa0c0, 0x43e0223c,
-                                         0x1d171b12, 0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3,
-                                         0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7,
-                                         0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
-                                         0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56,
-                                         0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f,
-                                         0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f,
-                                         0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, 0xf418596e,
-                                         0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
-                                         0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733,
-                                         0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e,
-                                         0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92,
-                                         0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1,
-                                         0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
-                                         0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839,
-                                         0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0
-                                       };
-
-    private static final int[] Tinv1 = { 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1,
-                                         0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
-                                         0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6,
-                                         0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a,
-                                         0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582,
-                                         0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487,
-                                         0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
-                                         0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd,
-                                         0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa,
-                                         0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f,
-                                         0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db,
-                                         0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb,
-                                         0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f,
-                                         0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43,
-                                         0x171b121d, 0x0d090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, 0xdd99eebb, 0x607fa3fd,
-                                         0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca,
-                                         0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3,
-                                         0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8,
-                                         0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4,
-                                         0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe,
-                                         0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4,
-                                         0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4,
-                                         0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315,
-                                         0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x0496e4df, 0xb5d19ee3,
-                                         0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252,
-                                         0x5610e933, 0x47d66d13, 0x61d79a8c, 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed,
-                                         0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
-                                         0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971,
-                                         0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042
-                                       };
-
-    private static final int[] Tinv2 = { 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145,
-                                         0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
-                                         0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9,
-                                         0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89,
-                                         0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231,
-                                         0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, 0xde94876c,
-                                         0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
-                                         0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4,
-                                         0x0506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef,
-                                         0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x06046fd4,
-                                         0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee,
-                                         0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff,
-                                         0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7,
-                                         0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0,
-                                         0x1b121d17, 0x090e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60,
-                                         0x01f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc,
-                                         0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330,
-                                         0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c,
-                                         0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf,
-                                         0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80,
-                                         0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418,
-                                         0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409,
-                                         0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8,
-                                         0x04f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5,
-                                         0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, 0x67b35a1d, 0xdb9252d2,
-                                         0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5,
-                                         0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
-                                         0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101,
-                                         0x0c08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257
-                                       };
-
-    private static final int[] Tinv3 = { 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d,
-                                         0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
-                                         0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3,
-                                         0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2,
-                                         0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a,
-                                         0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde,
-                                         0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
-                                         0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da,
-                                         0x06d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6, 0x0b39ec83, 0x40aaef60,
-                                         0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x046fd406,
-                                         0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8,
-                                         0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e,
-                                         0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757,
-                                         0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022,
-                                         0x121d171b, 0x0e0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f,
-                                         0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31,
-                                         0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2,
-                                         0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d,
-                                         0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad,
-                                         0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d,
-                                         0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859,
-                                         0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f,
-                                         0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7,
-                                         0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1,
-                                         0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db,
-                                         0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c,
-                                         0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
-                                         0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8,
-                                         0x08deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8
-                                       };
-
-    private final int shift(int r, int shift) {
-        return (((r >>> shift) | (r << (32 - shift))));
-    }
-
-    /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
-
-    private static final int m1 = 0x80808080;
-    private static final int m2 = 0x7f7f7f7f;
-    private static final int m3 = 0x0000001b;
-
-    private final int FFmulX(int x) {
-        return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
-    }
-
-    /*
-     * The following defines provide alternative definitions of FFmulX that
-     * might give improved performance if a fast 32-bit multiply is not
-     * available.
-     *
-     * private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x &
-     * m2) < < 1) ^ ((u >>> 3) | (u >>> 6)); } private static final int m4 =
-     * 0x1b1b1b1b; private int FFmulX(int x) { int u = x & m1; return ((x & m2) < <
-     * 1) ^ ((u - (u >>> 7)) & m4); }
-     *
-     */
-
-    private final int inv_mcol(int x) {
-        int f2 = FFmulX(x);
-        int f4 = FFmulX(f2);
-        int f8 = FFmulX(f4);
-        int f9 = x ^ f8;
-        return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
-    }
-
-    private final int subWord(int x) {
-        return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16) | S[(x >> 24) & 255] << 24);
-    }
-
-    /**
-     * Calculate the necessary round keys The number of calculations depends on
-     * key size and block size AES specified a fixed block size of 128 bits and
-     * key sizes 128/192/256 bits This code is written assuming those are the
-     * only possible values
-     */
-    private final int[][] generateWorkingKey(byte[] key, boolean forEncryption) {
-        int KC = key.length / 4; // key length in words
-        int t;
-
-        if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length)) {
-            throw new IllegalArgumentException("Key length not 128/192/256 bits.");
-        }
-
-        ROUNDS = KC + 6; // This is not always true for the generalized
-        // Rijndael that allows larger block sizes
-        int[][] W = new int[ROUNDS + 1][4]; // 4 words in a block
-        //
-        // copy the key into the round key array
-        //
-        t = 0;
-
-        for (int i = 0; i < key.length; t++) {
-            W[t >> 2][t & 3] = (key[i] & 0xff) | ((key[i + 1] & 0xff) << 8) | ((key[i + 2] & 0xff) << 16)
-                               | (key[i + 3] << 24);
-            i += 4;
-        }
-
-        //
-        // while not enough round key material calculated
-        // calculate new values
-        //
-        int k = (ROUNDS + 1) << 2;
-
-        for (int i = KC; (i < k); i++) {
-            int temp = W[(i - 1) >> 2][(i - 1) & 3];
-
-            if ((i % KC) == 0) {
-                temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1];
-            }
-            else if ((KC > 6) && ((i % KC) == 4)) {
-                temp = subWord(temp);
-            }
-
-            W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp;
-        }
-
-        if (!forEncryption) {
-            for (int j = 1; j < ROUNDS; j++) {
-                for (int i = 0; i < 4; i++) {
-                    W[j][i] = inv_mcol(W[j][i]);
-                }
-            }
-        }
-
-        return W;
-    }
-
-    private int ROUNDS;
-    private int[][] WorkingKey = null;
-    private int C0, C1, C2, C3;
-    private boolean doEncrypt;
-
-    private static final int BLOCK_SIZE = 16;
-
-    /**
-     * default constructor - 128 bit block size.
-     */
-    public AES() {
-    }
-
-    /**
-     * initialise an AES cipher.
-     *
-     * @param forEncryption
-     *            whether or not we are for encryption.
-     * @param key
-     *            the key required to set up the cipher.
-     * @exception IllegalArgumentException
-     *                if the params argument is inappropriate.
-     */
-
-    public final void init(boolean forEncryption, byte[] key) {
-        WorkingKey = generateWorkingKey(key, forEncryption);
-        this.doEncrypt = forEncryption;
-    }
-
-    public final String getAlgorithmName() {
-        return "AES";
-    }
-
-    public final int getBlockSize() {
-        return BLOCK_SIZE;
-    }
-
-    public final int processBlock(byte[] in, int inOff, byte[] out, int outOff) {
-        if (WorkingKey == null) {
-            throw new IllegalStateException("AES engine not initialised");
-        }
-
-        if ((inOff + (32 / 2)) > in.length) {
-            throw new IllegalArgumentException("input buffer too short");
-        }
-
-        if ((outOff + (32 / 2)) > out.length) {
-            throw new IllegalArgumentException("output buffer too short");
-        }
-
-        if (doEncrypt) {
-            unpackBlock(in, inOff);
-            encryptBlock(WorkingKey);
-            packBlock(out, outOff);
-        }
-        else {
-            unpackBlock(in, inOff);
-            decryptBlock(WorkingKey);
-            packBlock(out, outOff);
-        }
-
-        return BLOCK_SIZE;
-    }
-
-    public final void reset() {
-    }
-
-    private final void unpackBlock(byte[] bytes, int off) {
-        int index = off;
-        C0 = (bytes[index++] & 0xff);
-        C0 |= (bytes[index++] & 0xff) << 8;
-        C0 |= (bytes[index++] & 0xff) << 16;
-        C0 |= bytes[index++] << 24;
-        C1 = (bytes[index++] & 0xff);
-        C1 |= (bytes[index++] & 0xff) << 8;
-        C1 |= (bytes[index++] & 0xff) << 16;
-        C1 |= bytes[index++] << 24;
-        C2 = (bytes[index++] & 0xff);
-        C2 |= (bytes[index++] & 0xff) << 8;
-        C2 |= (bytes[index++] & 0xff) << 16;
-        C2 |= bytes[index++] << 24;
-        C3 = (bytes[index++] & 0xff);
-        C3 |= (bytes[index++] & 0xff) << 8;
-        C3 |= (bytes[index++] & 0xff) << 16;
-        C3 |= bytes[index++] << 24;
-    }
-
-    private final void packBlock(byte[] bytes, int off) {
-        int index = off;
-        bytes[index++] = (byte) C0;
-        bytes[index++] = (byte)(C0 >> 8);
-        bytes[index++] = (byte)(C0 >> 16);
-        bytes[index++] = (byte)(C0 >> 24);
-        bytes[index++] = (byte) C1;
-        bytes[index++] = (byte)(C1 >> 8);
-        bytes[index++] = (byte)(C1 >> 16);
-        bytes[index++] = (byte)(C1 >> 24);
-        bytes[index++] = (byte) C2;
-        bytes[index++] = (byte)(C2 >> 8);
-        bytes[index++] = (byte)(C2 >> 16);
-        bytes[index++] = (byte)(C2 >> 24);
-        bytes[index++] = (byte) C3;
-        bytes[index++] = (byte)(C3 >> 8);
-        bytes[index++] = (byte)(C3 >> 16);
-        bytes[index++] = (byte)(C3 >> 24);
-    }
-
-    private final void encryptBlock(int[][] KW) {
-        int r, r0, r1, r2, r3;
-        C0 ^= KW[0][0];
-        C1 ^= KW[0][1];
-        C2 ^= KW[0][2];
-        C3 ^= KW[0][3];
-
-        for (r = 1; r < ROUNDS - 1;) {
-            r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
-            r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
-            r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
-            r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
-            C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[(r3 >> 24) & 255] ^ KW[r][0];
-            C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[(r0 >> 24) & 255] ^ KW[r][1];
-            C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[(r1 >> 24) & 255] ^ KW[r][2];
-            C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[(r2 >> 24) & 255] ^ KW[r++][3];
-        }
-
-        r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
-        r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
-        r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
-        r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
-        // the final round's table is a simple function of S so we don't use a
-        // whole other four tables for it
-        C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16)
-             ^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0];
-        C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16)
-             ^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1];
-        C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16)
-             ^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2];
-        C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16)
-             ^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3];
-    }
-
-    private final void decryptBlock(int[][] KW) {
-        int r, r0, r1, r2, r3;
-        C0 ^= KW[ROUNDS][0];
-        C1 ^= KW[ROUNDS][1];
-        C2 ^= KW[ROUNDS][2];
-        C3 ^= KW[ROUNDS][3];
-
-        for (r = ROUNDS - 1; r > 1;) {
-            r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255]
-                 ^ KW[r][0];
-            r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255]
-                 ^ KW[r][1];
-            r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255]
-                 ^ KW[r][2];
-            r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255]
-                 ^ KW[r--][3];
-            C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[(r1 >> 24) & 255]
-                 ^ KW[r][0];
-            C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[(r2 >> 24) & 255]
-                 ^ KW[r][1];
-            C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[(r3 >> 24) & 255]
-                 ^ KW[r][2];
-            C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[(r0 >> 24) & 255]
-                 ^ KW[r--][3];
-        }
-
-        r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] ^ KW[r][0];
-        r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] ^ KW[r][1];
-        r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] ^ KW[r][2];
-        r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] ^ KW[r--][3];
-        // the final round's table is a simple function of Si so we don't use a
-        // whole other four tables for it
-        C0 = (Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(r2 >> 16) & 255] & 255) << 16)
-             ^ (Si[(r1 >> 24) & 255] << 24) ^ KW[0][0];
-        C1 = (Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16)
-             ^ (Si[(r2 >> 24) & 255] << 24) ^ KW[0][1];
-        C2 = (Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) ^ ((Si[(r0 >> 16) & 255] & 255) << 16)
-             ^ (Si[(r3 >> 24) & 255] << 24) ^ KW[0][2];
-        C3 = (Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) ^ ((Si[(r1 >> 16) & 255] & 255) << 16)
-             ^ (Si[(r0 >> 24) & 255] << 24) ^ KW[0][3];
-    }
-
-    public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
-        processBlock(src, srcoff, dst, dstoff);
-    }
-}
--- a/src/com/trilead/ssh2/crypto/cipher/BlockCipher.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * BlockCipher.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: BlockCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public interface BlockCipher {
-    public void init(boolean forEncryption, byte[] key);
-
-    public int getBlockSize();
-
-    public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff);
-}
--- a/src/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-import java.util.Vector;
-
-/**
- * BlockCipherFactory.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: BlockCipherFactory.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class BlockCipherFactory {
-    static class CipherEntry {
-        String type;
-        int blocksize;
-        int keysize;
-        String cipherClass;
-
-        public CipherEntry(String type, int blockSize, int keySize, String cipherClass) {
-            this.type = type;
-            this.blocksize = blockSize;
-            this.keysize = keySize;
-            this.cipherClass = cipherClass;
-        }
-    }
-
-    static Vector<CipherEntry> ciphers = new Vector<CipherEntry>();
-
-    static {
-        /* Higher Priority First */
-        ciphers.addElement(new CipherEntry("aes256-ctr", 16, 32, "com.trilead.ssh2.crypto.cipher.AES"));
-        ciphers.addElement(new CipherEntry("aes192-ctr", 16, 24, "com.trilead.ssh2.crypto.cipher.AES"));
-        ciphers.addElement(new CipherEntry("aes128-ctr", 16, 16, "com.trilead.ssh2.crypto.cipher.AES"));
-        ciphers.addElement(new CipherEntry("blowfish-ctr", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish"));
-        ciphers.addElement(new CipherEntry("aes256-cbc", 16, 32, "com.trilead.ssh2.crypto.cipher.AES"));
-        ciphers.addElement(new CipherEntry("aes192-cbc", 16, 24, "com.trilead.ssh2.crypto.cipher.AES"));
-        ciphers.addElement(new CipherEntry("aes128-cbc", 16, 16, "com.trilead.ssh2.crypto.cipher.AES"));
-        ciphers.addElement(new CipherEntry("blowfish-cbc", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish"));
-        ciphers.addElement(new CipherEntry("3des-ctr", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede"));
-        ciphers.addElement(new CipherEntry("3des-cbc", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede"));
-    }
-
-    public static String[] getDefaultCipherList() {
-        String list[] = new String[ciphers.size()];
-
-        for (int i = 0; i < ciphers.size(); i++) {
-            CipherEntry ce = ciphers.elementAt(i);
-            list[i] = new String(ce.type);
-        }
-
-        return list;
-    }
-
-    public static void checkCipherList(String[] cipherCandidates) {
-        for (int i = 0; i < cipherCandidates.length; i++)
-            getEntry(cipherCandidates[i]);
-    }
-
-    public static BlockCipher createCipher(String type, boolean encrypt, byte[] key, byte[] iv) {
-        try {
-            CipherEntry ce = getEntry(type);
-            Class cc = Class.forName(ce.cipherClass);
-            BlockCipher bc = (BlockCipher) cc.newInstance();
-
-            if (type.endsWith("-cbc")) {
-                bc.init(encrypt, key);
-                return new CBCMode(bc, iv, encrypt);
-            }
-            else if (type.endsWith("-ctr")) {
-                bc.init(true, key);
-                return new CTRMode(bc, iv, encrypt);
-            }
-
-            throw new IllegalArgumentException("Cannot instantiate " + type);
-        }
-        catch (Exception e) {
-            throw new IllegalArgumentException("Cannot instantiate " + type);
-        }
-    }
-
-    private static CipherEntry getEntry(String type) {
-        for (int i = 0; i < ciphers.size(); i++) {
-            CipherEntry ce = ciphers.elementAt(i);
-
-            if (ce.type.equals(type))
-                return ce;
-        }
-
-        throw new IllegalArgumentException("Unkown algorithm " + type);
-    }
-
-    public static int getBlockSize(String type) {
-        CipherEntry ce = getEntry(type);
-        return ce.blocksize;
-    }
-
-    public static int getKeySize(String type) {
-        CipherEntry ce = getEntry(type);
-        return ce.keysize;
-    }
-}
--- a/src/com/trilead/ssh2/crypto/cipher/BlowFish.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,377 +0,0 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/*
- This file was shamelessly taken from the Bouncy Castle Crypto package.
- Their licence file states the following:
-
- Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
- (http://www.bouncycastle.org)
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
-
-/**
- * A class that provides Blowfish key encryption operations, such as encoding
- * data and generating keys. All the algorithms herein are from Applied
- * Cryptography and implement a simplified cryptography interface.
- *
- * @author See comments in the source file
- * @version $Id: BlowFish.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class BlowFish implements BlockCipher {
-
-    private final static int[] KP = { 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0,
-                                      0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5,
-                                      0xB5470917, 0x9216D5D9, 0x8979FB1B
-                                    },
-
-                               KS0 = { 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, 0x24A19947,
-                                       0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658,
-                                       0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918,
-                                       0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
-                                       0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF,
-                                       0x7C72E993, 0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C,
-                                       0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60,
-                                       0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
-                                       0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, 0x6A51A0D2,
-                                       0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176,
-                                       0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F,
-                                       0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
-                                       0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6,
-                                       0x409F60C4, 0x5E5C9EC2, 0x196A2463, 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C,
-                                       0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39,
-                                       0xB9D3FBDB, 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
-                                       0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, 0x9E5C57BB,
-                                       0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8,
-                                       0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, 0x9A53E479, 0xB6F84565, 0xD28E49BC,
-                                       0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
-                                       0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB,
-                                       0xF2122B64, 0x8888B812, 0x900DF01C, 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777,
-                                       0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81,
-                                       0xD2ADA8D9, 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
-                                       0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, 0x2464369B,
-                                       0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9,
-                                       0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476,
-                                       0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
-                                       0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A
-                                     },
-
-                               KS1 = { 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71,
-                                       0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65,
-                                       0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26, 0x6382E9C6,
-                                       0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
-                                       0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A,
-                                       0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC,
-                                       0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1,
-                                       0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
-                                       0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, 0x7A584718,
-                                       0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908,
-                                       0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55, 0x81AC77D6,
-                                       0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
-                                       0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6,
-                                       0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D,
-                                       0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1,
-                                       0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
-                                       0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, 0xB5735C90,
-                                       0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA,
-                                       0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099, 0x95F7997E,
-                                       0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
-                                       0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF,
-                                       0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA,
-                                       0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A,
-                                       0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
-                                       0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, 0x3372F092,
-                                       0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E,
-                                       0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484, 0xFDD56705,
-                                       0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
-                                       0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7
-                                     },
-
-                               KS2 = { 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471,
-                                       0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF,
-                                       0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6,
-                                       0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
-                                       0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35,
-                                       0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332,
-                                       0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7,
-                                       0xD096954B, 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
-                                       0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE,
-                                       0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60,
-                                       0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62,
-                                       0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
-                                       0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60,
-                                       0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3,
-                                       0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF,
-                                       0x763BD6EB, 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
-                                       0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659,
-                                       0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086,
-                                       0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187,
-                                       0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
-                                       0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E,
-                                       0x20B45770, 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09,
-                                       0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F,
-                                       0x2868F169, 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
-                                       0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7,
-                                       0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188,
-                                       0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3,
-                                       0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
-                                       0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0
-                                     },
-
-                               KS3 = { 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, 0xD5118E9D,
-                                       0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79,
-                                       0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, 0x2939BBDB, 0xA9BA4650, 0xAC9526E8,
-                                       0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
-                                       0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3,
-                                       0xF752F7DA, 0x3F046F69, 0x77FA0A59, 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797,
-                                       0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472,
-                                       0x5A88F54C, 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
-                                       0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, 0xC3EB9E15,
-                                       0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5,
-                                       0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, 0x4DE81751, 0x3830DC8E, 0x379D5862,
-                                       0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
-                                       0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD,
-                                       0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB,
-                                       0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671,
-                                       0xAF537D5D, 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
-                                       0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, 0xA08839E1,
-                                       0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A,
-                                       0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF,
-                                       0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
-                                       0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532,
-                                       0xE0D392DF, 0xD3A0342B, 0x8971F21E, 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E,
-                                       0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5,
-                                       0xF6FB2299, 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
-                                       0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD,
-                                       0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3,
-                                       0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0,
-                                       0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
-                                       0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6
-                                     };
-
-    // ====================================
-    // Useful constants
-    // ====================================
-
-    private static final int ROUNDS = 16;
-    private static final int BLOCK_SIZE = 8; // bytes = 64 bits
-    private static final int SBOX_SK = 256;
-    private static final int P_SZ = ROUNDS + 2;
-
-    private final int[] S0, S1, S2, S3; // the s-boxes
-    private final int[] P; // the p-array
-
-    private boolean doEncrypt = false;
-
-    private byte[] workingKey = null;
-
-    public BlowFish() {
-        S0 = new int[SBOX_SK];
-        S1 = new int[SBOX_SK];
-        S2 = new int[SBOX_SK];
-        S3 = new int[SBOX_SK];
-        P = new int[P_SZ];
-    }
-
-    /**
-     * initialise a Blowfish cipher.
-     *
-     * @param encrypting
-     *            whether or not we are for encryption.
-     * @param key
-     *            the key required to set up the cipher.
-     * @exception IllegalArgumentException
-     *                if the params argument is inappropriate.
-     */
-    public void init(boolean encrypting, byte[] key) {
-        this.doEncrypt = encrypting;
-        this.workingKey = key;
-        setKey(this.workingKey);
-    }
-
-    public String getAlgorithmName() {
-        return "Blowfish";
-    }
-
-    public final void transformBlock(byte[] in, int inOff, byte[] out, int outOff) {
-        if (workingKey == null) {
-            throw new IllegalStateException("Blowfish not initialised");
-        }
-
-        if (doEncrypt) {
-            encryptBlock(in, inOff, out, outOff);
-        }
-        else {
-            decryptBlock(in, inOff, out, outOff);
-        }
-    }
-
-    public void reset() {
-    }
-
-    public int getBlockSize() {
-        return BLOCK_SIZE;
-    }
-
-    // ==================================
-    // Private Implementation
-    // ==================================
-
-    private int F(int x) {
-        return (((S0[(x >>> 24)] + S1[(x >>> 16) & 0xff]) ^ S2[(x >>> 8) & 0xff]) + S3[x & 0xff]);
-    }
-
-    /**
-     * apply the encryption cycle to each value pair in the table.
-     */
-    private void processTable(int xl, int xr, int[] table) {
-        int size = table.length;
-
-        for (int s = 0; s < size; s += 2) {
-            xl ^= P[0];
-
-            for (int i = 1; i < ROUNDS; i += 2) {
-                xr ^= F(xl) ^ P[i];
-                xl ^= F(xr) ^ P[i + 1];
-            }
-
-            xr ^= P[ROUNDS + 1];
-            table[s] = xr;
-            table[s + 1] = xl;
-            xr = xl; // end of cycle swap
-            xl = table[s];
-        }
-    }
-
-    private void setKey(byte[] key) {
-        /*
-         * - comments are from _Applied Crypto_, Schneier, p338 please be
-         * careful comparing the two, AC numbers the arrays from 1, the enclosed
-         * code from 0.
-         *
-         * (1) Initialise the S-boxes and the P-array, with a fixed string This
-         * string contains the hexadecimal digits of pi (3.141...)
-         */
-        System.arraycopy(KS0, 0, S0, 0, SBOX_SK);
-        System.arraycopy(KS1, 0, S1, 0, SBOX_SK);
-        System.arraycopy(KS2, 0, S2, 0, SBOX_SK);
-        System.arraycopy(KS3, 0, S3, 0, SBOX_SK);
-        System.arraycopy(KP, 0, P, 0, P_SZ);
-        /*
-         * (2) Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with
-         * the second 32-bits of the key, and so on for all bits of the key (up
-         * to P[17]). Repeatedly cycle through the key bits until the entire
-         * P-array has been XOR-ed with the key bits
-         */
-        int keyLength = key.length;
-        int keyIndex = 0;
-
-        for (int i = 0; i < P_SZ; i++) {
-            // get the 32 bits of the key, in 4 * 8 bit chunks
-            int data = 0x0000000;
-
-            for (int j = 0; j < 4; j++) {
-                // create a 32 bit block
-                data = (data << 8) | (key[keyIndex++] & 0xff);
-
-                // wrap when we get to the end of the key
-                if (keyIndex >= keyLength) {
-                    keyIndex = 0;
-                }
-            }
-
-            // XOR the newly created 32 bit chunk onto the P-array
-            P[i] ^= data;
-        }
-
-        /*
-         * (3) Encrypt the all-zero string with the Blowfish algorithm, using
-         * the subkeys described in (1) and (2)
-         *
-         * (4) Replace P1 and P2 with the output of step (3)
-         *
-         * (5) Encrypt the output of step(3) using the Blowfish algorithm, with
-         * the modified subkeys.
-         *
-         * (6) Replace P3 and P4 with the output of step (5)
-         *
-         * (7) Continue the process, replacing all elements of the P-array and
-         * then all four S-boxes in order, with the output of the continuously
-         * changing Blowfish algorithm
-         */
-        processTable(0, 0, P);
-        processTable(P[P_SZ - 2], P[P_SZ - 1], S0);
-        processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1);
-        processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2);
-        processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3);
-    }
-
-    /**
-     * Encrypt the given input starting at the given offset and place the result
-     * in the provided buffer starting at the given offset. The input will be an
-     * exact multiple of our blocksize.
-     */
-    private void encryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) {
-        int xl = BytesTo32bits(src, srcIndex);
-        int xr = BytesTo32bits(src, srcIndex + 4);
-        xl ^= P[0];
-
-        for (int i = 1; i < ROUNDS; i += 2) {
-            xr ^= F(xl) ^ P[i];
-            xl ^= F(xr) ^ P[i + 1];
-        }
-
-        xr ^= P[ROUNDS + 1];
-        Bits32ToBytes(xr, dst, dstIndex);
-        Bits32ToBytes(xl, dst, dstIndex + 4);
-    }
-
-    /**
-     * Decrypt the given input starting at the given offset and place the result
-     * in the provided buffer starting at the given offset. The input will be an
-     * exact multiple of our blocksize.
-     */
-    private void decryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) {
-        int xl = BytesTo32bits(src, srcIndex);
-        int xr = BytesTo32bits(src, srcIndex + 4);
-        xl ^= P[ROUNDS + 1];
-
-        for (int i = ROUNDS; i > 0; i -= 2) {
-            xr ^= F(xl) ^ P[i];
-            xl ^= F(xr) ^ P[i - 1];
-        }
-
-        xr ^= P[0];
-        Bits32ToBytes(xr, dst, dstIndex);
-        Bits32ToBytes(xl, dst, dstIndex + 4);
-    }
-
-    private int BytesTo32bits(byte[] b, int i) {
-        return ((b[i] & 0xff) << 24) | ((b[i + 1] & 0xff) << 16) | ((b[i + 2] & 0xff) << 8) | ((b[i + 3] & 0xff));
-    }
-
-    private void Bits32ToBytes(int in, byte[] b, int offset) {
-        b[offset + 3] = (byte) in;
-        b[offset + 2] = (byte)(in >> 8);
-        b[offset + 1] = (byte)(in >> 16);
-        b[offset] = (byte)(in >> 24);
-    }
-
-}
--- a/src/com/trilead/ssh2/crypto/cipher/CBCMode.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * CBCMode.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: CBCMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class CBCMode implements BlockCipher {
-    BlockCipher tc;
-    int blockSize;
-    boolean doEncrypt;
-
-    byte[] cbc_vector;
-    byte[] tmp_vector;
-
-    public void init(boolean forEncryption, byte[] key) {
-    }
-
-    public CBCMode(BlockCipher tc, byte[] iv, boolean doEncrypt)
-    throws IllegalArgumentException {
-        this.tc = tc;
-        this.blockSize = tc.getBlockSize();
-        this.doEncrypt = doEncrypt;
-
-        if (this.blockSize != iv.length)
-            throw new IllegalArgumentException("IV must be " + blockSize
-                                               + " bytes long! (currently " + iv.length + ")");
-
-        this.cbc_vector = new byte[blockSize];
-        this.tmp_vector = new byte[blockSize];
-        System.arraycopy(iv, 0, cbc_vector, 0, blockSize);
-    }
-
-    public int getBlockSize() {
-        return blockSize;
-    }
-
-    private void encryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
-        for (int i = 0; i < blockSize; i++)
-            cbc_vector[i] ^= src[srcoff + i];
-
-        tc.transformBlock(cbc_vector, 0, dst, dstoff);
-        System.arraycopy(dst, dstoff, cbc_vector, 0, blockSize);
-    }
-
-    private void decryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
-        /* Assume the worst, src and dst are overlapping... */
-        System.arraycopy(src, srcoff, tmp_vector, 0, blockSize);
-        tc.transformBlock(src, srcoff, dst, dstoff);
-
-        for (int i = 0; i < blockSize; i++)
-            dst[dstoff + i] ^= cbc_vector[i];
-
-        /* ...that is why we need a tmp buffer. */
-        byte[] swap = cbc_vector;
-        cbc_vector = tmp_vector;
-        tmp_vector = swap;
-    }
-
-    public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
-        if (doEncrypt)
-            encryptBlock(src, srcoff, dst, dstoff);
-        else
-            decryptBlock(src, srcoff, dst, dstoff);
-    }
-}
--- a/src/com/trilead/ssh2/crypto/cipher/CTRMode.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * This is CTR mode as described in draft-ietf-secsh-newmodes-XY.txt
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: CTRMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class CTRMode implements BlockCipher {
-    byte[] X;
-    byte[] Xenc;
-
-    BlockCipher bc;
-    int blockSize;
-    boolean doEncrypt;
-
-    int count = 0;
-
-    public void init(boolean forEncryption, byte[] key) {
-    }
-
-    public CTRMode(BlockCipher tc, byte[] iv, boolean doEnc) throws IllegalArgumentException {
-        bc = tc;
-        blockSize = bc.getBlockSize();
-        doEncrypt = doEnc;
-
-        if (blockSize != iv.length)
-            throw new IllegalArgumentException("IV must be " + blockSize + " bytes long! (currently " + iv.length + ")");
-
-        X = new byte[blockSize];
-        Xenc = new byte[blockSize];
-        System.arraycopy(iv, 0, X, 0, blockSize);
-    }
-
-    public final int getBlockSize() {
-        return blockSize;
-    }
-
-    public final void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
-        bc.transformBlock(X, 0, Xenc, 0);
-
-        for (int i = 0; i < blockSize; i++) {
-            dst[dstoff + i] = (byte)(src[srcoff + i] ^ Xenc[i]);
-        }
-
-        for (int i = (blockSize - 1); i >= 0; i--) {
-            X[i]++;
-
-            if (X[i] != 0)
-                break;
-        }
-    }
-}
--- a/src/com/trilead/ssh2/crypto/cipher/CipherInputStream.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,136 +0,0 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * CipherInputStream.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: CipherInputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class CipherInputStream {
-    BlockCipher currentCipher;
-    InputStream bi;
-    byte[] buffer;
-    byte[] enc;
-    int blockSize;
-    int pos;
-
-    /*
-     * We cannot use java.io.BufferedInputStream, since that is not available in
-     * J2ME. Everything could be improved alot here.
-     */
-
-    final int BUFF_SIZE = 2048;
-    byte[] input_buffer = new byte[BUFF_SIZE];
-    int input_buffer_pos = 0;
-    int input_buffer_size = 0;
-
-    public CipherInputStream(BlockCipher tc, InputStream bi) {
-        this.bi = bi;
-        changeCipher(tc);
-    }
-
-    private int fill_buffer() throws IOException {
-        input_buffer_pos = 0;
-        input_buffer_size = bi.read(input_buffer, 0, BUFF_SIZE);
-        return input_buffer_size;
-    }
-
-    private int internal_read(byte[] b, int off, int len) throws IOException {
-        if (input_buffer_size < 0)
-            return -1;
-
-        if (input_buffer_pos >= input_buffer_size) {
-            if (fill_buffer() <= 0)
-                return -1;
-        }
-
-        int avail = input_buffer_size - input_buffer_pos;
-        int thiscopy = (len > avail) ? avail : len;
-        System.arraycopy(input_buffer, input_buffer_pos, b, off, thiscopy);
-        input_buffer_pos += thiscopy;
-        return thiscopy;
-    }
-
-    public void changeCipher(BlockCipher bc) {
-        this.currentCipher = bc;
-        blockSize = bc.getBlockSize();
-        buffer = new byte[blockSize];
-        enc = new byte[blockSize];
-        pos = blockSize;
-    }
-
-    private void getBlock() throws IOException {
-        int n = 0;
-
-        while (n < blockSize) {
-            int len = internal_read(enc, n, blockSize - n);
-
-            if (len < 0)
-                throw new IOException("Cannot read full block, EOF reached.");
-
-            n += len;
-        }
-
-        try {
-            currentCipher.transformBlock(enc, 0, buffer, 0);
-        }
-        catch (Exception e) {
-            throw new IOException("Error while decrypting block.");
-        }
-
-        pos = 0;
-    }
-
-    public int read(byte[] dst) throws IOException {
-        return read(dst, 0, dst.length);
-    }
-
-    public int read(byte[] dst, int off, int len) throws IOException {
-        int count = 0;
-
-        while (len > 0) {
-            if (pos >= blockSize)
-                getBlock();
-
-            int avail = blockSize - pos;
-            int copy = Math.min(avail, len);
-            System.arraycopy(buffer, pos, dst, off, copy);
-            pos += copy;
-            off += copy;
-            len -= copy;
-            count += copy;
-        }
-
-        return count;
-    }
-
-    public int read() throws IOException {
-        if (pos >= blockSize) {
-            getBlock();
-        }
-
-        return buffer[pos++] & 0xff;
-    }
-
-    public int readPlain(byte[] b, int off, int len) throws IOException {
-        if (pos != blockSize)
-            throw new IOException("Cannot read plain since crypto buffer is not aligned.");
-
-        int n = 0;
-
-        while (n < len) {
-            int cnt = internal_read(b, off + n, len - n);
-
-            if (cnt < 0)
-                throw new IOException("Cannot fill buffer, EOF reached.");
-
-            n += cnt;
-        }
-
-        return n;
-    }
-}
--- a/src/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * CipherOutputStream.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: CipherOutputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class CipherOutputStream {
-    BlockCipher currentCipher;
-    OutputStream bo;
-    byte[] buffer;
-    byte[] enc;
-    int blockSize;
-    int pos;
-
-    /*
-     * We cannot use java.io.BufferedOutputStream, since that is not available
-     * in J2ME. Everything could be improved here alot.
-     */
-
-    final int BUFF_SIZE = 2048;
-    byte[] out_buffer = new byte[BUFF_SIZE];
-    int out_buffer_pos = 0;
-
-    public CipherOutputStream(BlockCipher tc, OutputStream bo) {
-        this.bo = bo;
-        changeCipher(tc);
-    }
-
-    private void internal_write(byte[] src, int off, int len) throws IOException {
-        while (len > 0) {
-            int space = BUFF_SIZE - out_buffer_pos;
-            int copy = (len > space) ? space : len;
-            System.arraycopy(src, off, out_buffer, out_buffer_pos, copy);
-            off += copy;
-            out_buffer_pos += copy;
-            len -= copy;
-
-            if (out_buffer_pos >= BUFF_SIZE) {
-                bo.write(out_buffer, 0, BUFF_SIZE);
-                out_buffer_pos = 0;
-            }
-        }
-    }
-
-    private void internal_write(int b) throws IOException {
-        out_buffer[out_buffer_pos++] = (byte) b;
-
-        if (out_buffer_pos >= BUFF_SIZE) {
-            bo.write(out_buffer, 0, BUFF_SIZE);
-            out_buffer_pos = 0;
-        }
-    }
-
-    public void flush() throws IOException {
-        if (pos != 0)
-            throw new IOException("FATAL: cannot flush since crypto buffer is not aligned.");
-
-        if (out_buffer_pos > 0) {
-            bo.write(out_buffer, 0, out_buffer_pos);
-            out_buffer_pos = 0;
-        }
-
-        bo.flush();
-    }
-
-    public void changeCipher(BlockCipher bc) {
-        this.currentCipher = bc;
-        blockSize = bc.getBlockSize();
-        buffer = new byte[blockSize];
-        enc = new byte[blockSize];
-        pos = 0;
-    }
-
-    private void writeBlock() throws IOException {
-        try {
-            currentCipher.transformBlock(buffer, 0, enc, 0);
-        }
-        catch (Exception e) {
-            throw(IOException) new IOException("Error while decrypting block.").initCause(e);
-        }
-
-        internal_write(enc, 0, blockSize);
-        pos = 0;
-    }
-
-    public void write(byte[] src, int off, int len) throws IOException {
-        while (len > 0) {
-            int avail = blockSize - pos;
-            int copy = Math.min(avail, len);
-            System.arraycopy(src, off, buffer, pos, copy);
-            pos += copy;
-            off += copy;
-            len -= copy;
-
-            if (pos >= blockSize)
-                writeBlock();
-        }
-    }
-
-    public void write(int b) throws IOException {
-        buffer[pos++] = (byte) b;
-
-        if (pos >= blockSize)
-            writeBlock();
-    }
-
-    public void writePlain(int b) throws IOException {
-        if (pos != 0)
-            throw new IOException("Cannot write plain since crypto buffer is not aligned.");
-
-        internal_write(b);
-    }
-
-    public void writePlain(byte[] b, int off, int len) throws IOException {
-        if (pos != 0)
-            throw new IOException("Cannot write plain since crypto buffer is not aligned.");
-
-        internal_write(b, off, len);
-    }
-}
--- a/src/com/trilead/ssh2/crypto/cipher/DES.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,353 +0,0 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/*
- This file is based on the 3DES implementation from the Bouncy Castle Crypto package.
- Their licence file states the following:
-
- Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
- (http://www.bouncycastle.org)
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
-
-/**
- * DES.
- *
- * @author See comments in the source file
- * @version $Id: DES.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class DES implements BlockCipher {
-    private int[] workingKey = null;
-
-    /**
-     * standard constructor.
-     */
-    public DES() {
-    }
-
-    /**
-     * initialise a DES cipher.
-     *
-     * @param encrypting
-     *            whether or not we are for encryption.
-     * @param key
-     *            the parameters required to set up the cipher.
-     * @exception IllegalArgumentException
-     *                if the params argument is inappropriate.
-     */
-    public void init(boolean encrypting, byte[] key) {
-        this.workingKey = generateWorkingKey(encrypting, key, 0);
-    }
-
-    public String getAlgorithmName() {
-        return "DES";
-    }
-
-    public int getBlockSize() {
-        return 8;
-    }
-
-    public void transformBlock(byte[] in, int inOff, byte[] out, int outOff) {
-        if (workingKey == null) {
-            throw new IllegalStateException("DES engine not initialised!");
-        }
-
-        desFunc(workingKey, in, inOff, out, outOff);
-    }
-
-    public void reset() {
-    }
-
-    /**
-     * what follows is mainly taken from "Applied Cryptography", by Bruce
-     * Schneier, however it also bears great resemblance to Richard
-     * Outerbridge's D3DES...
-     */
-
-    static short[] Df_Key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
-                              0x10, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67
-                            };
-
-    static short[] bytebit = { 0200, 0100, 040, 020, 010, 04, 02, 01 };
-
-    static int[] bigbyte = { 0x800000, 0x400000, 0x200000, 0x100000, 0x80000, 0x40000, 0x20000, 0x10000, 0x8000,
-                             0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1
-                           };
-
-    /*
-     * Use the key schedule specified in the Standard (ANSI X3.92-1981).
-     */
-
-    static byte[] pc1 = { 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2,
-                          59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12,
-                          4, 27, 19, 11, 3
-                        };
-
-    static byte[] totrot = { 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 };
-
-    static byte[] pc2 = { 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40,
-                          51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
-                        };
-
-    static int[] SP1 = { 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004,
-                         0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004,
-                         0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004,
-                         0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404,
-                         0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400,
-                         0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404,
-                         0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004,
-                         0x00010400, 0x00000000, 0x01010004
-                       };
-
-    static int[] SP2 = { 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020,
-                         0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020,
-                         0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020,
-                         0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020,
-                         0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020,
-                         0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020,
-                         0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000,
-                         0x80100020, 0x80108020, 0x00108000
-                       };
-
-    static int[] SP3 = { 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208,
-                         0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208,
-                         0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208,
-                         0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000,
-                         0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208,
-                         0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208,
-                         0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208,
-                         0x00000008, 0x08020008, 0x00020200
-                       };
-
-    static int[] SP4 = { 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001,
-                         0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001,
-                         0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081,
-                         0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001,
-                         0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081,
-                         0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000,
-                         0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080,
-                         0x00800000, 0x00002000, 0x00802080
-                       };
-
-    static int[] SP5 = { 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000,
-                         0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000,
-                         0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000,
-                         0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100,
-                         0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000,
-                         0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100,
-                         0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000,
-                         0x40080000, 0x02080100, 0x40000100
-                       };
-
-    static int[] SP6 = { 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010,
-                         0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010,
-                         0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010,
-                         0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010,
-                         0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000,
-                         0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010,
-                         0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000,
-                         0x20000000, 0x00400010, 0x20004010
-                       };
-
-    static int[] SP7 = { 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802,
-                         0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802,
-                         0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000,
-                         0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800,
-                         0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800,
-                         0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000,
-                         0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002,
-                         0x04000800, 0x00000800, 0x00200002
-                       };
-
-    static int[] SP8 = { 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040,
-                         0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040,
-                         0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040,
-                         0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000,
-                         0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040,
-                         0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040,
-                         0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040,
-                         0x00040040, 0x10000000, 0x10041000
-                       };
-
-    /**
-     * generate an integer based working key based on our secret key and what we
-     * processing we are planning to do.
-     *
-     * Acknowledgements for this routine go to James Gillogly & Phil Karn.
-     * (whoever, and wherever they are!).
-     */
-    protected int[] generateWorkingKey(boolean encrypting, byte[] key, int off) {
-        int[] newKey = new int[32];
-        boolean[] pc1m = new boolean[56], pcr = new boolean[56];
-
-        for (int j = 0; j < 56; j++) {
-            int l = pc1[j];
-            pc1m[j] = ((key[off + (l >>> 3)] & bytebit[l & 07]) != 0);
-        }
-
-        for (int i = 0; i < 16; i++) {
-            int l, m, n;
-
-            if (encrypting) {
-                m = i << 1;
-            }
-            else {
-                m = (15 - i) << 1;
-            }
-
-            n = m + 1;
-            newKey[m] = newKey[n] = 0;
-
-            for (int j = 0; j < 28; j++) {
-                l = j + totrot[i];
-
-                if (l < 28) {
-                    pcr[j] = pc1m[l];
-                }
-                else {
-                    pcr[j] = pc1m[l - 28];
-                }
-            }
-
-            for (int j = 28; j < 56; j++) {
-                l = j + totrot[i];
-
-                if (l < 56) {
-                    pcr[j] = pc1m[l];
-                }
-                else {
-                    pcr[j] = pc1m[l - 28];
-                }
-            }
-
-            for (int j = 0; j < 24; j++) {
-                if (pcr[pc2[j]]) {
-                    newKey[m] |= bigbyte[j];
-                }
-
-                if (pcr[pc2[j + 24]]) {
-                    newKey[n] |= bigbyte[j];
-                }
-            }
-        }
-
-        //
-        // store the processed key
-        //
-        for (int i = 0; i != 32; i += 2) {
-            int i1, i2;
-            i1 = newKey[i];
-            i2 = newKey[i + 1];
-            newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10) | ((i2 & 0x00fc0000) >>> 10)
-                        | ((i2 & 0x00000fc0) >>> 6);
-            newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16) | ((i2 & 0x0003f000) >>> 4)
-                            | (i2 & 0x0000003f);
-        }
-
-        return newKey;
-    }
-
-    /**
-     * the DES engine.
-     */
-    protected void desFunc(int[] wKey, byte[] in, int inOff, byte[] out, int outOff) {
-        int work, right, left;
-        left = (in[inOff + 0] & 0xff) << 24;
-        left |= (in[inOff + 1] & 0xff) << 16;
-        left |= (in[inOff + 2] & 0xff) << 8;
-        left |= (in[inOff + 3] & 0xff);
-        right = (in[inOff + 4] & 0xff) << 24;
-        right |= (in[inOff + 5] & 0xff) << 16;
-        right |= (in[inOff + 6] & 0xff) << 8;
-        right |= (in[inOff + 7] & 0xff);
-        work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
-        right ^= work;
-        left ^= (work << 4);
-        work = ((left >>> 16) ^ right) & 0x0000ffff;
-        right ^= work;
-        left ^= (work << 16);
-        work = ((right >>> 2) ^ left) & 0x33333333;
-        left ^= work;
-        right ^= (work << 2);
-        work = ((right >>> 8) ^ left) & 0x00ff00ff;
-        left ^= work;
-        right ^= (work << 8);
-        right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff;
-        work = (left ^ right) & 0xaaaaaaaa;
-        left ^= work;
-        right ^= work;
-        left = ((left << 1) | ((left >>> 31) & 1)) & 0xffffffff;
-
-        for (int round = 0; round < 8; round++) {
-            int fval;
-            work = (right << 28) | (right >>> 4);
-            work ^= wKey[round * 4 + 0];
-            fval = SP7[work & 0x3f];
-            fval |= SP5[(work >>> 8) & 0x3f];
-            fval |= SP3[(work >>> 16) & 0x3f];
-            fval |= SP1[(work >>> 24) & 0x3f];
-            work = right ^ wKey[round * 4 + 1];
-            fval |= SP8[work & 0x3f];
-            fval |= SP6[(work >>> 8) & 0x3f];
-            fval |= SP4[(work >>> 16) & 0x3f];
-            fval |= SP2[(work >>> 24) & 0x3f];
-            left ^= fval;
-            work = (left << 28) | (left >>> 4);
-            work ^= wKey[round * 4 + 2];
-            fval = SP7[work & 0x3f];
-            fval |= SP5[(work >>> 8) & 0x3f];
-            fval |= SP3[(work >>> 16) & 0x3f];
-            fval |= SP1[(work >>> 24) & 0x3f];
-            work = left ^ wKey[round * 4 + 3];
-            fval |= SP8[work & 0x3f];
-            fval |= SP6[(work >>> 8) & 0x3f];
-            fval |= SP4[(work >>> 16) & 0x3f];
-            fval |= SP2[(work >>> 24) & 0x3f];
-            right ^= fval;
-        }
-
-        right = (right << 31) | (right >>> 1);
-        work = (left ^ right) & 0xaaaaaaaa;
-        left ^= work;
-        right ^= work;
-        left = (left << 31) | (left >>> 1);
-        work = ((left >>> 8) ^ right) & 0x00ff00ff;
-        right ^= work;
-        left ^= (work << 8);
-        work = ((left >>> 2) ^ right) & 0x33333333;
-        right ^= work;
-        left ^= (work << 2);
-        work = ((right >>> 16) ^ left) & 0x0000ffff;
-        left ^= work;
-        right ^= (work << 16);
-        work = ((right >>> 4) ^ left) & 0x0f0f0f0f;
-        left ^= work;
-        right ^= (work << 4);
-        out[outOff + 0] = (byte)((right >>> 24) & 0xff);
-        out[outOff + 1] = (byte)((right >>> 16) & 0xff);
-        out[outOff + 2] = (byte)((right >>> 8) & 0xff);
-        out[outOff + 3] = (byte)(right & 0xff);
-        out[outOff + 4] = (byte)((left >>> 24) & 0xff);
-        out[outOff + 5] = (byte)((left >>> 16) & 0xff);
-        out[outOff + 6] = (byte)((left >>> 8) & 0xff);
-        out[outOff + 7] = (byte)(left & 0xff);
-    }
-}
--- a/src/com/trilead/ssh2/crypto/cipher/DESede.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/*
- This file was shamelessly taken (and modified) from the Bouncy Castle Crypto package.
- Their licence file states the following:
-
- Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
- (http://www.bouncycastle.org)
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
-
-/**
- * DESede.
- *
- * @author See comments in the source file
- * @version $Id: DESede.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class DESede extends DES {
-    private int[] key1 = null;
-    private int[] key2 = null;
-    private int[] key3 = null;
-
-    private boolean encrypt;
-
-    /**
-     * standard constructor.
-     */
-    public DESede() {
-    }
-
-    /**
-     * initialise a DES cipher.
-     *
-     * @param encrypting
-     *            whether or not we are for encryption.
-     * @param key
-     *            the parameters required to set up the cipher.
-     * @exception IllegalArgumentException
-     *                if the params argument is inappropriate.
-     */
-    public void init(boolean encrypting, byte[] key) {
-        key1 = generateWorkingKey(encrypting, key, 0);
-        key2 = generateWorkingKey(!encrypting, key, 8);
-        key3 = generateWorkingKey(encrypting, key, 16);
-        encrypt = encrypting;
-    }
-
-    public String getAlgorithmName() {
-        return "DESede";
-    }
-
-    public int getBlockSize() {
-        return 8;
-    }
-
-    public void transformBlock(byte[] in, int inOff, byte[] out, int outOff) {
-        if (key1 == null) {
-            throw new IllegalStateException("DESede engine not initialised!");
-        }
-
-        if (encrypt) {
-            desFunc(key1, in, inOff, out, outOff);
-            desFunc(key2, out, outOff, out, outOff);
-            desFunc(key3, out, outOff, out, outOff);
-        }
-        else {
-            desFunc(key3, in, inOff, out, outOff);
-            desFunc(key2, out, outOff, out, outOff);
-            desFunc(key1, out, outOff, out, outOff);
-        }
-    }
-
-    public void reset() {
-    }
-}
--- a/src/com/trilead/ssh2/crypto/cipher/NullCipher.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * NullCipher.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: NullCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class NullCipher implements BlockCipher {
-    private int blockSize = 8;
-
-    public NullCipher() {
-    }
-
-    public NullCipher(int blockSize) {
-        this.blockSize = blockSize;
-    }
-
-    public void init(boolean forEncryption, byte[] key) {
-    }
-
-    public int getBlockSize() {
-        return blockSize;
-    }
-
-    public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff) {
-        System.arraycopy(src, srcoff, dst, dstoff, blockSize);
-    }
-}
--- a/src/com/trilead/ssh2/crypto/dh/DhExchange.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/**
- *
- */
-package com.trilead.ssh2.crypto.dh;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.spec.InvalidKeySpecException;
-
-import javax.crypto.KeyAgreement;
-import javax.crypto.interfaces.DHPrivateKey;
-import javax.crypto.interfaces.DHPublicKey;
-import javax.crypto.spec.DHParameterSpec;
-import javax.crypto.spec.DHPublicKeySpec;
-
-/**
- * @author kenny
- *
- */
-public class DhExchange extends GenericDhExchange {
-
-    /* Given by the standard */
-
-    private static final BigInteger P1 = new BigInteger(
-        "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
-        + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
-        + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
-        + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
-        + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
-        + "FFFFFFFFFFFFFFFF", 16);
-
-    private static final BigInteger P14 = new BigInteger(
-        "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
-        + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
-        + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
-        + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
-        + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
-        + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
-        + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
-        + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
-        + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
-        + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
-        + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16);
-
-    private static final BigInteger G = BigInteger.valueOf(2);
-
-    /* Client public and private */
-
-    private DHPrivateKey clientPrivate;
-    private DHPublicKey clientPublic;
-
-    /* Server public */
-
-    private DHPublicKey serverPublic;
-
-    @Override
-    public void init(String name) throws IOException {
-        final DHParameterSpec spec;
-
-        if ("diffie-hellman-group1-sha1".equals(name)) {
-            spec = new DHParameterSpec(P1, G);
-        }
-        else if ("diffie-hellman-group14-sha1".equals(name)) {
-            spec = new DHParameterSpec(P14, G);
-        }
-        else {
-            throw new IllegalArgumentException("Unknown DH group " + name);
-        }
-
-        try {
-            KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
-            kpg.initialize(spec);
-            KeyPair pair = kpg.generateKeyPair();
-            clientPrivate = (DHPrivateKey) pair.getPrivate();
-            clientPublic = (DHPublicKey) pair.getPublic();
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw(IOException) new IOException("No DH keypair generator").initCause(e);
-        }
-        catch (InvalidAlgorithmParameterException e) {
-            throw(IOException) new IOException("Invalid DH parameters").initCause(e);
-        }
-    }
-
-    @Override
-    public byte[] getE() {
-        if (clientPublic == null)
-            throw new IllegalStateException("DhExchange not initialized!");
-
-        return clientPublic.getY().toByteArray();
-    }
-
-    @Override
-    protected byte[] getServerE() {
-        if (serverPublic == null)
-            throw new IllegalStateException("DhExchange not initialized!");
-
-        return serverPublic.getY().toByteArray();
-    }
-
-    @Override
-    public void setF(byte[] f) throws IOException {
-        if (clientPublic == null)
-            throw new IllegalStateException("DhExchange not initialized!");
-
-        final KeyAgreement ka;
-
-        try {
-            KeyFactory kf = KeyFactory.getInstance("DH");
-            DHParameterSpec params = clientPublic.getParams();
-            this.serverPublic = (DHPublicKey) kf.generatePublic(new DHPublicKeySpec(
-                                    new BigInteger(f), params.getP(), params.getG()));
-            ka = KeyAgreement.getInstance("DH");
-            ka.init(clientPrivate);
-            ka.doPhase(serverPublic, true);
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw(IOException) new IOException("No DH key agreement method").initCause(e);
-        }
-        catch (InvalidKeyException e) {
-            throw(IOException) new IOException("Invalid DH key").initCause(e);
-        }
-        catch (InvalidKeySpecException e) {
-            throw(IOException) new IOException("Invalid DH key").initCause(e);
-        }
-
-        sharedSecret = new BigInteger(ka.generateSecret());
-    }
-
-    @Override
-    public String getHashAlgo() {
-        return "SHA1";
-    }
-}
--- a/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-
-package com.trilead.ssh2.crypto.dh;
-
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
-
-
-/**
- * DhGroupExchange.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DhGroupExchange.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DhGroupExchange {
-    /* Given by the standard */
-
-    private BigInteger p;
-    private BigInteger g;
-
-    /* Client public and private */
-
-    private BigInteger e;
-    private BigInteger x;
-
-    /* Server public */
-
-    private BigInteger f;
-
-    /* Shared secret */
-
-    private BigInteger k;
-
-    public DhGroupExchange(BigInteger p, BigInteger g) {
-        this.p = p;
-        this.g = g;
-    }
-
-    public void init(SecureRandom rnd) {
-        k = null;
-        x = new BigInteger(p.bitLength() - 1, rnd);
-        e = g.modPow(x, p);
-    }
-
-    /**
-     * @return Returns the e.
-     */
-    public BigInteger getE() {
-        if (e == null)
-            throw new IllegalStateException("Not initialized!");
-
-        return e;
-    }
-
-    /**
-     * @return Returns the shared secret k.
-     */
-    public BigInteger getK() {
-        if (k == null)
-            throw new IllegalStateException("Shared secret not yet known, need f first!");
-
-        return k;
-    }
-
-    /**
-     * Sets f and calculates the shared secret.
-     */
-    public void setF(BigInteger f) {
-        if (e == null)
-            throw new IllegalStateException("Not initialized!");
-
-        BigInteger zero = BigInteger.valueOf(0);
-
-        if (zero.compareTo(f) >= 0 || p.compareTo(f) <= 0)
-            throw new IllegalArgumentException("Invalid f specified!");
-
-        this.f = f;
-        this.k = f.modPow(x, p);
-    }
-
-    public byte[] calculateH(String hashAlgo, byte[] clientversion, byte[] serverversion,
-                             byte[] clientKexPayload, byte[] serverKexPayload, byte[] hostKey, DHGexParameters para) {
-        HashForSSH2Types hash = new HashForSSH2Types(hashAlgo);
-        hash.updateByteString(clientversion);
-        hash.updateByteString(serverversion);
-        hash.updateByteString(clientKexPayload);
-        hash.updateByteString(serverKexPayload);
-        hash.updateByteString(hostKey);
-
-        if (para.getMin_group_len() > 0)
-            hash.updateUINT32(para.getMin_group_len());
-
-        hash.updateUINT32(para.getPref_group_len());
-
-        if (para.getMax_group_len() > 0)
-            hash.updateUINT32(para.getMax_group_len());
-
-        hash.updateBigInt(p);
-        hash.updateBigInt(g);
-        hash.updateBigInt(e);
-        hash.updateBigInt(f);
-        hash.updateBigInt(k);
-        return hash.getDigest();
-    }
-}
--- a/src/com/trilead/ssh2/crypto/dh/EcDhExchange.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-/**
- *
- */
-package com.trilead.ssh2.crypto.dh;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-
-import javax.crypto.KeyAgreement;
-
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
-
-/**
- * @author kenny
- *
- */
-public class EcDhExchange extends GenericDhExchange {
-    private ECPrivateKey clientPrivate;
-    private ECPublicKey clientPublic;
-    private ECPublicKey serverPublic;
-
-    @Override
-    public void init(String name) throws IOException {
-        final ECParameterSpec spec;
-
-        if ("ecdh-sha2-nistp256".equals(name)) {
-            spec = ECDSASHA2Verify.EllipticCurves.nistp256;
-        }
-        else if ("ecdh-sha2-nistp384".equals(name)) {
-            spec = ECDSASHA2Verify.EllipticCurves.nistp384;
-        }
-        else if ("ecdh-sha2-nistp521".equals(name)) {
-            spec = ECDSASHA2Verify.EllipticCurves.nistp521;
-        }
-        else {
-            throw new IllegalArgumentException("Unknown EC curve " + name);
-        }
-
-        KeyPairGenerator kpg;
-
-        try {
-            kpg = KeyPairGenerator.getInstance("EC");
-            kpg.initialize(spec);
-            KeyPair pair = kpg.generateKeyPair();
-            clientPrivate = (ECPrivateKey) pair.getPrivate();
-            clientPublic = (ECPublicKey) pair.getPublic();
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw(IOException) new IOException("No DH keypair generator").initCause(e);
-        }
-        catch (InvalidAlgorithmParameterException e) {
-            throw(IOException) new IOException("Invalid DH parameters").initCause(e);
-        }
-    }
-
-    @Override
-    public byte[] getE() {
-        return ECDSASHA2Verify.encodeECPoint(clientPublic.getW(), clientPublic.getParams()
-                                             .getCurve());
-    }
-
-    @Override
-    protected byte[] getServerE() {
-        return ECDSASHA2Verify.encodeECPoint(serverPublic.getW(), serverPublic.getParams()
-                                             .getCurve());
-    }
-
-    @Override
-    public void setF(byte[] f) throws IOException {
-        if (clientPublic == null)
-            throw new IllegalStateException("DhDsaExchange not initialized!");
-
-        final KeyAgreement ka;
-
-        try {
-            KeyFactory kf = KeyFactory.getInstance("EC");
-            ECParameterSpec params = clientPublic.getParams();
-            ECPoint serverPoint = ECDSASHA2Verify.decodeECPoint(f, params.getCurve());
-            this.serverPublic = (ECPublicKey) kf.generatePublic(new ECPublicKeySpec(serverPoint,
-                                params));
-            ka = KeyAgreement.getInstance("ECDH");
-            ka.init(clientPrivate);
-            ka.doPhase(serverPublic, true);
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw(IOException) new IOException("No ECDH key agreement method").initCause(e);
-        }
-        catch (InvalidKeyException e) {
-            throw(IOException) new IOException("Invalid ECDH key").initCause(e);
-        }
-        catch (InvalidKeySpecException e) {
-            throw(IOException) new IOException("Invalid ECDH key").initCause(e);
-        }
-
-        sharedSecret = new BigInteger(ka.generateSecret());
-    }
-
-    @Override
-    public String getHashAlgo() {
-        return ECDSASHA2Verify.getDigestAlgorithmForParams(clientPublic.getParams());
-    }
-}
--- a/src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-
-package com.trilead.ssh2.crypto.dh;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-
-import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
-import com.trilead.ssh2.log.Logger;
-
-
-/**
- * DhExchange.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public abstract class GenericDhExchange {
-    private static final Logger log = Logger.getLogger(GenericDhExchange.class);
-
-    /* Shared secret */
-
-    BigInteger sharedSecret;
-
-    protected GenericDhExchange() {
-    }
-
-    public static GenericDhExchange getInstance(String algo) {
-        if (algo.startsWith("ecdh-sha2-")) {
-            return new EcDhExchange();
-        }
-        else {
-            return new DhExchange();
-        }
-    }
-
-    public abstract void init(String name) throws IOException;
-
-    /**
-     * @return Returns the e (public value)
-     * @throws IllegalStateException
-     */
-    public abstract byte[] getE();
-
-    /**
-     * @return Returns the server's e (public value)
-     * @throws IllegalStateException
-     */
-    protected abstract byte[] getServerE();
-
-    /**
-     * @return Returns the shared secret k.
-     * @throws IllegalStateException
-     */
-    public BigInteger getK() {
-        if (sharedSecret == null)
-            throw new IllegalStateException("Shared secret not yet known, need f first!");
-
-        return sharedSecret;
-    }
-
-    /**
-     * @param f
-     */
-    public abstract void setF(byte[] f) throws IOException;
-
-    public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
-                             byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException {
-        HashForSSH2Types hash = new HashForSSH2Types(getHashAlgo());
-
-        if (log.isEnabled()) {
-            log.log(90, "Client: '" + new String(clientversion, "ISO-8859-1") + "'");
-            log.log(90, "Server: '" + new String(serverversion, "ISO-8859-1") + "'");
-        }
-
-        hash.updateByteString(clientversion);
-        hash.updateByteString(serverversion);
-        hash.updateByteString(clientKexPayload);
-        hash.updateByteString(serverKexPayload);
-        hash.updateByteString(hostKey);
-        hash.updateByteString(getE());
-        hash.updateByteString(getServerE());
-        hash.updateBigInt(sharedSecret);
-        return hash.getDigest();
-    }
-
-    public abstract String getHashAlgo();
-}
--- a/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-import java.math.BigInteger;
-import java.security.DigestException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * HashForSSH2Types.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: HashForSSH2Types.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class HashForSSH2Types {
-    MessageDigest md;
-
-    public HashForSSH2Types(String type) {
-        try {
-            md = MessageDigest.getInstance(type);
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("Unsupported algorithm " + type);
-        }
-    }
-
-    public void updateByte(byte b) {
-        /* HACK - to test it with J2ME */
-        byte[] tmp = new byte[1];
-        tmp[0] = b;
-        md.update(tmp);
-    }
-
-    public void updateBytes(byte[] b) {
-        md.update(b);
-    }
-
-    public void updateUINT32(int v) {
-        md.update((byte)(v >> 24));
-        md.update((byte)(v >> 16));
-        md.update((byte)(v >> 8));
-        md.update((byte)(v));
-    }
-
-    public void updateByteString(byte[] b) {
-        updateUINT32(b.length);
-        updateBytes(b);
-    }
-
-    public void updateBigInt(BigInteger b) {
-        updateByteString(b.toByteArray());
-    }
-
-    public void reset() {
-        md.reset();
-    }
-
-    public int getDigestLength() {
-        return md.getDigestLength();
-    }
-
-    public byte[] getDigest() {
-        byte[] tmp = new byte[md.getDigestLength()];
-        getDigest(tmp);
-        return tmp;
-    }
-
-    public void getDigest(byte[] out) {
-        getDigest(out, 0);
-    }
-
-    public void getDigest(byte[] out, int off) {
-        try {
-            md.digest(out, off, out.length - off);
-        }
-        catch (DigestException e) {
-            // TODO is this right?!
-            throw new RuntimeException("Unable to digest", e);
-        }
-    }
-}
--- a/src/com/trilead/ssh2/crypto/digest/MAC.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-
-import javax.crypto.Mac;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.SecretKeySpec;
-
-/**
- * MAC.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: MAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public final class MAC {
-    /**
-     * From http://tools.ietf.org/html/rfc4253
-     */
-    private static final String HMAC_MD5 = "hmac-md5";
-
-    /**
-     * From http://tools.ietf.org/html/rfc4253
-     */
-    private static final String HMAC_MD5_96 = "hmac-md5-96";
-
-    /**
-     * From http://tools.ietf.org/html/rfc4253
-     */
-    private static final String HMAC_SHA1 = "hmac-sha1";
-
-    /**
-     * From http://tools.ietf.org/html/rfc4253
-     */
-    private static final String HMAC_SHA1_96 = "hmac-sha1-96";
-
-    /**
-     * From http://tools.ietf.org/html/rfc6668
-     */
-    private static final String HMAC_SHA2_256 = "hmac-sha2-256";
-
-    /**
-     * From http://tools.ietf.org/html/rfc6668
-     */
-    private static final String HMAC_SHA2_512 = "hmac-sha2-512";
-
-    Mac mac;
-    int outSize;
-    int macSize;
-    byte[] buffer;
-
-    /* Higher Priority First */
-    private static final String[] MAC_LIST = {
-        HMAC_SHA2_256, HMAC_SHA2_512,
-        HMAC_SHA1_96, HMAC_SHA1, HMAC_MD5_96, HMAC_MD5
-    };
-
-    public final static String[] getMacList() {
-        return MAC_LIST;
-    }
-
-    public final static void checkMacList(String[] macs) {
-        for (int i = 0; i < macs.length; i++)
-            getKeyLen(macs[i]);
-    }
-
-    public final static int getKeyLen(String type) {
-        if (HMAC_SHA1.equals(type) || HMAC_SHA1_96.equals(type))
-            return 20;
-
-        if (HMAC_MD5.equals(type) || HMAC_MD5_96.equals(type))
-            return 16;
-
-        if (HMAC_SHA2_256.equals(type))
-            return 32;
-
-        if (HMAC_SHA2_512.equals(type))
-            return 64;
-
-        throw new IllegalArgumentException("Unkown algorithm " + type);
-    }
-
-    public MAC(String type, byte[] key) {
-        try {
-            if (HMAC_SHA1.equals(type) || HMAC_SHA1_96.equals(type)) {
-                mac = Mac.getInstance("HmacSHA1");
-            }
-            else if (HMAC_MD5.equals(type) || HMAC_MD5_96.equals(type)) {
-                mac = Mac.getInstance("HmacMD5");
-            }
-            else if (HMAC_SHA2_256.equals(type)) {
-                mac = Mac.getInstance("HmacSHA256");
-            }
-            else if (HMAC_SHA2_512.equals(type)) {
-                mac = Mac.getInstance("HmacSHA512");
-            }
-            else
-                throw new IllegalArgumentException("Unkown algorithm " + type);
-        }
-        catch (NoSuchAlgorithmException e) {
-            throw new IllegalArgumentException("Unknown algorithm " + type, e);
-        }
-
-        macSize = mac.getMacLength();
-
-        if (type.endsWith("-96")) {
-            outSize = 12;
-            buffer = new byte[macSize];
-        }
-        else {
-            outSize = macSize;
-            buffer = null;
-        }
-
-        try {
-            mac.init(new SecretKeySpec(key, type));
-        }
-        catch (InvalidKeyException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    public final void initMac(int seq) {
-        mac.reset();
-        mac.update((byte)(seq >> 24));
-        mac.update((byte)(seq >> 16));
-        mac.update((byte)(seq >> 8));
-        mac.update((byte)(seq));
-    }
-
-    public final void update(byte[] packetdata, int off, int len) {
-        mac.update(packetdata, off, len);
-    }
-
-    public final void getMac(byte[] out, int off) {
-        try {
-            if (buffer != null) {
-                mac.doFinal(buffer, 0);
-                System.arraycopy(buffer, 0, out, off, out.length - off);
-            }
-            else {
-                mac.doFinal(out, off);
-            }
-        }
-        catch (ShortBufferException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    public final int size() {
-        return outSize;
-    }
-}
--- a/src/com/trilead/ssh2/log/Logger.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-
-package com.trilead.ssh2.log;
-
-import com.trilead.ssh2.DebugLogger;
-
-/**
- * Logger - a very simple logger, mainly used during development.
- * Is not based on log4j (to reduce external dependencies).
- * However, if needed, something like log4j could easily be
- * hooked in.
- * <p>
- * For speed reasons, the static variables are not protected
- * with semaphores. In other words, if you dynamicaly change the
- * logging settings, then some threads may still use the old setting.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Logger.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
- */
-
-public class Logger {
-    public static boolean enabled = false;
-    public static DebugLogger logger = null;
-
-    private String className;
-
-    public final static Logger getLogger(Class x) {
-        return new Logger(x);
-    }
-
-    public Logger(Class x) {
-        this.className = x.getName();
-    }
-
-    public final boolean isEnabled() {
-        return enabled;
-    }
-
-    public final void log(int level, String message) {
-        if (!enabled)
-            return;
-
-        DebugLogger target = logger;
-
-        if (target == null)
-            return;
-
-        target.log(level, className, message);
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketChannelAuthAgentReq.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-package com.trilead.ssh2.packets;
-
-/**
- * PacketGlobalAuthAgent.
- *
- * @author Kenny Root, kenny@the-b.org
- * @version $Id$
- */
-public class PacketChannelAuthAgentReq {
-    byte[] payload;
-
-    public int recipientChannelID;
-
-    public PacketChannelAuthAgentReq(int recipientChannelID) {
-        this.recipientChannelID = recipientChannelID;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeString("auth-agent-req@openssh.com");
-            tw.writeBoolean(true); // want reply
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketChannelOpenConfirmation.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketChannelOpenConfirmation.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketChannelOpenConfirmation {
-    byte[] payload;
-
-    public int recipientChannelID;
-    public int senderChannelID;
-    public int initialWindowSize;
-    public int maxPacketSize;
-
-    public PacketChannelOpenConfirmation(int recipientChannelID, int senderChannelID, int initialWindowSize,
-                                         int maxPacketSize) {
-        this.recipientChannelID = recipientChannelID;
-        this.senderChannelID = senderChannelID;
-        this.initialWindowSize = initialWindowSize;
-        this.maxPacketSize = maxPacketSize;
-    }
-
-    public PacketChannelOpenConfirmation(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION)
-            throw new IOException(
-                "This is not a SSH_MSG_CHANNEL_OPEN_CONFIRMATION! ("
-                + packet_type + ")");
-
-        recipientChannelID = tr.readUINT32();
-        senderChannelID = tr.readUINT32();
-        initialWindowSize = tr.readUINT32();
-        maxPacketSize = tr.readUINT32();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeUINT32(senderChannelID);
-            tw.writeUINT32(initialWindowSize);
-            tw.writeUINT32(maxPacketSize);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketChannelOpenFailure.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketChannelOpenFailure.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketChannelOpenFailure.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketChannelOpenFailure {
-    byte[] payload;
-
-    public int recipientChannelID;
-    public int reasonCode;
-    public String description;
-    public String languageTag;
-
-    public PacketChannelOpenFailure(int recipientChannelID, int reasonCode, String description,
-                                    String languageTag) {
-        this.recipientChannelID = recipientChannelID;
-        this.reasonCode = reasonCode;
-        this.description = description;
-        this.languageTag = languageTag;
-    }
-
-    public PacketChannelOpenFailure(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_FAILURE)
-            throw new IOException(
-                "This is not a SSH_MSG_CHANNEL_OPEN_FAILURE! ("
-                + packet_type + ")");
-
-        recipientChannelID = tr.readUINT32();
-        reasonCode = tr.readUINT32();
-        description = tr.readString();
-        languageTag = tr.readString();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN_FAILURE packet!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_FAILURE);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeUINT32(reasonCode);
-            tw.writeString(description);
-            tw.writeString(languageTag);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketChannelTrileadPing.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketChannelTrileadPing.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketChannelTrileadPing.java,v 1.1 2008/03/03 07:01:36
- *          cplattne Exp $
- */
-public class PacketChannelTrileadPing {
-    byte[] payload;
-
-    public int recipientChannelID;
-
-    public PacketChannelTrileadPing(int recipientChannelID) {
-        this.recipientChannelID = recipientChannelID;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeString("trilead-ping");
-            tw.writeBoolean(true);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketChannelWindowAdjust.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketChannelWindowAdjust.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketChannelWindowAdjust {
-    byte[] payload;
-
-    public int recipientChannelID;
-    public int windowChange;
-
-    public PacketChannelWindowAdjust(int recipientChannelID, int windowChange) {
-        this.recipientChannelID = recipientChannelID;
-        this.windowChange = windowChange;
-    }
-
-    public PacketChannelWindowAdjust(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST)
-            throw new IOException(
-                "This is not a SSH_MSG_CHANNEL_WINDOW_ADJUST! ("
-                + packet_type + ")");
-
-        recipientChannelID = tr.readUINT32();
-        windowChange = tr.readUINT32();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_CHANNEL_WINDOW_ADJUST packet!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeUINT32(windowChange);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketDisconnect.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketDisconnect.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketDisconnect.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class PacketDisconnect {
-    byte[] payload;
-
-    int reason;
-    String desc;
-    String lang;
-
-    public PacketDisconnect(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_DISCONNECT)
-            throw new IOException("This is not a Disconnect Packet! (" + packet_type + ")");
-
-        reason = tr.readUINT32();
-        desc = tr.readString();
-        lang = tr.readString();
-    }
-
-    public PacketDisconnect(int reason, String desc, String lang) {
-        this.reason = reason;
-        this.desc = desc;
-        this.lang = lang;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_DISCONNECT);
-            tw.writeUINT32(reason);
-            tw.writeString(desc);
-            tw.writeString(lang);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketGlobalCancelForwardRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketGlobalCancelForwardRequest.java,v 1.1 2007/10/15 12:49:55
- *          cplattne Exp $
- */
-public class PacketGlobalCancelForwardRequest {
-    byte[] payload;
-
-    public boolean wantReply;
-    public String bindAddress;
-    public int bindPort;
-
-    public PacketGlobalCancelForwardRequest(boolean wantReply, String bindAddress, int bindPort) {
-        this.wantReply = wantReply;
-        this.bindAddress = bindAddress;
-        this.bindPort = bindPort;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
-            tw.writeString("cancel-tcpip-forward");
-            tw.writeBoolean(wantReply);
-            tw.writeString(bindAddress);
-            tw.writeUINT32(bindPort);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketGlobalForwardRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketGlobalForwardRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketGlobalForwardRequest {
-    byte[] payload;
-
-    public boolean wantReply;
-    public String bindAddress;
-    public int bindPort;
-
-    public PacketGlobalForwardRequest(boolean wantReply, String bindAddress, int bindPort) {
-        this.wantReply = wantReply;
-        this.bindAddress = bindAddress;
-        this.bindPort = bindPort;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
-            tw.writeString("tcpip-forward");
-            tw.writeBoolean(wantReply);
-            tw.writeString(bindAddress);
-            tw.writeUINT32(bindPort);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketGlobalTrileadPing.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketGlobalTrileadPing.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $
- */
-public class PacketGlobalTrileadPing {
-    byte[] payload;
-
-    public PacketGlobalTrileadPing() {
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
-            tw.writeString("trilead-ping");
-            tw.writeBoolean(true);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketIgnore.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketIgnore.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketIgnore.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketIgnore {
-    byte[] payload;
-
-    byte[] data;
-
-    public void setData(byte[] data) {
-        this.data = data;
-        payload = null;
-    }
-
-    public PacketIgnore() {
-    }
-
-    public PacketIgnore(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_IGNORE)
-            throw new IOException("This is not a SSH_MSG_IGNORE packet! (" + packet_type + ")");
-
-        /* Could parse String body */
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_IGNORE);
-
-            if (data != null)
-                tw.writeString(data, 0, data.length);
-            else
-                tw.writeString("");
-
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketKexDHInit.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-package com.trilead.ssh2.packets;
-
-/**
- * PacketKexDHInit.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDHInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDHInit {
-    byte[] payload;
-
-    byte[] publicKey;
-
-    public PacketKexDHInit(byte[] publicKey) {
-        this.publicKey = publicKey;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_KEXDH_INIT);
-            tw.writeString(publicKey, 0, publicKey.length);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketKexDHReply.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketKexDHReply.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDHReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDHReply {
-    byte[] payload;
-
-    byte[] hostKey;
-    byte[] publicKey;
-    byte[] signature;
-
-    public PacketKexDHReply(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_KEXDH_REPLY)
-            throw new IOException("This is not a SSH_MSG_KEXDH_REPLY! ("
-                                  + packet_type + ")");
-
-        hostKey = tr.readByteString();
-        publicKey = tr.readByteString();
-        signature = tr.readByteString();
-
-        if (tr.remain() != 0) throw new IOException("PADDING IN SSH_MSG_KEXDH_REPLY!");
-    }
-
-    public byte[] getF() {
-        return publicKey;
-    }
-
-    public byte[] getHostKey() {
-        return hostKey;
-    }
-
-    public byte[] getSignature() {
-        return signature;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexGroup.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDhGexGroup.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDhGexGroup.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDhGexGroup {
-    byte[] payload;
-
-    BigInteger p;
-    BigInteger g;
-
-    public PacketKexDhGexGroup(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_GROUP)
-            throw new IllegalArgumentException(
-                "This is not a SSH_MSG_KEX_DH_GEX_GROUP! (" + packet_type
-                + ")");
-
-        p = tr.readMPINT();
-        g = tr.readMPINT();
-
-        if (tr.remain() != 0)
-            throw new IOException("PADDING IN SSH_MSG_KEX_DH_GEX_GROUP!");
-    }
-
-    public BigInteger getG() {
-        return g;
-    }
-
-    public BigInteger getP() {
-        return p;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexInit.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDhGexInit.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDhGexInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDhGexInit {
-    byte[] payload;
-
-    BigInteger e;
-
-    public PacketKexDhGexInit(BigInteger e) {
-        this.e = e;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_INIT);
-            tw.writeMPInt(e);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexReply.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDhGexReply.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDhGexReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDhGexReply {
-    byte[] payload;
-
-    byte[] hostKey;
-    BigInteger f;
-    byte[] signature;
-
-    public PacketKexDhGexReply(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_REPLY)
-            throw new IOException("This is not a SSH_MSG_KEX_DH_GEX_REPLY! (" + packet_type + ")");
-
-        hostKey = tr.readByteString();
-        f = tr.readMPINT();
-        signature = tr.readByteString();
-
-        if (tr.remain() != 0)
-            throw new IOException("PADDING IN SSH_MSG_KEX_DH_GEX_REPLY!");
-    }
-
-    public BigInteger getF() {
-        return f;
-    }
-
-    public byte[] getHostKey() {
-        return hostKey;
-    }
-
-    public byte[] getSignature() {
-        return signature;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexRequest.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import com.trilead.ssh2.DHGexParameters;
-
-/**
- * PacketKexDhGexRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDhGexRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDhGexRequest {
-    byte[] payload;
-
-    int min;
-    int n;
-    int max;
-
-    public PacketKexDhGexRequest(DHGexParameters para) {
-        this.min = para.getMin_group_len();
-        this.n = para.getPref_group_len();
-        this.max = para.getMax_group_len();
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST);
-            tw.writeUINT32(min);
-            tw.writeUINT32(n);
-            tw.writeUINT32(max);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-import com.trilead.ssh2.DHGexParameters;
-
-/**
- * PacketKexDhGexRequestOld.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDhGexRequestOld.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDhGexRequestOld {
-    byte[] payload;
-
-    int n;
-
-    public PacketKexDhGexRequestOld(DHGexParameters para) {
-        this.n = para.getPref_group_len();
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST_OLD);
-            tw.writeUINT32(n);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketKexInit.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,144 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.crypto.CryptoWishList;
-import com.trilead.ssh2.transport.KexParameters;
-
-
-/**
- * PacketKexInit.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexInit {
-    byte[] payload;
-
-    KexParameters kp = new KexParameters();
-
-    public PacketKexInit(CryptoWishList cwl) {
-        kp.cookie = new byte[16];
-        new SecureRandom().nextBytes(kp.cookie);
-        kp.kex_algorithms = cwl.kexAlgorithms;
-        kp.server_host_key_algorithms = cwl.serverHostKeyAlgorithms;
-        kp.encryption_algorithms_client_to_server = cwl.c2s_enc_algos;
-        kp.encryption_algorithms_server_to_client = cwl.s2c_enc_algos;
-        kp.mac_algorithms_client_to_server = cwl.c2s_mac_algos;
-        kp.mac_algorithms_server_to_client = cwl.s2c_mac_algos;
-        kp.compression_algorithms_client_to_server = cwl.c2s_comp_algos;
-        kp.compression_algorithms_server_to_client = cwl.s2c_comp_algos;
-        kp.languages_client_to_server = new String[] {};
-        kp.languages_server_to_client = new String[] {};
-        kp.first_kex_packet_follows = false;
-        kp.reserved_field1 = 0;
-    }
-
-    public PacketKexInit(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_KEXINIT)
-            throw new IOException("This is not a KexInitPacket! (" + packet_type + ")");
-
-        kp.cookie = tr.readBytes(16);
-        kp.kex_algorithms = tr.readNameList();
-        kp.server_host_key_algorithms = tr.readNameList();
-        kp.encryption_algorithms_client_to_server = tr.readNameList();
-        kp.encryption_algorithms_server_to_client = tr.readNameList();
-        kp.mac_algorithms_client_to_server = tr.readNameList();
-        kp.mac_algorithms_server_to_client = tr.readNameList();
-        kp.compression_algorithms_client_to_server = tr.readNameList();
-        kp.compression_algorithms_server_to_client = tr.readNameList();
-        kp.languages_client_to_server = tr.readNameList();
-        kp.languages_server_to_client = tr.readNameList();
-        kp.first_kex_packet_follows = tr.readBoolean();
-        kp.reserved_field1 = tr.readUINT32();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in KexInitPacket!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_KEXINIT);
-            tw.writeBytes(kp.cookie, 0, 16);
-            tw.writeNameList(kp.kex_algorithms);
-            tw.writeNameList(kp.server_host_key_algorithms);
-            tw.writeNameList(kp.encryption_algorithms_client_to_server);
-            tw.writeNameList(kp.encryption_algorithms_server_to_client);
-            tw.writeNameList(kp.mac_algorithms_client_to_server);
-            tw.writeNameList(kp.mac_algorithms_server_to_client);
-            tw.writeNameList(kp.compression_algorithms_client_to_server);
-            tw.writeNameList(kp.compression_algorithms_server_to_client);
-            tw.writeNameList(kp.languages_client_to_server);
-            tw.writeNameList(kp.languages_server_to_client);
-            tw.writeBoolean(kp.first_kex_packet_follows);
-            tw.writeUINT32(kp.reserved_field1);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-
-    public KexParameters getKexParameters() {
-        return kp;
-    }
-
-    public String[] getCompression_algorithms_client_to_server() {
-        return kp.compression_algorithms_client_to_server;
-    }
-
-    public String[] getCompression_algorithms_server_to_client() {
-        return kp.compression_algorithms_server_to_client;
-    }
-
-    public byte[] getCookie() {
-        return kp.cookie;
-    }
-
-    public String[] getEncryption_algorithms_client_to_server() {
-        return kp.encryption_algorithms_client_to_server;
-    }
-
-    public String[] getEncryption_algorithms_server_to_client() {
-        return kp.encryption_algorithms_server_to_client;
-    }
-
-    public boolean isFirst_kex_packet_follows() {
-        return kp.first_kex_packet_follows;
-    }
-
-    public String[] getKex_algorithms() {
-        return kp.kex_algorithms;
-    }
-
-    public String[] getLanguages_client_to_server() {
-        return kp.languages_client_to_server;
-    }
-
-    public String[] getLanguages_server_to_client() {
-        return kp.languages_server_to_client;
-    }
-
-    public String[] getMac_algorithms_client_to_server() {
-        return kp.mac_algorithms_client_to_server;
-    }
-
-    public String[] getMac_algorithms_server_to_client() {
-        return kp.mac_algorithms_server_to_client;
-    }
-
-    public int getReserved_field1() {
-        return kp.reserved_field1;
-    }
-
-    public String[] getServer_host_key_algorithms() {
-        return kp.server_host_key_algorithms;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketNewKeys.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketNewKeys.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketNewKeys.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketNewKeys {
-    byte[] payload;
-
-    public PacketNewKeys() {
-    }
-
-    public PacketNewKeys(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_NEWKEYS)
-            throw new IOException("This is not a SSH_MSG_NEWKEYS! ("
-                                  + packet_type + ")");
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_NEWKEYS packet!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_NEWKEYS);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketOpenDirectTCPIPChannel.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketOpenDirectTCPIPChannel.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketOpenDirectTCPIPChannel {
-    byte[] payload;
-
-    int channelID;
-    int initialWindowSize;
-    int maxPacketSize;
-
-    String host_to_connect;
-    int port_to_connect;
-    String originator_IP_address;
-    int originator_port;
-
-    public PacketOpenDirectTCPIPChannel(int channelID, int initialWindowSize, int maxPacketSize,
-                                        String host_to_connect, int port_to_connect, String originator_IP_address,
-                                        int originator_port) {
-        this.channelID = channelID;
-        this.initialWindowSize = initialWindowSize;
-        this.maxPacketSize = maxPacketSize;
-        this.host_to_connect = host_to_connect;
-        this.port_to_connect = port_to_connect;
-        this.originator_IP_address = originator_IP_address;
-        this.originator_port = originator_port;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN);
-            tw.writeString("direct-tcpip");
-            tw.writeUINT32(channelID);
-            tw.writeUINT32(initialWindowSize);
-            tw.writeUINT32(maxPacketSize);
-            tw.writeString(host_to_connect);
-            tw.writeUINT32(port_to_connect);
-            tw.writeString(originator_IP_address);
-            tw.writeUINT32(originator_port);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketOpenSessionChannel.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketOpenSessionChannel.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketOpenSessionChannel.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketOpenSessionChannel {
-    byte[] payload;
-
-    int channelID;
-    int initialWindowSize;
-    int maxPacketSize;
-
-    public PacketOpenSessionChannel(int channelID, int initialWindowSize,
-                                    int maxPacketSize) {
-        this.channelID = channelID;
-        this.initialWindowSize = initialWindowSize;
-        this.maxPacketSize = maxPacketSize;
-    }
-
-    public PacketOpenSessionChannel(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN)
-            throw new IOException("This is not a SSH_MSG_CHANNEL_OPEN! ("
-                                  + packet_type + ")");
-
-        channelID = tr.readUINT32();
-        initialWindowSize = tr.readUINT32();
-        maxPacketSize = tr.readUINT32();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN packet!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN);
-            tw.writeString("session");
-            tw.writeUINT32(channelID);
-            tw.writeUINT32(initialWindowSize);
-            tw.writeUINT32(maxPacketSize);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketServiceAccept.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketServiceAccept.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketServiceAccept.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class PacketServiceAccept {
-    byte[] payload;
-
-    String serviceName;
-
-    public PacketServiceAccept(String serviceName) {
-        this.serviceName = serviceName;
-    }
-
-    public PacketServiceAccept(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_SERVICE_ACCEPT)
-            throw new IOException("This is not a SSH_MSG_SERVICE_ACCEPT! (" + packet_type + ")");
-
-        /* Be clever in case the server is not. Some servers seem to violate RFC4253 */
-
-        if (tr.remain() > 0) {
-            serviceName = tr.readString();
-        }
-        else {
-            serviceName = "";
-        }
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_SERVICE_ACCEPT packet!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_SERVICE_ACCEPT);
-            tw.writeString(serviceName);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketServiceRequest.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketServiceRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketServiceRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketServiceRequest {
-    byte[] payload;
-
-    String serviceName;
-
-    public PacketServiceRequest(String serviceName) {
-        this.serviceName = serviceName;
-    }
-
-    public PacketServiceRequest(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_SERVICE_REQUEST)
-            throw new IOException("This is not a SSH_MSG_SERVICE_REQUEST! ("
-                                  + packet_type + ")");
-
-        serviceName = tr.readString();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_SERVICE_REQUEST packet!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_SERVICE_REQUEST);
-            tw.writeString(serviceName);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketSessionExecCommand.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketSessionExecCommand.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketSessionExecCommand.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketSessionExecCommand {
-    byte[] payload;
-
-    public int recipientChannelID;
-    public boolean wantReply;
-    public String command;
-
-    public PacketSessionExecCommand(int recipientChannelID, boolean wantReply, String command) {
-        this.recipientChannelID = recipientChannelID;
-        this.wantReply = wantReply;
-        this.command = command;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeString("exec");
-            tw.writeBoolean(wantReply);
-            tw.writeString(command);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketSessionPtyRequest.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketSessionPtyRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketSessionPtyRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketSessionPtyRequest {
-    byte[] payload;
-
-    public int recipientChannelID;
-    public boolean wantReply;
-    public String term;
-    public int character_width;
-    public int character_height;
-    public int pixel_width;
-    public int pixel_height;
-    public byte[] terminal_modes;
-
-    public PacketSessionPtyRequest(int recipientChannelID, boolean wantReply, String term,
-                                   int character_width, int character_height, int pixel_width, int pixel_height,
-                                   byte[] terminal_modes) {
-        this.recipientChannelID = recipientChannelID;
-        this.wantReply = wantReply;
-        this.term = term;
-        this.character_width = character_width;
-        this.character_height = character_height;
-        this.pixel_width = pixel_width;
-        this.pixel_height = pixel_height;
-        this.terminal_modes = terminal_modes;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeString("pty-req");
-            tw.writeBoolean(wantReply);
-            tw.writeString(term);
-            tw.writeUINT32(character_width);
-            tw.writeUINT32(character_height);
-            tw.writeUINT32(pixel_width);
-            tw.writeUINT32(pixel_height);
-            tw.writeString(terminal_modes, 0, terminal_modes.length);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketSessionPtyResize.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-package com.trilead.ssh2.packets;
-
-public class PacketSessionPtyResize {
-    byte[] payload;
-
-    public int recipientChannelID;
-    public int width;
-    public int height;
-    public int pixelWidth;
-    public int pixelHeight;
-
-    public PacketSessionPtyResize(int recipientChannelID, int width, int height, int pixelWidth, int pixelHeight) {
-        this.recipientChannelID = recipientChannelID;
-        this.width = width;
-        this.height = height;
-        this.pixelWidth = pixelWidth;
-        this.pixelHeight = pixelHeight;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeString("window-change");
-            tw.writeBoolean(false);
-            tw.writeUINT32(width);
-            tw.writeUINT32(height);
-            tw.writeUINT32(pixelWidth);
-            tw.writeUINT32(pixelHeight);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
-
-
--- a/src/com/trilead/ssh2/packets/PacketSessionStartShell.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketSessionStartShell.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketSessionStartShell.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketSessionStartShell {
-    byte[] payload;
-
-    public int recipientChannelID;
-    public boolean wantReply;
-
-    public PacketSessionStartShell(int recipientChannelID, boolean wantReply) {
-        this.recipientChannelID = recipientChannelID;
-        this.wantReply = wantReply;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeString("shell");
-            tw.writeBoolean(wantReply);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketSessionSubsystemRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketSessionSubsystemRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketSessionSubsystemRequest {
-    byte[] payload;
-
-    public int recipientChannelID;
-    public boolean wantReply;
-    public String subsystem;
-
-    public PacketSessionSubsystemRequest(int recipientChannelID, boolean wantReply, String subsystem) {
-        this.recipientChannelID = recipientChannelID;
-        this.wantReply = wantReply;
-        this.subsystem = subsystem;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeString("subsystem");
-            tw.writeBoolean(wantReply);
-            tw.writeString(subsystem);
-            payload = tw.getBytes();
-            tw.getBytes(payload);
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketSessionX11Request.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketSessionX11Request.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketSessionX11Request.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketSessionX11Request {
-    byte[] payload;
-
-    public int recipientChannelID;
-    public boolean wantReply;
-
-    public boolean singleConnection;
-    String x11AuthenticationProtocol;
-    String x11AuthenticationCookie;
-    int x11ScreenNumber;
-
-    public PacketSessionX11Request(int recipientChannelID, boolean wantReply, boolean singleConnection,
-                                   String x11AuthenticationProtocol, String x11AuthenticationCookie, int x11ScreenNumber) {
-        this.recipientChannelID = recipientChannelID;
-        this.wantReply = wantReply;
-        this.singleConnection = singleConnection;
-        this.x11AuthenticationProtocol = x11AuthenticationProtocol;
-        this.x11AuthenticationCookie = x11AuthenticationCookie;
-        this.x11ScreenNumber = x11ScreenNumber;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
-            tw.writeUINT32(recipientChannelID);
-            tw.writeString("x11-req");
-            tw.writeBoolean(wantReply);
-            tw.writeBoolean(singleConnection);
-            tw.writeString(x11AuthenticationProtocol);
-            tw.writeString(x11AuthenticationCookie);
-            tw.writeUINT32(x11ScreenNumber);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketUserauthBanner.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthBanner.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthBanner.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthBanner {
-    byte[] payload;
-
-    String message;
-    String language;
-
-    public PacketUserauthBanner(String message, String language) {
-        this.message = message;
-        this.language = language;
-    }
-
-    public String getBanner() {
-        return message;
-    }
-
-    public PacketUserauthBanner(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_USERAUTH_BANNER)
-            throw new IOException("This is not a SSH_MSG_USERAUTH_BANNER! (" + packet_type + ")");
-
-        message = tr.readString("UTF-8");
-        language = tr.readString();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_USERAUTH_BANNER);
-            tw.writeString(message);
-            tw.writeString(language);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketUserauthFailure.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthBanner.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthFailure.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthFailure {
-    byte[] payload;
-
-    String[] authThatCanContinue;
-    boolean partialSuccess;
-
-    public PacketUserauthFailure(String[] authThatCanContinue, boolean partialSuccess) {
-        this.authThatCanContinue = authThatCanContinue;
-        this.partialSuccess = partialSuccess;
-    }
-
-    public PacketUserauthFailure(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_USERAUTH_FAILURE)
-            throw new IOException("This is not a SSH_MSG_USERAUTH_FAILURE! (" + packet_type + ")");
-
-        authThatCanContinue = tr.readNameList();
-        partialSuccess = tr.readBoolean();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_USERAUTH_FAILURE packet!");
-    }
-
-    public String[] getAuthThatCanContinue() {
-        return authThatCanContinue;
-    }
-
-    public boolean isPartialSuccess() {
-        return partialSuccess;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthInfoRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthInfoRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthInfoRequest {
-    byte[] payload;
-
-    String name;
-    String instruction;
-    String languageTag;
-    int numPrompts;
-
-    String prompt[];
-    boolean echo[];
-
-    public PacketUserauthInfoRequest(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
-            throw new IOException("This is not a SSH_MSG_USERAUTH_INFO_REQUEST! (" + packet_type + ")");
-
-        name = tr.readString();
-        instruction = tr.readString();
-        languageTag = tr.readString();
-        numPrompts = tr.readUINT32();
-        prompt = new String[numPrompts];
-        echo = new boolean[numPrompts];
-
-        for (int i = 0; i < numPrompts; i++) {
-            prompt[i] = tr.readString();
-            echo[i] = tr.readBoolean();
-        }
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_USERAUTH_INFO_REQUEST packet!");
-    }
-
-    public boolean[] getEcho() {
-        return echo;
-    }
-
-    public String getInstruction() {
-        return instruction;
-    }
-
-    public String getLanguageTag() {
-        return languageTag;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public int getNumPrompts() {
-        return numPrompts;
-    }
-
-    public String[] getPrompt() {
-        return prompt;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketUserauthInfoResponse.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthInfoResponse.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthInfoResponse {
-    byte[] payload;
-
-    String[] responses;
-
-    public PacketUserauthInfoResponse(String[] responses) {
-        this.responses = responses;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_USERAUTH_INFO_RESPONSE);
-            tw.writeUINT32(responses.length);
-
-            for (int i = 0; i < responses.length; i++)
-                tw.writeString(responses[i]);
-
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketUserauthRequestInteractive.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthRequestInteractive.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthRequestInteractive {
-    byte[] payload;
-
-    String userName;
-    String serviceName;
-    String[] submethods;
-
-    public PacketUserauthRequestInteractive(String serviceName, String user, String[] submethods) {
-        this.serviceName = serviceName;
-        this.userName = user;
-        this.submethods = submethods;
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
-            tw.writeString(userName);
-            tw.writeString(serviceName);
-            tw.writeString("keyboard-interactive");
-            tw.writeString(""); // draft-ietf-secsh-newmodes-04.txt says that
-            // the language tag should be empty.
-            tw.writeNameList(submethods);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestNone.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthRequestPassword.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthRequestNone.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthRequestNone {
-    byte[] payload;
-
-    String userName;
-    String serviceName;
-
-    public PacketUserauthRequestNone(String serviceName, String user) {
-        this.serviceName = serviceName;
-        this.userName = user;
-    }
-
-    public PacketUserauthRequestNone(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST)
-            throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! (" + packet_type + ")");
-
-        userName = tr.readString();
-        serviceName = tr.readString();
-        String method = tr.readString();
-
-        if (method.equals("none") == false)
-            throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST with type none!");
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
-            tw.writeString(userName);
-            tw.writeString(serviceName);
-            tw.writeString("none");
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthRequestPassword.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthRequestPassword.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthRequestPassword {
-    byte[] payload;
-
-    String userName;
-    String serviceName;
-    String password;
-
-    public PacketUserauthRequestPassword(String serviceName, String user, String pass) {
-        this.serviceName = serviceName;
-        this.userName = user;
-        this.password = pass;
-    }
-
-    public PacketUserauthRequestPassword(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST)
-            throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! (" + packet_type + ")");
-
-        userName = tr.readString();
-        serviceName = tr.readString();
-        String method = tr.readString();
-
-        if (method.equals("password") == false)
-            throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST with type password!");
-
-        /* ... */
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
-            tw.writeString(userName);
-            tw.writeString(serviceName);
-            tw.writeString("password");
-            tw.writeBoolean(false);
-            tw.writeString(password);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthRequestPublicKey.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthRequestPublicKey.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthRequestPublicKey {
-    byte[] payload;
-
-    String userName;
-    String serviceName;
-    String password;
-    String pkAlgoName;
-    byte[] pk;
-    byte[] sig;
-
-    public PacketUserauthRequestPublicKey(String serviceName, String user,
-                                          String pkAlgorithmName, byte[] pk, byte[] sig) {
-        this.serviceName = serviceName;
-        this.userName = user;
-        this.pkAlgoName = pkAlgorithmName;
-        this.pk = pk;
-        this.sig = sig;
-    }
-
-    public PacketUserauthRequestPublicKey(byte payload[], int off, int len) throws IOException {
-        this.payload = new byte[len];
-        System.arraycopy(payload, off, this.payload, 0, len);
-        TypesReader tr = new TypesReader(payload, off, len);
-        int packet_type = tr.readByte();
-
-        if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST)
-            throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! ("
-                                  + packet_type + ")");
-
-        throw new IOException("Not implemented!");
-    }
-
-    public byte[] getPayload() {
-        if (payload == null) {
-            TypesWriter tw = new TypesWriter();
-            tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
-            tw.writeString(userName);
-            tw.writeString(serviceName);
-            tw.writeString("publickey");
-            tw.writeBoolean(true);
-            tw.writeString(pkAlgoName);
-            tw.writeString(pk, 0, pk.length);
-            tw.writeString(sig, 0, sig.length);
-            payload = tw.getBytes();
-        }
-
-        return payload;
-    }
-}
--- a/src/com/trilead/ssh2/packets/Packets.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * Packets.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Packets.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class Packets {
-    public static final int SSH_MSG_DISCONNECT = 1;
-    public static final int SSH_MSG_IGNORE = 2;
-    public static final int SSH_MSG_UNIMPLEMENTED = 3;
-    public static final int SSH_MSG_DEBUG = 4;
-    public static final int SSH_MSG_SERVICE_REQUEST = 5;
-    public static final int SSH_MSG_SERVICE_ACCEPT = 6;
-
-    public static final int SSH_MSG_KEXINIT = 20;
-    public static final int SSH_MSG_NEWKEYS = 21;
-
-    public static final int SSH_MSG_KEXDH_INIT = 30;
-    public static final int SSH_MSG_KEXDH_REPLY = 31;
-
-    public static final int SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30;
-    public static final int SSH_MSG_KEX_DH_GEX_REQUEST = 34;
-    public static final int SSH_MSG_KEX_DH_GEX_GROUP = 31;
-    public static final int SSH_MSG_KEX_DH_GEX_INIT = 32;
-    public static final int SSH_MSG_KEX_DH_GEX_REPLY = 33;
-
-    public static final int SSH_MSG_USERAUTH_REQUEST = 50;
-    public static final int SSH_MSG_USERAUTH_FAILURE = 51;
-    public static final int SSH_MSG_USERAUTH_SUCCESS = 52;
-    public static final int SSH_MSG_USERAUTH_BANNER = 53;
-    public static final int SSH_MSG_USERAUTH_INFO_REQUEST = 60;
-    public static final int SSH_MSG_USERAUTH_INFO_RESPONSE = 61;
-
-    public static final int SSH_MSG_GLOBAL_REQUEST = 80;
-    public static final int SSH_MSG_REQUEST_SUCCESS = 81;
-    public static final int SSH_MSG_REQUEST_FAILURE = 82;
-
-    public static final int SSH_MSG_CHANNEL_OPEN = 90;
-    public static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91;
-    public static final int SSH_MSG_CHANNEL_OPEN_FAILURE = 92;
-    public static final int SSH_MSG_CHANNEL_WINDOW_ADJUST = 93;
-    public static final int SSH_MSG_CHANNEL_DATA = 94;
-    public static final int SSH_MSG_CHANNEL_EXTENDED_DATA = 95;
-    public static final int SSH_MSG_CHANNEL_EOF = 96;
-    public static final int SSH_MSG_CHANNEL_CLOSE = 97;
-    public static final int SSH_MSG_CHANNEL_REQUEST = 98;
-    public static final int SSH_MSG_CHANNEL_SUCCESS = 99;
-    public static final int SSH_MSG_CHANNEL_FAILURE = 100;
-
-    public static final int SSH_EXTENDED_DATA_STDERR = 1;
-
-    public static final int SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1;
-    public static final int SSH_DISCONNECT_PROTOCOL_ERROR = 2;
-    public static final int SSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3;
-    public static final int SSH_DISCONNECT_RESERVED = 4;
-    public static final int SSH_DISCONNECT_MAC_ERROR = 5;
-    public static final int SSH_DISCONNECT_COMPRESSION_ERROR = 6;
-    public static final int SSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7;
-    public static final int SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8;
-    public static final int SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9;
-    public static final int SSH_DISCONNECT_CONNECTION_LOST = 10;
-    public static final int SSH_DISCONNECT_BY_APPLICATION = 11;
-    public static final int SSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12;
-    public static final int SSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13;
-    public static final int SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14;
-    public static final int SSH_DISCONNECT_ILLEGAL_USER_NAME = 15;
-
-    public static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1;
-    public static final int SSH_OPEN_CONNECT_FAILED = 2;
-    public static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3;
-    public static final int SSH_OPEN_RESOURCE_SHORTAGE = 4;
-
-    private static final String[] reverseNames = new String[101];
-
-    static {
-        reverseNames[1] = "SSH_MSG_DISCONNECT";
-        reverseNames[2] = "SSH_MSG_IGNORE";
-        reverseNames[3] = "SSH_MSG_UNIMPLEMENTED";
-        reverseNames[4] = "SSH_MSG_DEBUG";
-        reverseNames[5] = "SSH_MSG_SERVICE_REQUEST";
-        reverseNames[6] = "SSH_MSG_SERVICE_ACCEPT";
-        reverseNames[20] = "SSH_MSG_KEXINIT";
-        reverseNames[21] = "SSH_MSG_NEWKEYS";
-        reverseNames[30] = "SSH_MSG_KEXDH_INIT";
-        reverseNames[31] = "SSH_MSG_KEXDH_REPLY/SSH_MSG_KEX_DH_GEX_GROUP";
-        reverseNames[32] = "SSH_MSG_KEX_DH_GEX_INIT";
-        reverseNames[33] = "SSH_MSG_KEX_DH_GEX_REPLY";
-        reverseNames[34] = "SSH_MSG_KEX_DH_GEX_REQUEST";
-        reverseNames[50] = "SSH_MSG_USERAUTH_REQUEST";
-        reverseNames[51] = "SSH_MSG_USERAUTH_FAILURE";
-        reverseNames[52] = "SSH_MSG_USERAUTH_SUCCESS";
-        reverseNames[53] = "SSH_MSG_USERAUTH_BANNER";
-        reverseNames[60] = "SSH_MSG_USERAUTH_INFO_REQUEST";
-        reverseNames[61] = "SSH_MSG_USERAUTH_INFO_RESPONSE";
-        reverseNames[80] = "SSH_MSG_GLOBAL_REQUEST";
-        reverseNames[81] = "SSH_MSG_REQUEST_SUCCESS";
-        reverseNames[82] = "SSH_MSG_REQUEST_FAILURE";
-        reverseNames[90] = "SSH_MSG_CHANNEL_OPEN";
-        reverseNames[91] = "SSH_MSG_CHANNEL_OPEN_CONFIRMATION";
-        reverseNames[92] = "SSH_MSG_CHANNEL_OPEN_FAILURE";
-        reverseNames[93] = "SSH_MSG_CHANNEL_WINDOW_ADJUST";
-        reverseNames[94] = "SSH_MSG_CHANNEL_DATA";
-        reverseNames[95] = "SSH_MSG_CHANNEL_EXTENDED_DATA";
-        reverseNames[96] = "SSH_MSG_CHANNEL_EOF";
-        reverseNames[97] = "SSH_MSG_CHANNEL_CLOSE";
-        reverseNames[98] = "SSH_MSG_CHANNEL_REQUEST";
-        reverseNames[99] = "SSH_MSG_CHANNEL_SUCCESS";
-        reverseNames[100] = "SSH_MSG_CHANNEL_FAILURE";
-    }
-
-    public static final String getMessageName(int type) {
-        String res = null;
-
-        if ((type >= 0) && (type < reverseNames.length)) {
-            res = reverseNames[type];
-        }
-
-        return (res == null) ? ("UNKNOWN MSG " + type) : res;
-    }
-
-    //  public static final void debug(String tag, byte[] msg)
-    //  {
-    //      System.err.println(tag + " Type: " + msg[0] + ", LEN: " + msg.length);
-    //
-    //      for (int i = 0; i < msg.length; i++)
-    //      {
-    //          if (((msg[i] >= 'a') && (msg[i] <= 'z')) || ((msg[i] >= 'A') && (msg[i] <= 'Z'))
-    //                  || ((msg[i] >= '0') && (msg[i] <= '9')) || (msg[i] == ' '))
-    //              System.err.print((char) msg[i]);
-    //          else
-    //              System.err.print(".");
-    //      }
-    //      System.err.println();
-    //      System.err.flush();
-    //  }
-}
--- a/src/com/trilead/ssh2/packets/TypesReader.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-import java.math.BigInteger;
-
-import com.trilead.ssh2.util.Tokenizer;
-
-
-/**
- * TypesReader.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: TypesReader.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class TypesReader {
-    byte[] arr;
-    int pos = 0;
-    int max = 0;
-
-    public TypesReader(byte[] arr) {
-        this.arr = arr;
-        pos = 0;
-        max = arr.length;
-    }
-
-    public TypesReader(byte[] arr, int off) {
-        this.arr = arr;
-        this.pos = off;
-        this.max = arr.length;
-
-        if ((pos < 0) || (pos > arr.length))
-            throw new IllegalArgumentException("Illegal offset.");
-    }
-
-    public TypesReader(byte[] arr, int off, int len) {
-        this.arr = arr;
-        this.pos = off;
-        this.max = off + len;
-
-        if ((pos < 0) || (pos > arr.length))
-            throw new IllegalArgumentException("Illegal offset.");
-
-        if ((max < 0) || (max > arr.length))
-            throw new IllegalArgumentException("Illegal length.");
-    }
-
-    public int readByte() throws IOException {
-        if (pos >= max)
-            throw new IOException("Packet too short.");
-
-        return (arr[pos++] & 0xff);
-    }
-
-    public byte[] readBytes(int len) throws IOException {
-        if ((pos + len) > max)
-            throw new IOException("Packet too short.");
-
-        byte[] res = new byte[len];
-        System.arraycopy(arr, pos, res, 0, len);
-        pos += len;
-        return res;
-    }
-
-    public void readBytes(byte[] dst, int off, int len) throws IOException {
-        if ((pos + len) > max)
-            throw new IOException("Packet too short.");
-
-        System.arraycopy(arr, pos, dst, off, len);
-        pos += len;
-    }
-
-    public boolean readBoolean() throws IOException {
-        if (pos >= max)
-            throw new IOException("Packet too short.");
-
-        return (arr[pos++] != 0);
-    }
-
-    public int readUINT32() throws IOException {
-        if ((pos + 4) > max)
-            throw new IOException("Packet too short.");
-
-        return ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
-               | (arr[pos++] & 0xff);
-    }
-
-    public long readUINT64() throws IOException {
-        if ((pos + 8) > max)
-            throw new IOException("Packet too short.");
-
-        long high = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
-                    | (arr[pos++] & 0xff); /* sign extension may take place - will be shifted away =) */
-        long low = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
-                   | (arr[pos++] & 0xff); /* sign extension may take place - handle below */
-        return (high << 32) | (low & 0xffffffffl); /* see Java language spec (15.22.1, 5.6.2) */
-    }
-
-    public BigInteger readMPINT() throws IOException {
-        BigInteger b;
-        byte raw[] = readByteString();
-
-        if (raw.length == 0)
-            b = BigInteger.ZERO;
-        else
-            b = new BigInteger(raw);
-
-        return b;
-    }
-
-    public byte[] readByteString() throws IOException {
-        int len = readUINT32();
-
-        if ((len + pos) > max)
-            throw new IOException("Malformed SSH byte string.");
-
-        byte[] res = new byte[len];
-        System.arraycopy(arr, pos, res, 0, len);
-        pos += len;
-        return res;
-    }
-
-    public String readString(String charsetName) throws IOException {
-        int len = readUINT32();
-
-        if ((len + pos) > max)
-            throw new IOException("Malformed SSH string.");
-
-        String res = (charsetName == null) ? new String(arr, pos, len) : new String(arr, pos, len, charsetName);
-        pos += len;
-        return res;
-    }
-
-    public String readString() throws IOException {
-        int len = readUINT32();
-
-        if ((len + pos) > max)
-            throw new IOException("Malformed SSH string.");
-
-        String res = new String(arr, pos, len, "ISO-8859-1");
-        pos += len;
-        return res;
-    }
-
-    public String[] readNameList() throws IOException {
-        return Tokenizer.parseTokens(readString(), ',');
-    }
-
-    public int remain() {
-        return max - pos;
-    }
-
-}
--- a/src/com/trilead/ssh2/packets/TypesWriter.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,149 +0,0 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-
-/**
- * TypesWriter.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: TypesWriter.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class TypesWriter {
-    byte arr[];
-    int pos;
-
-    public TypesWriter() {
-        arr = new byte[256];
-        pos = 0;
-    }
-
-    private void resize(int len) {
-        byte new_arr[] = new byte[len];
-        System.arraycopy(arr, 0, new_arr, 0, arr.length);
-        arr = new_arr;
-    }
-
-    public int length() {
-        return pos;
-    }
-
-    public byte[] getBytes() {
-        byte[] dst = new byte[pos];
-        System.arraycopy(arr, 0, dst, 0, pos);
-        return dst;
-    }
-
-    public void getBytes(byte dst[]) {
-        System.arraycopy(arr, 0, dst, 0, pos);
-    }
-
-    public void writeUINT32(int val, int off) {
-        if ((off + 4) > arr.length)
-            resize(off + 32);
-
-        arr[off++] = (byte)(val >> 24);
-        arr[off++] = (byte)(val >> 16);
-        arr[off++] = (byte)(val >> 8);
-        arr[off++] = (byte) val;
-    }
-
-    public void writeUINT32(int val) {
-        writeUINT32(val, pos);
-        pos += 4;
-    }
-
-    public void writeUINT64(long val) {
-        if ((pos + 8) > arr.length)
-            resize(arr.length + 32);
-
-        arr[pos++] = (byte)(val >> 56);
-        arr[pos++] = (byte)(val >> 48);
-        arr[pos++] = (byte)(val >> 40);
-        arr[pos++] = (byte)(val >> 32);
-        arr[pos++] = (byte)(val >> 24);
-        arr[pos++] = (byte)(val >> 16);
-        arr[pos++] = (byte)(val >> 8);
-        arr[pos++] = (byte) val;
-    }
-
-    public void writeBoolean(boolean v) {
-        if ((pos + 1) > arr.length)
-            resize(arr.length + 32);
-
-        arr[pos++] = v ? (byte) 1 : (byte) 0;
-    }
-
-    public void writeByte(int v, int off) {
-        if ((off + 1) > arr.length)
-            resize(off + 32);
-
-        arr[off] = (byte) v;
-    }
-
-    public void writeByte(int v) {
-        writeByte(v, pos);
-        pos++;
-    }
-
-    public void writeMPInt(BigInteger b) {
-        byte raw[] = b.toByteArray();
-
-        if ((raw.length == 1) && (raw[0] == 0))
-            writeUINT32(0); /* String with zero bytes of data */
-        else
-            writeString(raw, 0, raw.length);
-    }
-
-    public void writeBytes(byte[] buff) {
-        writeBytes(buff, 0, buff.length);
-    }
-
-    public void writeBytes(byte[] buff, int off, int len) {
-        if ((pos + len) > arr.length)
-            resize(arr.length + len + 32);
-
-        System.arraycopy(buff, off, arr, pos, len);
-        pos += len;
-    }
-
-    public void writeString(byte[] buff, int off, int len) {
-        writeUINT32(len);
-        writeBytes(buff, off, len);
-    }
-
-    public void writeString(String v) {
-        byte[] b;
-
-        try {
-            /* All Java JVMs must support ISO-8859-1 */
-            b = v.getBytes("ISO-8859-1");
-        }
-        catch (UnsupportedEncodingException ignore) {
-            b = v.getBytes();
-        }
-
-        writeUINT32(b.length);
-        writeBytes(b, 0, b.length);
-    }
-
-    public void writeString(String v, String charsetName) throws UnsupportedEncodingException {
-        byte[] b = (charsetName == null) ? v.getBytes() : v.getBytes(charsetName);
-        writeUINT32(b.length);
-        writeBytes(b, 0, b.length);
-    }
-
-    public void writeNameList(String v[]) {
-        StringBuffer sb = new StringBuffer();
-
-        for (int i = 0; i < v.length; i++) {
-            if (i > 0)
-                sb.append(',');
-
-            sb.append(v[i]);
-        }
-
-        writeString(sb.toString());
-    }
-}
--- a/src/com/trilead/ssh2/sftp/AttrTextHints.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * Values for the 'text-hint' field in the SFTP ATTRS data type.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AttrTextHints.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class AttrTextHints {
-    /**
-     * The server knows the file is a text file, and should be opened
-     * using the SSH_FXF_ACCESS_TEXT_MODE flag.
-     */
-    public static final int SSH_FILEXFER_ATTR_KNOWN_TEXT = 0x00;
-
-    /**
-     * The server has applied a heuristic or other mechanism and
-     * believes that the file should be opened with the
-     * SSH_FXF_ACCESS_TEXT_MODE flag.
-     */
-    public static final int SSH_FILEXFER_ATTR_GUESSED_TEXT = 0x01;
-
-    /**
-     * The server knows the file has binary content.
-     */
-    public static final int SSH_FILEXFER_ATTR_KNOWN_BINARY = 0x02;
-
-    /**
-     * The server has applied a heuristic or other mechanism and
-     * believes has binary content, and should not be opened with the
-     * SSH_FXF_ACCESS_TEXT_MODE flag.
-     */
-    public static final int SSH_FILEXFER_ATTR_GUESSED_BINARY = 0x03;
-}
--- a/src/com/trilead/ssh2/sftp/AttribBits.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,128 +0,0 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * SFTP Attribute Bits for the "attrib-bits" and "attrib-bits-valid" fields
- * of the SFTP ATTR data type.
- * <p>
- * Yes, these are the "attrib-bits", even though they have "_FLAGS_" in
- * their name. Don't ask - I did not invent it.
- * <p>
- * "<i>These fields, taken together, reflect various attributes of the file
- * or directory, on the server. Bits not set in 'attrib-bits-valid' MUST be
- * ignored in the 'attrib-bits' field.  This allows both the server and the
- * client to communicate only the bits it knows about without inadvertently
- * twiddling bits they don't understand.</i>"
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AttribBits.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class AttribBits {
-    /**
-     * Advisory, read-only bit. This bit is not part of the access
-     * control information on the file, but is rather an advisory field
-     * indicating that the file should not be written.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_READONLY = 0x00000001;
-
-    /**
-     * The file is part of the operating system.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_SYSTEM = 0x00000002;
-
-    /**
-     * File SHOULD NOT be shown to user unless specifically requested.
-     * For example, most UNIX systems SHOULD set this bit if the filename
-     * begins with a 'period'. This bit may be read-only (see section 5.4 of
-     * the SFTP standard draft). Most UNIX systems will not allow this to be
-     * changed.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_HIDDEN = 0x00000004;
-
-    /**
-     * This attribute applies only to directories. This attribute is
-     * always read-only, and cannot be modified. This attribute means
-     * that files and directory names in this directory should be compared
-     * without regard to case.
-     * <p>
-     * It is recommended that where possible, the server's filesystem be
-     * allowed to do comparisons. For example, if a client wished to prompt
-     * a user before overwriting a file, it should not compare the new name
-     * with the previously retrieved list of names in the directory. Rather,
-     * it should first try to create the new file by specifying
-     * SSH_FXF_CREATE_NEW flag. Then, if this fails and returns
-     * SSH_FX_FILE_ALREADY_EXISTS, it should prompt the user and then retry
-     * the create specifying SSH_FXF_CREATE_TRUNCATE.
-     * <p>
-     * Unless otherwise specified, filenames are assumed to be case sensitive.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE = 0x00000008;
-
-    /**
-     * The file should be included in backup / archive operations.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_ARCHIVE = 0x00000010;
-
-    /**
-     * The file is stored on disk using file-system level transparent
-     * encryption. This flag does not affect the file data on the wire
-     * (for either READ or WRITE requests.)
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED = 0x00000020;
-
-    /**
-     * The file is stored on disk using file-system level transparent
-     * compression. This flag does not affect the file data on the wire.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_COMPRESSED = 0x00000040;
-
-    /**
-     * The file is a sparse file; this means that file blocks that have
-     * not been explicitly written are not stored on disk. For example, if
-     * a client writes a buffer at 10 M from the beginning of the file,
-     * the blocks between the previous EOF marker and the 10 M offset would
-     * not consume physical disk space.
-     * <p>
-     * Some servers may store all files as sparse files, in which case
-     * this bit will be unconditionally set. Other servers may not have
-     * a mechanism for determining if the file is sparse, and so the file
-     * MAY be stored sparse even if this flag is not set.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_SPARSE = 0x00000080;
-
-    /**
-     * Opening the file without either the SSH_FXF_ACCESS_APPEND_DATA or
-     * the SSH_FXF_ACCESS_APPEND_DATA_ATOMIC flag (see section 8.1.1.3
-     * of the SFTP standard draft) MUST result in an
-     * SSH_FX_INVALID_PARAMETER error.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_APPEND_ONLY = 0x00000100;
-
-    /**
-     * The file cannot be deleted or renamed, no hard link can be created
-     * to this file, and no data can be written to the file.
-     * <p>
-     * This bit implies a stronger level of protection than
-     * SSH_FILEXFER_ATTR_FLAGS_READONLY, the file permission mask or ACLs.
-     * Typically even the superuser cannot write to immutable files, and
-     * only the superuser can set or remove the bit.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_IMMUTABLE = 0x00000200;
-
-    /**
-     * When the file is modified, the changes are written synchronously
-     * to the disk.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_SYNC = 0x00000400;
-
-    /**
-     * The server MAY include this bit in a directory listing or realpath
-     * response. It indicates there was a failure in the translation to UTF-8.
-     * If this flag is included, the server SHOULD also include the
-     * UNTRANSLATED_NAME attribute.
-     */
-    public static final int SSH_FILEXFER_ATTR_FLAGS_TRANSLATION_ERR = 0x00000800;
-
-}
--- a/src/com/trilead/ssh2/sftp/AttribFlags.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * Attribute Flags. The 'valid-attribute-flags' field in
- * the SFTP ATTRS data type specifies which of the fields are actually present.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AttribFlags.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class AttribFlags {
-    /**
-     * Indicates that the 'allocation-size' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
-
-    /** Protocol version 6:
-     * 0x00000002 was used in a previous version of this protocol.
-     * It is now a reserved value and MUST NOT appear in the mask.
-     * Some future version of this protocol may reuse this value.
-     */
-    public static final int SSH_FILEXFER_ATTR_V3_UIDGID = 0x00000002;
-
-    /**
-     * Indicates that the 'permissions' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
-
-    /**
-     * Indicates that the 'atime' and 'mtime' field are present
-     * (protocol v3).
-     */
-    public static final int SSH_FILEXFER_ATTR_V3_ACMODTIME = 0x00000008;
-
-    /**
-     * Indicates that the 'atime' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008;
-
-    /**
-     * Indicates that the 'createtime' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010;
-
-    /**
-     * Indicates that the 'mtime' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020;
-
-    /**
-     * Indicates that the 'acl' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040;
-
-    /**
-     * Indicates that the 'owner' and 'group' fields are present.
-     */
-    public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080;
-
-    /**
-     * Indicates that additionally to the 'atime', 'createtime',
-     * 'mtime' and 'ctime' fields (if present), there is also
-     * 'atime-nseconds', 'createtime-nseconds',  'mtime-nseconds'
-     * and 'ctime-nseconds'.
-     */
-    public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100;
-
-    /**
-     * Indicates that the 'attrib-bits' and 'attrib-bits-valid'
-     * fields are present.
-     */
-    public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200;
-
-    /**
-     * Indicates that the 'allocation-size' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400;
-
-    /**
-     * Indicates that the 'text-hint' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800;
-
-    /**
-     * Indicates that the 'mime-type' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000;
-
-    /**
-     * Indicates that the 'link-count' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000;
-
-    /**
-     * Indicates that the 'untranslated-name' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000;
-
-    /**
-     * Indicates that the 'ctime' field is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000;
-
-    /**
-     * Indicates that the 'extended-count' field (and probablby some
-     * 'extensions') is present.
-     */
-    public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
-}
--- a/src/com/trilead/ssh2/sftp/AttribPermissions.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * Permissions for the 'permissions' field in the SFTP ATTRS data type.
- * <p>
- * "<i>The 'permissions' field contains a bit mask specifying file permissions.
- * These permissions correspond to the st_mode field of the stat structure
- * defined by POSIX [IEEE.1003-1.1996].</i>"
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AttribPermissions.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class AttribPermissions {
-    /* Octal values! */
-
-    public static final int S_IRUSR = 0400;
-    public static final int S_IWUSR = 0200;
-    public static final int S_IXUSR = 0100;
-    public static final int S_IRGRP = 0040;
-    public static final int S_IWGRP = 0020;
-    public static final int S_IXGRP = 0010;
-    public static final int S_IROTH = 0004;
-    public static final int S_IWOTH = 0002;
-    public static final int S_IXOTH = 0001;
-    public static final int S_ISUID = 04000;
-    public static final int S_ISGID = 02000;
-    public static final int S_ISVTX = 01000;
-}
--- a/src/com/trilead/ssh2/sftp/AttribTypes.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * Types for the 'type' field in the SFTP ATTRS data type.
- * <p>
- * "<i>On a POSIX system, these values would be derived from the mode field
- * of the stat structure.  SPECIAL should be used for files that are of
- * a known type which cannot be expressed in the protocol. UNKNOWN
- * should be used if the type is not known.</i>"
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AttribTypes.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class AttribTypes {
-    public static final int SSH_FILEXFER_TYPE_REGULAR = 1;
-    public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2;
-    public static final int SSH_FILEXFER_TYPE_SYMLINK = 3;
-    public static final int SSH_FILEXFER_TYPE_SPECIAL = 4;
-    public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5;
-    public static final int SSH_FILEXFER_TYPE_SOCKET = 6;
-    public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7;
-    public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8;
-    public static final int SSH_FILEXFER_TYPE_FIFO = 9;
-}
--- a/src/com/trilead/ssh2/sftp/ErrorCodes.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * SFTP Error Codes
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ErrorCodes.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class ErrorCodes {
-    public static final int SSH_FX_OK = 0;
-    public static final int SSH_FX_EOF = 1;
-    public static final int SSH_FX_NO_SUCH_FILE = 2;
-    public static final int SSH_FX_PERMISSION_DENIED = 3;
-    public static final int SSH_FX_FAILURE = 4;
-    public static final int SSH_FX_BAD_MESSAGE = 5;
-    public static final int SSH_FX_NO_CONNECTION = 6;
-    public static final int SSH_FX_CONNECTION_LOST = 7;
-    public static final int SSH_FX_OP_UNSUPPORTED = 8;
-    public static final int SSH_FX_INVALID_HANDLE = 9;
-    public static final int SSH_FX_NO_SUCH_PATH = 10;
-    public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
-    public static final int SSH_FX_WRITE_PROTECT = 12;
-    public static final int SSH_FX_NO_MEDIA = 13;
-    public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14;
-    public static final int SSH_FX_QUOTA_EXCEEDED = 15;
-    public static final int SSH_FX_UNKNOWN_PRINCIPAL = 16;
-    public static final int SSH_FX_LOCK_CONFLICT = 17;
-    public static final int SSH_FX_DIR_NOT_EMPTY = 18;
-    public static final int SSH_FX_NOT_A_DIRECTORY = 19;
-    public static final int SSH_FX_INVALID_FILENAME = 20;
-    public static final int SSH_FX_LINK_LOOP = 21;
-    public static final int SSH_FX_CANNOT_DELETE = 22;
-    public static final int SSH_FX_INVALID_PARAMETER = 23;
-    public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24;
-    public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25;
-    public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26;
-    public static final int SSH_FX_DELETE_PENDING = 27;
-    public static final int SSH_FX_FILE_CORRUPT = 28;
-    public static final int SSH_FX_OWNER_INVALID = 29;
-    public static final int SSH_FX_GROUP_INVALID = 30;
-    public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31;
-
-    private static final String[][] messages = {
-
-        { "SSH_FX_OK", "Indicates successful completion of the operation." },
-        {
-            "SSH_FX_EOF",
-            "An attempt to read past the end-of-file was made; or, there are no more directory entries to return."
-        },
-        { "SSH_FX_NO_SUCH_FILE", "A reference was made to a file which does not exist." },
-        { "SSH_FX_PERMISSION_DENIED", "The user does not have sufficient permissions to perform the operation." },
-        { "SSH_FX_FAILURE", "An error occurred, but no specific error code exists to describe the failure." },
-        { "SSH_FX_BAD_MESSAGE", "A badly formatted packet or other SFTP protocol incompatibility was detected." },
-        { "SSH_FX_NO_CONNECTION", "There is no connection to the server." },
-        { "SSH_FX_CONNECTION_LOST", "The connection to the server was lost." },
-        {
-            "SSH_FX_OP_UNSUPPORTED",
-            "An attempted operation could not be completed by the server because the server does not support the operation."
-        },
-        { "SSH_FX_INVALID_HANDLE", "The handle value was invalid." },
-        { "SSH_FX_NO_SUCH_PATH", "The file path does not exist or is invalid." },
-        { "SSH_FX_FILE_ALREADY_EXISTS", "The file already exists." },
-        { "SSH_FX_WRITE_PROTECT", "The file is on read-only media, or the media is write protected." },
-        {
-            "SSH_FX_NO_MEDIA",
-            "The requested operation cannot be completed because there is no media available in the drive."
-        },
-        {
-            "SSH_FX_NO_SPACE_ON_FILESYSTEM",
-            "The requested operation cannot be completed because there is insufficient free space on the filesystem."
-        },
-        {
-            "SSH_FX_QUOTA_EXCEEDED",
-            "The operation cannot be completed because it would exceed the user's storage quota."
-        },
-        {
-            "SSH_FX_UNKNOWN_PRINCIPAL",
-            "A principal referenced by the request (either the 'owner', 'group', or 'who' field of an ACL), was unknown. The error specific data contains the problematic names."
-        },
-        { "SSH_FX_LOCK_CONFLICT", "The file could not be opened because it is locked by another process." },
-        { "SSH_FX_DIR_NOT_EMPTY", "The directory is not empty." },
-        { "SSH_FX_NOT_A_DIRECTORY", "The specified file is not a directory." },
-        { "SSH_FX_INVALID_FILENAME", "The filename is not valid." },
-        {
-            "SSH_FX_LINK_LOOP",
-            "Too many symbolic links encountered or, an SSH_FXF_NOFOLLOW open encountered a symbolic link as the final component."
-        },
-        {
-            "SSH_FX_CANNOT_DELETE",
-            "The file cannot be deleted. One possible reason is that the advisory READONLY attribute-bit is set."
-        },
-        {
-            "SSH_FX_INVALID_PARAMETER",
-            "One of the parameters was out of range, or the parameters specified cannot be used together."
-        },
-        {
-            "SSH_FX_FILE_IS_A_DIRECTORY",
-            "The specified file was a directory in a context where a directory cannot be used."
-        },
-        {
-            "SSH_FX_BYTE_RANGE_LOCK_CONFLICT",
-            " A read or write operation failed because another process's mandatory byte-range lock overlaps with the request."
-        },
-        { "SSH_FX_BYTE_RANGE_LOCK_REFUSED", "A request for a byte range lock was refused." },
-        { "SSH_FX_DELETE_PENDING", "An operation was attempted on a file for which a delete operation is pending." },
-        { "SSH_FX_FILE_CORRUPT", "The file is corrupt; an filesystem integrity check should be run." },
-        { "SSH_FX_OWNER_INVALID", "The principal specified can not be assigned as an owner of a file." },
-        { "SSH_FX_GROUP_INVALID", "The principal specified can not be assigned as the primary group of a file." },
-        {
-            "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK",
-            "The requested operation could not be completed because the	specifed byte range lock has not been granted."
-        },
-
-    };
-
-    public static final String[] getDescription(int errorCode) {
-        if ((errorCode < 0) || (errorCode >= messages.length))
-            return null;
-
-        return messages[errorCode];
-    }
-}
--- a/src/com/trilead/ssh2/sftp/OpenFlags.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,222 +0,0 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * SFTP Open Flags.
- *
- * The following table is provided to assist in mapping POSIX semantics
- * to equivalent SFTP file open parameters:
- * <p>
- * TODO: This comment should be moved to the open method.
- * <p>
- * <ul>
- * <li>O_RDONLY
- * <ul><li>desired-access = READ_DATA | READ_ATTRIBUTES</li></ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_WRONLY
- * <ul><li>desired-access = WRITE_DATA | WRITE_ATTRIBUTES</li></ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_RDWR
- * <ul><li>desired-access = READ_DATA | READ_ATTRIBUTES | WRITE_DATA | WRITE_ATTRIBUTES</li></ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_APPEND
- * <ul>
- * <li>desired-access = WRITE_DATA | WRITE_ATTRIBUTES | APPEND_DATA</li>
- * <li>flags = SSH_FXF_ACCESS_APPEND_DATA and or SSH_FXF_ACCESS_APPEND_DATA_ATOMIC</li>
- * </ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_CREAT
- * <ul>
- * <li>flags = SSH_FXF_OPEN_OR_CREATE</li>
- * </ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_TRUNC
- * <ul>
- * <li>flags = SSH_FXF_TRUNCATE_EXISTING</li>
- * </ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_TRUNC|O_CREATE
- * <ul>
- * <li>flags = SSH_FXF_CREATE_TRUNCATE</li>
- * </ul>
- * </li>
- * </ul>
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: OpenFlags.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class OpenFlags {
-    /**
-     * Disposition is a 3 bit field that controls how the file is opened.
-     * The server MUST support these bits (possible enumaration values:
-     * SSH_FXF_CREATE_NEW, SSH_FXF_CREATE_TRUNCATE, SSH_FXF_OPEN_EXISTING,
-     * SSH_FXF_OPEN_OR_CREATE or SSH_FXF_TRUNCATE_EXISTING).
-     */
-    public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007;
-
-    /**
-     * A new file is created; if the file already exists, the server
-     * MUST return status SSH_FX_FILE_ALREADY_EXISTS.
-     */
-    public static final int SSH_FXF_CREATE_NEW = 0x00000000;
-
-    /**
-     * A new file is created; if the file already exists, it is opened
-     * and truncated.
-     */
-    public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001;
-
-    /**
-     * An existing file is opened.  If the file does not exist, the
-     * server MUST return SSH_FX_NO_SUCH_FILE. If a directory in the
-     * path does not exist, the server SHOULD return
-     * SSH_FX_NO_SUCH_PATH. It is also acceptable if the server
-     * returns SSH_FX_NO_SUCH_FILE in this case.
-     */
-    public static final int SSH_FXF_OPEN_EXISTING = 0x00000002;
-
-    /**
-     * If the file exists, it is opened. If the file does not exist,
-     * it is created.
-     */
-    public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003;
-
-    /**
-     * An existing file is opened and truncated. If the file does not
-     * exist, the server MUST return the same error codes as defined
-     * for SSH_FXF_OPEN_EXISTING.
-     */
-    public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004;
-
-    /**
-     * Data is always written at the end of the file. The offset field
-     * of the SSH_FXP_WRITE requests are ignored.
-     * <p>
-     * Data is not required to be appended atomically. This means that
-     * if multiple writers attempt to append data simultaneously, data
-     * from the first may be lost. However, data MAY be appended
-     * atomically.
-     */
-    public static final int SSH_FXF_ACCESS_APPEND_DATA = 0x00000008;
-
-    /**
-     * Data is always written at the end of the file. The offset field
-     * of the SSH_FXP_WRITE requests are ignored.
-     * <p>
-     * Data MUST be written atomically so that there is no chance that
-     * multiple appenders can collide and result in data being lost.
-     * <p>
-     * If both append flags are specified, the server SHOULD use atomic
-     * append if it is available, but SHOULD use non-atomic appends
-     * otherwise. The server SHOULD NOT fail the request in this case.
-     */
-    public static final int SSH_FXF_ACCESS_APPEND_DATA_ATOMIC = 0x00000010;
-
-    /**
-     * Indicates that the server should treat the file as text and
-     * convert it to the canonical newline convention in use.
-     * (See Determining Server Newline Convention in section 5.3 in the
-     * SFTP standard draft).
-     * <p>
-     * When a file is opened with this flag, the offset field in the read
-     * and write functions is ignored.
-     * <p>
-     * Servers MUST process multiple, parallel reads and writes correctly
-     * in this mode.  Naturally, it is permissible for them to do this by
-     * serializing the requests.
-     * <p>
-     * Clients SHOULD use the SSH_FXF_ACCESS_APPEND_DATA flag to append
-     * data to a text file rather then using write with a calculated offset.
-     */
-    public static final int SSH_FXF_ACCESS_TEXT_MODE = 0x00000020;
-
-    /**
-     * The server MUST guarantee that no other handle has been opened
-     * with ACE4_READ_DATA access, and that no other handle will be
-     * opened with ACE4_READ_DATA access until the client closes the
-     * handle. (This MUST apply both to other clients and to other
-     * processes on the server.)
-     * <p>
-     * If there is a conflicting lock the server MUST return
-     * SSH_FX_LOCK_CONFLICT.  If the server cannot make the locking
-     * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
-     * <p>
-     * Other handles MAY be opened for ACE4_WRITE_DATA or any other
-     * combination of accesses, as long as ACE4_READ_DATA is not included
-     * in the mask.
-     */
-    public static final int SSH_FXF_ACCESS_BLOCK_READ = 0x00000040;
-
-    /**
-     * The server MUST guarantee that no other handle has been opened
-     * with ACE4_WRITE_DATA or ACE4_APPEND_DATA access, and that no other
-     * handle will be opened with ACE4_WRITE_DATA or ACE4_APPEND_DATA
-     * access until the client closes the handle. (This MUST apply both
-     * to other clients and to other processes on the server.)
-     * <p>
-     * If there is a conflicting lock the server MUST return
-     * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
-     * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
-     * <p>
-     * Other handles MAY be opened for ACE4_READ_DATA or any other
-     * combination of accesses, as long as neither ACE4_WRITE_DATA nor
-     * ACE4_APPEND_DATA are included in the mask.
-     */
-    public static final int SSH_FXF_ACCESS_BLOCK_WRITE = 0x00000080;
-
-    /**
-     * The server MUST guarantee that no other handle has been opened
-     * with ACE4_DELETE access, opened with the
-     * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that no other handle
-     * will be opened with ACE4_DELETE access or with the
-     * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that the file itself
-     * is not deleted in any other way until the client closes the handle.
-     * <p>
-     * If there is a conflicting lock the server MUST return
-     * SSH_FX_LOCK_CONFLICT.  If the server cannot make the locking
-     * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
-     */
-    public static final int SSH_FXF_ACCESS_BLOCK_DELETE = 0x00000100;
-
-    /**
-     * If this bit is set, the above BLOCK modes are advisory. In advisory
-     * mode, only other accesses that specify a BLOCK mode need be
-     * considered when determining whether the BLOCK can be granted,
-     * and the server need not prevent I/O operations that violate the
-     * block mode.
-     * <p>
-     * The server MAY perform mandatory locking even if the BLOCK_ADVISORY
-     * bit is set.
-     */
-    public static final int SSH_FXF_ACCESS_BLOCK_ADVISORY = 0x00000200;
-
-    /**
-     * If the final component of the path is a symlink, then the open
-     * MUST fail, and the error SSH_FX_LINK_LOOP MUST be returned.
-     */
-    public static final int SSH_FXF_ACCESS_NOFOLLOW = 0x00000400;
-
-    /**
-     * The file should be deleted when the last handle to it is closed.
-     * (The last handle may not be an sftp-handle.)  This MAY be emulated
-     * by a server if the OS doesn't support it by deleting the file when
-     * this handle is closed.
-     * <p>
-     * It is implementation specific whether the directory entry is
-     * removed immediately or when the handle is closed.
-     */
-    public static final int SSH_FXF_ACCESS_DELETE_ON_CLOSE = 0x00000800;
-}
--- a/src/com/trilead/ssh2/sftp/Packet.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * SFTP Paket Types
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Packet.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class Packet {
-    public static final int SSH_FXP_INIT = 1;
-    public static final int SSH_FXP_VERSION = 2;
-    public static final int SSH_FXP_OPEN = 3;
-    public static final int SSH_FXP_CLOSE = 4;
-    public static final int SSH_FXP_READ = 5;
-    public static final int SSH_FXP_WRITE = 6;
-    public static final int SSH_FXP_LSTAT = 7;
-    public static final int SSH_FXP_FSTAT = 8;
-    public static final int SSH_FXP_SETSTAT = 9;
-    public static final int SSH_FXP_FSETSTAT = 10;
-    public static final int SSH_FXP_OPENDIR = 11;
-    public static final int SSH_FXP_READDIR = 12;
-    public static final int SSH_FXP_REMOVE = 13;
-    public static final int SSH_FXP_MKDIR = 14;
-    public static final int SSH_FXP_RMDIR = 15;
-    public static final int SSH_FXP_REALPATH = 16;
-    public static final int SSH_FXP_STAT = 17;
-    public static final int SSH_FXP_RENAME = 18;
-    public static final int SSH_FXP_READLINK = 19;
-    public static final int SSH_FXP_SYMLINK = 20;
-
-    public static final int SSH_FXP_STATUS = 101;
-    public static final int SSH_FXP_HANDLE = 102;
-    public static final int SSH_FXP_DATA = 103;
-    public static final int SSH_FXP_NAME = 104;
-    public static final int SSH_FXP_ATTRS = 105;
-
-    public static final int SSH_FXP_EXTENDED = 200;
-    public static final int SSH_FXP_EXTENDED_REPLY = 201;
-}
--- a/src/com/trilead/ssh2/signature/DSASHA1Verify.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,230 +0,0 @@
-
-package com.trilead.ssh2.signature;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-
-
-/**
- * DSASHA1Verify.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DSASHA1Verify.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class DSASHA1Verify {
-    private static final Logger log = Logger.getLogger(DSASHA1Verify.class);
-
-    public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException {
-        TypesReader tr = new TypesReader(key);
-        String key_format = tr.readString();
-
-        if (key_format.equals("ssh-dss") == false)
-            throw new IllegalArgumentException("This is not a ssh-dss public key!");
-
-        BigInteger p = tr.readMPINT();
-        BigInteger q = tr.readMPINT();
-        BigInteger g = tr.readMPINT();
-        BigInteger y = tr.readMPINT();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in DSA public key!");
-
-        try {
-            KeyFactory kf = KeyFactory.getInstance("DSA");
-            KeySpec ks = new DSAPublicKeySpec(y, p, q, g);
-            return (DSAPublicKey) kf.generatePublic(ks);
-        }
-        catch (NoSuchAlgorithmException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (InvalidKeySpecException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-    }
-
-    public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk) throws IOException {
-        TypesWriter tw = new TypesWriter();
-        tw.writeString("ssh-dss");
-        DSAParams params = pk.getParams();
-        tw.writeMPInt(params.getP());
-        tw.writeMPInt(params.getQ());
-        tw.writeMPInt(params.getG());
-        tw.writeMPInt(pk.getY());
-        return tw.getBytes();
-    }
-
-    /**
-     * Convert from Java's signature ASN.1 encoding to the SSH spec.
-     * <p>
-     * Java ASN.1 encoding:
-     * <pre>
-     * SEQUENCE ::= {
-     *    r INTEGER,
-     *    s INTEGER
-     * }
-     * </pre>
-     */
-    public static byte[] encodeSSHDSASignature(byte[] ds) {
-        TypesWriter tw = new TypesWriter();
-        tw.writeString("ssh-dss");
-        int len, index;
-        index = 3;
-        len = ds[index++] & 0xff;
-        byte[] r = new byte[len];
-        System.arraycopy(ds, index, r, 0, r.length);
-        index = index + len + 1;
-        len = ds[index++] & 0xff;
-        byte[] s = new byte[len];
-        System.arraycopy(ds, index, s, 0, s.length);
-        byte[] a40 = new byte[40];
-        /* Patch (unsigned) r and s into the target array. */
-        int r_copylen = (r.length < 20) ? r.length : 20;
-        int s_copylen = (s.length < 20) ? s.length : 20;
-        System.arraycopy(r, r.length - r_copylen, a40, 20 - r_copylen, r_copylen);
-        System.arraycopy(s, s.length - s_copylen, a40, 40 - s_copylen, s_copylen);
-        tw.writeString(a40, 0, 40);
-        return tw.getBytes();
-    }
-
-    public static byte[] decodeSSHDSASignature(byte[] sig) throws IOException {
-        byte[] rsArray = null;
-
-        if (sig.length == 40) {
-            /* OK, another broken SSH server. */
-            rsArray = sig;
-        }
-        else {
-            /* Hopefully a server obeying the standard... */
-            TypesReader tr = new TypesReader(sig);
-            String sig_format = tr.readString();
-
-            if (sig_format.equals("ssh-dss") == false)
-                throw new IOException("Peer sent wrong signature format");
-
-            rsArray = tr.readByteString();
-
-            if (rsArray.length != 40)
-                throw new IOException("Peer sent corrupt signature");
-
-            if (tr.remain() != 0)
-                throw new IOException("Padding in DSA signature!");
-        }
-
-        int i = 0;
-        int j = 0;
-        byte[] tmp;
-
-        if (rsArray[0] == 0 && rsArray[1] == 0 && rsArray[2] == 0) {
-            j = ((rsArray[i++] << 24) & 0xff000000) | ((rsArray[i++] << 16) & 0x00ff0000)
-                | ((rsArray[i++] << 8) & 0x0000ff00) | ((rsArray[i++]) & 0x000000ff);
-            i += j;
-            j = ((rsArray[i++] << 24) & 0xff000000) | ((rsArray[i++] << 16) & 0x00ff0000)
-                | ((rsArray[i++] << 8) & 0x0000ff00) | ((rsArray[i++]) & 0x000000ff);
-            tmp = new byte[j];
-            System.arraycopy(rsArray, i, tmp, 0, j);
-            rsArray = tmp;
-        }
-
-        /* ASN.1 */
-        int frst = ((rsArray[0] & 0x80) != 0 ? 1 : 0);
-        int scnd = ((rsArray[20] & 0x80) != 0 ? 1 : 0);
-        /* Calculate output length */
-        int length = rsArray.length + 6 + frst + scnd;
-        tmp = new byte[length];
-        /* DER-encoding to match Java */
-        tmp[0] = (byte) 0x30;
-
-        if (rsArray.length != 40)
-            throw new IOException("Peer sent corrupt signature");
-
-        /* Calculate length */
-        tmp[1] = (byte) 0x2c;
-        tmp[1] += frst;
-        tmp[1] += scnd;
-        /* First item */
-        tmp[2] = (byte) 0x02;
-        /* First item length */
-        tmp[3] = (byte) 0x14;
-        tmp[3] += frst;
-        /* Copy in the data for first item */
-        System.arraycopy(rsArray, 0, tmp, 4 + frst, 20);
-        /* Second item */
-        tmp[4 + tmp[3]] = (byte) 0x02;
-        /* Second item length */
-        tmp[5 + tmp[3]] = (byte) 0x14;
-        tmp[5 + tmp[3]] += scnd;
-        /* Copy in the data for the second item */
-        System.arraycopy(rsArray, 20, tmp, 6 + tmp[3] + scnd, 20);
-        /* Swap buffers */
-        rsArray = tmp;
-        return rsArray;
-    }
-
-    public static boolean verifySignature(byte[] message, byte[] ds, DSAPublicKey dpk) throws IOException {
-        try {
-            Signature s = Signature.getInstance("SHA1withDSA");
-            s.initVerify(dpk);
-            s.update(message);
-            return s.verify(ds);
-        }
-        catch (NoSuchAlgorithmException e) {
-            IOException ex = new IOException("No such algorithm");
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (InvalidKeyException e) {
-            IOException ex = new IOException("No such algorithm");
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (SignatureException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-    }
-
-    public static byte[] generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd) throws IOException {
-        try {
-            Signature s = Signature.getInstance("SHA1withDSA");
-            s.initSign(pk);
-            s.update(message);
-            return s.sign();
-        }
-        catch (NoSuchAlgorithmException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (InvalidKeyException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (SignatureException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-    }
-}
--- a/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,488 +0,0 @@
-/**
- *
- */
-package com.trilead.ssh2.signature;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECFieldFp;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.EllipticCurve;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-import java.util.Map;
-import java.util.TreeMap;
-
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-
-/**
- * @author Kenny Root
- *
- */
-public class ECDSASHA2Verify {
-    private static final Logger log = Logger.getLogger(ECDSASHA2Verify.class);
-
-    public static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-";
-
-    private static final String NISTP256 = "nistp256";
-    private static final String NISTP256_OID = "1.2.840.10045.3.1.7";
-    private static final String NISTP384 = "nistp384";
-    private static final String NISTP384_OID = "1.3.132.0.34";
-    private static final String NISTP521 = "nistp521";
-    private static final String NISTP521_OID = "1.3.132.0.35";
-
-    private static final Map<String, ECParameterSpec> CURVES = new TreeMap<String, ECParameterSpec>();
-    static {
-        CURVES.put(NISTP256, EllipticCurves.nistp256);
-        CURVES.put(NISTP384, EllipticCurves.nistp384);
-        CURVES.put(NISTP521, EllipticCurves.nistp521);
-    }
-
-    private static final Map<Integer, String> CURVE_SIZES = new TreeMap<Integer, String>();
-    static {
-        CURVE_SIZES.put(256, NISTP256);
-        CURVE_SIZES.put(384, NISTP384);
-        CURVE_SIZES.put(521, NISTP521);
-    }
-
-    private static final Map<String, String> CURVE_OIDS = new TreeMap<String, String>();
-    static {
-        CURVE_OIDS.put(NISTP256_OID, NISTP256);
-        CURVE_OIDS.put(NISTP384_OID, NISTP256);
-        CURVE_OIDS.put(NISTP521_OID, NISTP256);
-    }
-
-    public static int[] getCurveSizes() {
-        int[] keys = new int[CURVE_SIZES.size()];
-        int i = 0;
-
-        for (Integer n : CURVE_SIZES.keySet().toArray(new Integer[keys.length])) {
-            keys[i++] = n;
-        }
-
-        return keys;
-    }
-
-    public static ECParameterSpec getCurveForSize(int size) {
-        final String name = CURVE_SIZES.get(size);
-
-        if (name == null) {
-            return null;
-        }
-
-        return CURVES.get(name);
-    }
-
-    public static ECPublicKey decodeSSHECDSAPublicKey(byte[] key) throws IOException {
-        TypesReader tr = new TypesReader(key);
-        String key_format = tr.readString();
-
-        if (key_format.startsWith(ECDSA_SHA2_PREFIX) == false)
-            throw new IllegalArgumentException("This is not an ECDSA public key");
-
-        String curveName = tr.readString();
-        byte[] groupBytes = tr.readByteString();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in ECDSA public key!");
-
-        if (key_format.equals(ECDSA_SHA2_PREFIX + curveName) == false) {
-            throw new IOException("Key format is inconsistent with curve name: " + key_format
-                                  + " != " + curveName);
-        }
-
-        ECParameterSpec params = CURVES.get(curveName);
-
-        if (params == null) {
-            throw new IOException("Curve is not supported: " + curveName);
-        }
-
-        ECPoint group = ECDSASHA2Verify.decodeECPoint(groupBytes, params.getCurve());
-
-        if (group == null) {
-            throw new IOException("Invalid ECDSA group");
-        }
-
-        KeySpec keySpec = new ECPublicKeySpec(group, params);
-
-        try {
-            KeyFactory kf = KeyFactory.getInstance("EC");
-            return (ECPublicKey) kf.generatePublic(keySpec);
-        }
-        catch (NoSuchAlgorithmException nsae) {
-            IOException ioe = new IOException("No EC KeyFactory available");
-            ioe.initCause(nsae);
-            throw ioe;
-        }
-        catch (InvalidKeySpecException ikse) {
-            IOException ioe = new IOException("No EC KeyFactory available");
-            ioe.initCause(ikse);
-            throw ioe;
-        }
-    }
-
-    public static byte[] encodeSSHECDSAPublicKey(ECPublicKey key) throws IOException {
-        TypesWriter tw = new TypesWriter();
-        String curveName = getCurveName(key.getParams());
-        String keyFormat = ECDSA_SHA2_PREFIX + curveName;
-        tw.writeString(keyFormat);
-        tw.writeString(curveName);
-        byte[] encoded = encodeECPoint(key.getW(), key.getParams().getCurve());
-        tw.writeString(encoded, 0, encoded.length);
-        return tw.getBytes();
-    }
-
-    public static String getCurveName(ECParameterSpec params) throws IOException {
-        int fieldSize = getCurveSize(params);
-        final String curveName = getCurveName(fieldSize);
-
-        if (curveName == null) {
-            throw new IOException("invalid curve size " + fieldSize);
-        }
-
-        return curveName;
-    }
-
-    public static String getCurveName(int fieldSize) {
-        String curveName = CURVE_SIZES.get(fieldSize);
-
-        if (curveName == null) {
-            return null;
-        }
-
-        return curveName;
-    }
-
-    public static int getCurveSize(ECParameterSpec params) {
-        return params.getCurve().getField().getFieldSize();
-    }
-
-    public static ECParameterSpec getCurveForOID(String oid) {
-        String name = CURVE_OIDS.get(oid);
-
-        if (name == null)
-            return null;
-
-        return CURVES.get(name);
-    }
-
-    public static byte[] decodeSSHECDSASignature(byte[] sig) throws IOException {
-        byte[] rsArray = null;
-        TypesReader tr = new TypesReader(sig);
-        String sig_format = tr.readString();
-
-        if (sig_format.startsWith(ECDSA_SHA2_PREFIX) == false)
-            throw new IOException("Peer sent wrong signature format");
-
-        String curveName = sig_format.substring(ECDSA_SHA2_PREFIX.length());
-
-        if (CURVES.containsKey(curveName) == false) {
-            throw new IOException("Unsupported curve: " + curveName);
-        }
-
-        rsArray = tr.readByteString();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in ECDSA signature!");
-
-        byte[] rArray;
-        byte[] sArray;
-        {
-            TypesReader rsReader = new TypesReader(rsArray);
-            rArray = rsReader.readMPINT().toByteArray();
-            sArray = rsReader.readMPINT().toByteArray();
-        }
-        int first = rArray.length;
-        int second = sArray.length;
-
-        /* We can't have the high bit set, so add an extra zero at the beginning if so. */
-        if ((rArray[0] & 0x80) != 0) {
-            first++;
-        }
-
-        if ((sArray[0] & 0x80) != 0) {
-            second++;
-        }
-
-        /* Calculate total output length */
-        ByteArrayOutputStream os = new ByteArrayOutputStream(6 + first + second);
-        /* ASN.1 SEQUENCE tag */
-        os.write(0x30);
-        /* Size of SEQUENCE */
-        writeLength(4 + first + second, os);
-        /* ASN.1 INTEGER tag */
-        os.write(0x02);
-        /* "r" INTEGER length */
-        writeLength(first, os);
-
-        /* Copy in the "r" INTEGER */
-        if (first != rArray.length) {
-            os.write(0x00);
-        }
-
-        os.write(rArray);
-        /* ASN.1 INTEGER tag */
-        os.write(0x02);
-        /* "s" INTEGER length */
-        writeLength(second, os);
-
-        /* Copy in the "s" INTEGER */
-        if (second != sArray.length) {
-            os.write(0x00);
-        }
-
-        os.write(sArray);
-        return os.toByteArray();
-    }
-
-    private static final void writeLength(int length, OutputStream os) throws IOException {
-        if (length <= 0x7F) {
-            os.write(length);
-            return;
-        }
-
-        int numOctets = 0;
-        int lenCopy = length;
-
-        while (lenCopy != 0) {
-            lenCopy >>>= 8;
-            numOctets++;
-        }
-
-        os.write(0x80 | numOctets);
-
-        for (int i = (numOctets - 1) * 8; i >= 0; i -= 8) {
-            os.write((byte)(length >> i));
-        }
-    }
-
-    public static byte[] encodeSSHECDSASignature(byte[] sig, ECParameterSpec params) throws IOException {
-        TypesWriter tw = new TypesWriter();
-        String curveName = getCurveName(params);
-        tw.writeString(ECDSA_SHA2_PREFIX + curveName);
-
-        if ((sig[0] != 0x30) || (sig[1] != sig.length - 2) || (sig[2] != 0x02)) {
-            throw new IOException("Invalid signature format");
-        }
-
-        int rLength = sig[3];
-
-        if ((rLength + 6 > sig.length) || (sig[4 + rLength] != 0x02)) {
-            throw new IOException("Invalid signature format");
-        }
-
-        int sLength = sig[5 + rLength];
-
-        if (6 + rLength + sLength > sig.length) {
-            throw new IOException("Invalid signature format");
-        }
-
-        byte[] rArray = new byte[rLength];
-        byte[] sArray = new byte[sLength];
-        System.arraycopy(sig, 4, rArray, 0, rLength);
-        System.arraycopy(sig, 6 + rLength, sArray, 0, sLength);
-        BigInteger r = new BigInteger(rArray);
-        BigInteger s = new BigInteger(sArray);
-        // Write the <r,s> to its own types writer.
-        TypesWriter rsWriter = new TypesWriter();
-        rsWriter.writeMPInt(r);
-        rsWriter.writeMPInt(s);
-        byte[] encoded = rsWriter.getBytes();
-        tw.writeString(encoded, 0, encoded.length);
-        return tw.getBytes();
-    }
-
-    public static byte[] generateSignature(byte[] message, ECPrivateKey pk) throws IOException {
-        final String algo = getSignatureAlgorithmForParams(pk.getParams());
-
-        try {
-            Signature s = Signature.getInstance(algo);
-            s.initSign(pk);
-            s.update(message);
-            return s.sign();
-        }
-        catch (NoSuchAlgorithmException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (InvalidKeyException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (SignatureException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-    }
-
-    public static boolean verifySignature(byte[] message, byte[] ds, ECPublicKey dpk) throws IOException {
-        final String algo = getSignatureAlgorithmForParams(dpk.getParams());
-
-        try {
-            Signature s = Signature.getInstance(algo);
-            s.initVerify(dpk);
-            s.update(message);
-            return s.verify(ds);
-        }
-        catch (NoSuchAlgorithmException e) {
-            IOException ex = new IOException("No such algorithm");
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (InvalidKeyException e) {
-            IOException ex = new IOException("No such algorithm");
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (SignatureException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-    }
-
-    private static String getSignatureAlgorithmForParams(ECParameterSpec params) {
-        int size = getCurveSize(params);
-
-        if (size <= 256) {
-            return "SHA256withECDSA";
-        }
-        else if (size <= 384) {
-            return "SHA384withECDSA";
-        }
-        else {
-            return "SHA512withECDSA";
-        }
-    }
-
-    public static String getDigestAlgorithmForParams(ECParameterSpec params) {
-        int size = getCurveSize(params);
-
-        if (size <= 256) {
-            return "SHA256";
-        }
-        else if (size <= 384) {
-            return "SHA384";
-        }
-        else {
-            return "SHA512";
-        }
-    }
-
-    /**
-     * Decode an OctetString to EllipticCurvePoint according to SECG 2.3.4
-     */
-    public static ECPoint decodeECPoint(byte[] M, EllipticCurve curve) {
-        if (M.length == 0) {
-            return null;
-        }
-
-        // M has len 2 ceil(log_2(q)/8) + 1 ?
-        int elementSize = (curve.getField().getFieldSize() + 7) / 8;
-
-        if (M.length != 2 * elementSize + 1) {
-            return null;
-        }
-
-        // step 3.2
-        if (M[0] != 0x04) {
-            return null;
-        }
-
-        // Step 3.3
-        byte[] xp = new byte[elementSize];
-        System.arraycopy(M, 1, xp, 0, elementSize);
-        // Step 3.4
-        byte[] yp = new byte[elementSize];
-        System.arraycopy(M, 1 + elementSize, yp, 0, elementSize);
-        ECPoint P = new ECPoint(new BigInteger(1, xp), new BigInteger(1, yp));
-        // TODO check point 3.5
-        // Step 3.6
-        return P;
-    }
-
-    /**
-     * Encode EllipticCurvePoint to an OctetString
-     */
-    public static byte[] encodeECPoint(ECPoint group, EllipticCurve curve) {
-        // M has len 2 ceil(log_2(q)/8) + 1 ?
-        int elementSize = (curve.getField().getFieldSize() + 7) / 8;
-        byte[] M = new byte[2 * elementSize + 1];
-        // Uncompressed format
-        M[0] = 0x04;
-        {
-            byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray());
-            System.arraycopy(affineX, 0, M, 1 + elementSize - affineX.length, affineX.length);
-        }
-        {
-            byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray());
-            System.arraycopy(affineY, 0, M, 1 + elementSize + elementSize - affineY.length,
-                             affineY.length);
-        }
-        return M;
-    }
-
-    private static byte[] removeLeadingZeroes(byte[] input) {
-        if (input[0] != 0x00) {
-            return input;
-        }
-
-        int pos = 1;
-
-        while (pos < input.length - 1 && input[pos] == 0x00) {
-            pos++;
-        }
-
-        byte[] output = new byte[input.length - pos];
-        System.arraycopy(input, pos, output, 0, output.length);
-        return output;
-    }
-
-    public static class EllipticCurves {
-        public static ECParameterSpec nistp256 = new ECParameterSpec(
-            new EllipticCurve(
-                new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
-                new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16),
-                new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)),
-            new ECPoint(new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
-                        new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
-            new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
-            1);
-
-        public static ECParameterSpec nistp384 = new ECParameterSpec(
-            new EllipticCurve(
-                new ECFieldFp(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16)),
-                new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16),
-                new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16)),
-            new ECPoint(new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16),
-                        new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16)),
-            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
-            1);
-
-        public static ECParameterSpec nistp521 = new ECParameterSpec(
-            new EllipticCurve(
-                new ECFieldFp(new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)),
-                new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),
-                new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16)),
-            new ECPoint(new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", 16),
-                        new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", 16)),
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16),
-            1);
-    }
-}
--- a/src/com/trilead/ssh2/signature/RSASHA1Verify.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-
-package com.trilead.ssh2.signature;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-import java.security.spec.RSAPublicKeySpec;
-
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-
-
-/**
- * RSASHA1Verify.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RSASHA1Verify.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class RSASHA1Verify {
-    private static final Logger log = Logger.getLogger(RSASHA1Verify.class);
-
-    public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException {
-        TypesReader tr = new TypesReader(key);
-        String key_format = tr.readString();
-
-        if (key_format.equals("ssh-rsa") == false)
-            throw new IllegalArgumentException("This is not a ssh-rsa public key");
-
-        BigInteger e = tr.readMPINT();
-        BigInteger n = tr.readMPINT();
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in RSA public key!");
-
-        KeySpec keySpec = new RSAPublicKeySpec(n, e);
-
-        try {
-            KeyFactory kf = KeyFactory.getInstance("RSA");
-            return (RSAPublicKey) kf.generatePublic(keySpec);
-        }
-        catch (NoSuchAlgorithmException nsae) {
-            IOException ioe = new IOException("No RSA KeyFactory available");
-            ioe.initCause(nsae);
-            throw ioe;
-        }
-        catch (InvalidKeySpecException ikse) {
-            IOException ioe = new IOException("No RSA KeyFactory available");
-            ioe.initCause(ikse);
-            throw ioe;
-        }
-    }
-
-    public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException {
-        TypesWriter tw = new TypesWriter();
-        tw.writeString("ssh-rsa");
-        tw.writeMPInt(pk.getPublicExponent());
-        tw.writeMPInt(pk.getModulus());
-        return tw.getBytes();
-    }
-
-    public static byte[] decodeSSHRSASignature(byte[] sig) throws IOException {
-        TypesReader tr = new TypesReader(sig);
-        String sig_format = tr.readString();
-
-        if (sig_format.equals("ssh-rsa") == false)
-            throw new IOException("Peer sent wrong signature format");
-
-        /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
-         * containing s (which is an integer, without lengths or padding, unsigned and in
-         * network byte order)." See also below.
-         */
-        byte[] s = tr.readByteString();
-
-        if (s.length == 0)
-            throw new IOException("Error in RSA signature, S is empty.");
-
-        if (log.isEnabled()) {
-            log.log(80, "Decoding ssh-rsa signature string (length: " + s.length + ")");
-        }
-
-        if (tr.remain() != 0)
-            throw new IOException("Padding in RSA signature!");
-
-        if (s[0] == 0 && s[1] == 0 && s[2] == 0) {
-            int i = 0;
-            int j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000)
-                    | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff);
-            i += j;
-            j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000)
-                | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff);
-            byte[] tmp = new byte[j];
-            System.arraycopy(s, i, tmp, 0, j);
-            sig = tmp;
-        }
-
-        return s;
-    }
-
-    public static byte[] encodeSSHRSASignature(byte[] s) throws IOException {
-        TypesWriter tw = new TypesWriter();
-        tw.writeString("ssh-rsa");
-
-        /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
-         * containing s (which is an integer, without lengths or padding, unsigned and in
-         * network byte order)."
-         */
-
-        /* Remove first zero sign byte, if present */
-
-        if ((s.length > 1) && (s[0] == 0x00))
-            tw.writeString(s, 1, s.length - 1);
-        else
-            tw.writeString(s, 0, s.length);
-
-        return tw.getBytes();
-    }
-
-    public static byte[] generateSignature(byte[] message, RSAPrivateKey pk) throws IOException {
-        try {
-            Signature s = Signature.getInstance("SHA1withRSA");
-            s.initSign(pk);
-            s.update(message);
-            return s.sign();
-        }
-        catch (NoSuchAlgorithmException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (InvalidKeyException e) {
-            IOException ex =  new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (SignatureException e) {
-            IOException ex =  new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-    }
-
-    public static boolean verifySignature(byte[] message, byte[] ds, RSAPublicKey dpk) throws IOException {
-        try {
-            Signature s = Signature.getInstance("SHA1withRSA");
-            s.initVerify(dpk);
-            s.update(message);
-            return s.verify(ds);
-        }
-        catch (NoSuchAlgorithmException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (InvalidKeyException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-        catch (SignatureException e) {
-            IOException ex = new IOException();
-            ex.initCause(e);
-            throw ex;
-        }
-    }
-}
--- a/src/com/trilead/ssh2/transport/ClientServerHello.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-
-import com.trilead.ssh2.Connection;
-
-/**
- * ClientServerHello.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ClientServerHello.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class ClientServerHello {
-    String server_line;
-    String client_line;
-
-    String server_versioncomment;
-
-    public final static int readLineRN(InputStream is, byte[] buffer) throws IOException {
-        int pos = 0;
-        boolean need10 = false;
-        int len = 0;
-
-        while (true) {
-            int c = is.read();
-
-            if (c == -1)
-                throw new IOException("Premature connection close");
-
-            buffer[pos++] = (byte) c;
-
-            if (c == 13) {
-                need10 = true;
-                continue;
-            }
-
-            if (c == 10)
-                break;
-
-            if (need10 == true)
-                throw new IOException("Malformed line sent by the server, the line does not end correctly.");
-
-            len++;
-
-            if (pos >= buffer.length)
-                throw new IOException("The server sent a too long line.");
-        }
-
-        return len;
-    }
-
-    public ClientServerHello(InputStream bi, OutputStream bo) throws IOException {
-        client_line = "SSH-2.0-" + Connection.identification;
-        bo.write((client_line + "\r\n").getBytes("ISO-8859-1"));
-        bo.flush();
-        byte[] serverVersion = new byte[512];
-
-        for (int i = 0; i < 50; i++) {
-            int len = readLineRN(bi, serverVersion);
-            server_line = new String(serverVersion, 0, len, "ISO-8859-1");
-
-            if (server_line.startsWith("SSH-"))
-                break;
-        }
-
-        if (server_line.startsWith("SSH-") == false)
-            throw new IOException(
-                "Malformed server identification string. There was no line starting with 'SSH-' amongst the first 50 lines.");
-
-        if (server_line.startsWith("SSH-1.99-"))
-            server_versioncomment = server_line.substring(9);
-        else if (server_line.startsWith("SSH-2.0-"))
-            server_versioncomment = server_line.substring(8);
-        else
-            throw new IOException("Server uses incompatible protocol, it is not SSH-2 compatible.");
-    }
-
-    /**
-     * @return Returns the client_versioncomment.
-     */
-    public byte[] getClientString() {
-        byte[] result;
-
-        try {
-            result = client_line.getBytes("ISO-8859-1");
-        }
-        catch (UnsupportedEncodingException ign) {
-            result = client_line.getBytes();
-        }
-
-        return result;
-    }
-
-    /**
-     * @return Returns the server_versioncomment.
-     */
-    public byte[] getServerString() {
-        byte[] result;
-
-        try {
-            result = server_line.getBytes("ISO-8859-1");
-        }
-        catch (UnsupportedEncodingException ign) {
-            result = server_line.getBytes();
-        }
-
-        return result;
-    }
-}
--- a/src/com/trilead/ssh2/transport/KexManager.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,568 +0,0 @@
-
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-import java.security.SecureRandom;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPublicKey;
-import java.util.Set;
-import java.util.TreeSet;
-
-import com.trilead.ssh2.ConnectionInfo;
-import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.ServerHostKeyVerifier;
-import com.trilead.ssh2.compression.CompressionFactory;
-import com.trilead.ssh2.compression.ICompressor;
-import com.trilead.ssh2.crypto.CryptoWishList;
-import com.trilead.ssh2.crypto.KeyMaterial;
-import com.trilead.ssh2.crypto.cipher.BlockCipher;
-import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
-import com.trilead.ssh2.crypto.dh.DhGroupExchange;
-import com.trilead.ssh2.crypto.dh.GenericDhExchange;
-import com.trilead.ssh2.crypto.digest.MAC;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.PacketKexDHInit;
-import com.trilead.ssh2.packets.PacketKexDHReply;
-import com.trilead.ssh2.packets.PacketKexDhGexGroup;
-import com.trilead.ssh2.packets.PacketKexDhGexInit;
-import com.trilead.ssh2.packets.PacketKexDhGexReply;
-import com.trilead.ssh2.packets.PacketKexDhGexRequest;
-import com.trilead.ssh2.packets.PacketKexDhGexRequestOld;
-import com.trilead.ssh2.packets.PacketKexInit;
-import com.trilead.ssh2.packets.PacketNewKeys;
-import com.trilead.ssh2.packets.Packets;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
-import com.trilead.ssh2.signature.RSASHA1Verify;
-
-
-/**
- * KexManager.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class KexManager {
-    private static final Logger log = Logger.getLogger(KexManager.class);
-
-    private static final Set<String> HOSTKEY_ALGS = new TreeSet<String>();
-    static {
-        HOSTKEY_ALGS.add("ecdsa-sha2-nistp256");
-        HOSTKEY_ALGS.add("ecdsa-sha2-nistp384");
-        HOSTKEY_ALGS.add("ecdsa-sha2-nistp521");
-        HOSTKEY_ALGS.add("ssh-rsa");
-        HOSTKEY_ALGS.add("ssh-dsa");
-    }
-
-    private static final Set<String> KEX_ALGS = new TreeSet<String>();
-    static {
-        KEX_ALGS.add("ecdh-sha2-nistp256");
-        KEX_ALGS.add("ecdh-sha2-nistp384");
-        KEX_ALGS.add("ecdh-sha2-nistp521");
-        KEX_ALGS.add("diffie-hellman-group-exchange-sha256");
-        KEX_ALGS.add("diffie-hellman-group-exchange-sha1");
-        KEX_ALGS.add("diffie-hellman-group14-sha1");
-        KEX_ALGS.add("diffie-hellman-group1-sha1");
-    }
-
-    KexState kxs;
-    int kexCount = 0;
-    KeyMaterial km;
-    byte[] sessionId;
-    ClientServerHello csh;
-
-    final Object accessLock = new Object();
-    ConnectionInfo lastConnInfo = null;
-
-    boolean connectionClosed = false;
-
-    boolean ignore_next_kex_packet = false;
-
-    final TransportManager tm;
-
-    CryptoWishList nextKEXcryptoWishList;
-    DHGexParameters nextKEXdhgexParameters;
-
-    ServerHostKeyVerifier verifier;
-    final String hostname;
-    final int port;
-    final SecureRandom rnd;
-
-    public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
-                      ServerHostKeyVerifier keyVerifier, SecureRandom rnd) {
-        this.tm = tm;
-        this.csh = csh;
-        this.nextKEXcryptoWishList = initialCwl;
-        this.nextKEXdhgexParameters = new DHGexParameters();
-        this.hostname = hostname;
-        this.port = port;
-        this.verifier = keyVerifier;
-        this.rnd = rnd;
-    }
-
-    public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException {
-        synchronized (accessLock) {
-            while (true) {
-                if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount))
-                    return lastConnInfo;
-
-                if (connectionClosed)
-                    throw(IOException) new IOException("Key exchange was not finished, connection is closed.")
-                    .initCause(tm.getReasonClosedCause());
-
-                try {
-                    accessLock.wait();
-                }
-                catch (InterruptedException e) {
-                }
-            }
-        }
-    }
-
-    private String getFirstMatch(String[] client, String[] server) throws NegotiateException {
-        if (client == null || server == null)
-            throw new IllegalArgumentException();
-
-        if (client.length == 0)
-            return null;
-
-        for (int i = 0; i < client.length; i++) {
-            for (int j = 0; j < server.length; j++) {
-                if (client[i].equals(server[j]))
-                    return client[i];
-            }
-        }
-
-        throw new NegotiateException();
-    }
-
-    private boolean compareFirstOfNameList(String[] a, String[] b) {
-        if (a == null || b == null)
-            throw new IllegalArgumentException();
-
-        if ((a.length == 0) && (b.length == 0))
-            return true;
-
-        if ((a.length == 0) || (b.length == 0))
-            return false;
-
-        return (a[0].equals(b[0]));
-    }
-
-    private boolean isGuessOK(KexParameters cpar, KexParameters spar) {
-        if (cpar == null || spar == null)
-            throw new IllegalArgumentException();
-
-        if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false) {
-            return false;
-        }
-
-        if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false) {
-            return false;
-        }
-
-        /*
-         * We do NOT check here if the other algorithms can be agreed on, this
-         * is just a check if kex_algorithms and server_host_key_algorithms were
-         * guessed right!
-         */
-        return true;
-    }
-
-    private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server) {
-        NegotiatedParameters np = new NegotiatedParameters();
-
-        try {
-            np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);
-            log.log(20, "kex_algo=" + np.kex_algo);
-            np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
-                                                    server.server_host_key_algorithms);
-            log.log(20, "server_host_key_algo=" + np.server_host_key_algo);
-            np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
-                                           server.encryption_algorithms_client_to_server);
-            np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
-                                           server.encryption_algorithms_server_to_client);
-            log.log(20, "enc_algo_client_to_server=" + np.enc_algo_client_to_server);
-            log.log(20, "enc_algo_server_to_client=" + np.enc_algo_server_to_client);
-            np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
-                                           server.mac_algorithms_client_to_server);
-            np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
-                                           server.mac_algorithms_server_to_client);
-            log.log(20, "mac_algo_client_to_server=" + np.mac_algo_client_to_server);
-            log.log(20, "mac_algo_server_to_client=" + np.mac_algo_server_to_client);
-            np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
-                                            server.compression_algorithms_client_to_server);
-            np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
-                                            server.compression_algorithms_server_to_client);
-            log.log(20, "comp_algo_client_to_server=" + np.comp_algo_client_to_server);
-            log.log(20, "comp_algo_server_to_client=" + np.comp_algo_server_to_client);
-        }
-        catch (NegotiateException e) {
-            return null;
-        }
-
-        try {
-            np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
-                                       server.languages_client_to_server);
-        }
-        catch (NegotiateException e1) {
-            np.lang_client_to_server = null;
-        }
-
-        try {
-            np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
-                                       server.languages_server_to_client);
-        }
-        catch (NegotiateException e2) {
-            np.lang_server_to_client = null;
-        }
-
-        if (isGuessOK(client, server))
-            np.guessOK = true;
-
-        return np;
-    }
-
-    public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException {
-        nextKEXcryptoWishList = cwl;
-        nextKEXdhgexParameters = dhgex;
-
-        if (kxs == null) {
-            kxs = new KexState();
-            kxs.dhgexParameters = nextKEXdhgexParameters;
-            PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList);
-            kxs.localKEX = kp;
-            tm.sendKexMessage(kp.getPayload());
-        }
-    }
-
-    private boolean establishKeyMaterial() {
-        try {
-            int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
-            int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
-            int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);
-            int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
-            int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
-            int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);
-            km = KeyMaterial.create(kxs.hashAlgo, kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
-                                    enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
-        }
-        catch (IllegalArgumentException e) {
-            return false;
-        }
-
-        return true;
-    }
-
-    private void finishKex() throws IOException {
-        if (sessionId == null)
-            sessionId = kxs.H;
-
-        establishKeyMaterial();
-        /* Tell the other side that we start using the new material */
-        PacketNewKeys ign = new PacketNewKeys();
-        tm.sendKexMessage(ign.getPayload());
-        BlockCipher cbc;
-        MAC mac;
-        ICompressor comp;
-
-        try {
-            cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server,
-                                                  km.initial_iv_client_to_server);
-            mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);
-            comp = CompressionFactory.createCompressor(kxs.np.comp_algo_client_to_server);
-        }
-        catch (IllegalArgumentException e1) {
-            throw new IOException("Fatal error during MAC startup!");
-        }
-
-        tm.changeSendCipher(cbc, mac);
-        tm.changeSendCompression(comp);
-        tm.kexFinished();
-    }
-
-    public static final String[] getDefaultServerHostkeyAlgorithmList() {
-        return HOSTKEY_ALGS.toArray(new String[HOSTKEY_ALGS.size()]);
-    }
-
-    public static final void checkServerHostkeyAlgorithmsList(String[] algos) {
-        for (int i = 0; i < algos.length; i++) {
-            if (!HOSTKEY_ALGS.contains(algos[i]))
-                throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
-        }
-    }
-
-    public static final String[] getDefaultKexAlgorithmList() {
-        return KEX_ALGS.toArray(new String[KEX_ALGS.size()]);
-    }
-
-    public static final void checkKexAlgorithmList(String[] algos) {
-        for (int i = 0; i < algos.length; i++) {
-            if (!KEX_ALGS.contains(algos[i]))
-                throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
-        }
-    }
-
-    private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException {
-        if (kxs.np.server_host_key_algo.startsWith("ecdsa-sha2-")) {
-            byte[] rs = ECDSASHA2Verify.decodeSSHECDSASignature(sig);
-            ECPublicKey epk = ECDSASHA2Verify.decodeSSHECDSAPublicKey(hostkey);
-            log.log(50, "Verifying ecdsa signature");
-            return ECDSASHA2Verify.verifySignature(kxs.H, rs, epk);
-        }
-
-        if (kxs.np.server_host_key_algo.equals("ssh-rsa")) {
-            byte[] rs = RSASHA1Verify.decodeSSHRSASignature(sig);
-            RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);
-            log.log(50, "Verifying ssh-rsa signature");
-            return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
-        }
-
-        if (kxs.np.server_host_key_algo.equals("ssh-dss")) {
-            byte[] ds = DSASHA1Verify.decodeSSHDSASignature(sig);
-            DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);
-            log.log(50, "Verifying ssh-dss signature");
-            return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);
-        }
-
-        throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
-    }
-
-    public synchronized void handleMessage(byte[] msg, int msglen) throws IOException {
-        PacketKexInit kip;
-
-        if (msg == null) {
-            synchronized (accessLock) {
-                connectionClosed = true;
-                accessLock.notifyAll();
-                return;
-            }
-        }
-
-        if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT))
-            throw new IOException("Unexpected KEX message (type " + msg[0] + ")");
-
-        if (ignore_next_kex_packet) {
-            ignore_next_kex_packet = false;
-            return;
-        }
-
-        if (msg[0] == Packets.SSH_MSG_KEXINIT) {
-            if ((kxs != null) && (kxs.state != 0))
-                throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!");
-
-            if (kxs == null) {
-                /*
-                 * Ah, OK, peer wants to do KEX. Let's be nice and play
-                 * together.
-                 */
-                kxs = new KexState();
-                kxs.dhgexParameters = nextKEXdhgexParameters;
-                kip = new PacketKexInit(nextKEXcryptoWishList);
-                kxs.localKEX = kip;
-                tm.sendKexMessage(kip.getPayload());
-            }
-
-            kip = new PacketKexInit(msg, 0, msglen);
-            kxs.remoteKEX = kip;
-            kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());
-
-            if (kxs.np == null)
-                throw new IOException("Cannot negotiate, proposals do not match.");
-
-            if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false)) {
-                /*
-                 * Guess was wrong, we need to ignore the next kex packet.
-                 */
-                ignore_next_kex_packet = true;
-            }
-
-            if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1")
-                    || kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256")) {
-                if (kxs.dhgexParameters.getMin_group_len() == 0 || csh.server_versioncomment.matches("OpenSSH_2\\.([0-4]\\.|5\\.[0-2]).*")) {
-                    PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
-                    tm.sendKexMessage(dhgexreq.getPayload());
-                }
-                else {
-                    PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
-                    tm.sendKexMessage(dhgexreq.getPayload());
-                }
-
-                if (kxs.np.kex_algo.endsWith("sha1")) {
-                    kxs.hashAlgo = "SHA1";
-                }
-                else {
-                    kxs.hashAlgo = "SHA-256";
-                }
-
-                kxs.state = 1;
-                return;
-            }
-
-            if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
-                    || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")
-                    || kxs.np.kex_algo.equals("ecdh-sha2-nistp256")
-                    || kxs.np.kex_algo.equals("ecdh-sha2-nistp384")
-                    || kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) {
-                kxs.dhx = GenericDhExchange.getInstance(kxs.np.kex_algo);
-                kxs.dhx.init(kxs.np.kex_algo);
-                kxs.hashAlgo = kxs.dhx.getHashAlgo();
-                PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
-                tm.sendKexMessage(kp.getPayload());
-                kxs.state = 1;
-                return;
-            }
-
-            throw new IllegalStateException("Unknown KEX method!");
-        }
-
-        if (msg[0] == Packets.SSH_MSG_NEWKEYS) {
-            if (km == null)
-                throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
-
-            BlockCipher cbc;
-            MAC mac;
-            ICompressor comp;
-
-            try {
-                cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
-                                                      km.enc_key_server_to_client, km.initial_iv_server_to_client);
-                mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);
-                comp = CompressionFactory.createCompressor(kxs.np.comp_algo_server_to_client);
-            }
-            catch (IllegalArgumentException e1) {
-                throw new IOException("Fatal error during MAC startup!");
-            }
-
-            tm.changeRecvCipher(cbc, mac);
-            tm.changeRecvCompression(comp);
-            ConnectionInfo sci = new ConnectionInfo();
-            kexCount++;
-            sci.keyExchangeAlgorithm = kxs.np.kex_algo;
-            sci.keyExchangeCounter = kexCount;
-            sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
-            sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
-            sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
-            sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
-            sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
-            sci.serverHostKey = kxs.hostkey;
-
-            synchronized (accessLock) {
-                lastConnInfo = sci;
-                accessLock.notifyAll();
-            }
-
-            kxs = null;
-            return;
-        }
-
-        if ((kxs == null) || (kxs.state == 0))
-            throw new IOException("Unexpected Kex submessage!");
-
-        if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1")
-                || kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256")) {
-            if (kxs.state == 1) {
-                PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen);
-                kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
-                kxs.dhgx.init(rnd);
-                PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
-                tm.sendKexMessage(dhgexinit.getPayload());
-                kxs.state = 2;
-                return;
-            }
-
-            if (kxs.state == 2) {
-                PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen);
-                kxs.hostkey = dhgexrpl.getHostKey();
-
-                if (verifier != null) {
-                    boolean vres = false;
-
-                    try {
-                        vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
-                    }
-                    catch (Exception e) {
-                        throw(IOException) new IOException(
-                            "The server hostkey was not accepted by the verifier callback.").initCause(e);
-                    }
-
-                    if (vres == false)
-                        throw new IOException("The server hostkey was not accepted by the verifier callback");
-                }
-
-                kxs.dhgx.setF(dhgexrpl.getF());
-
-                try {
-                    kxs.H = kxs.dhgx.calculateH(kxs.hashAlgo,
-                                                csh.getClientString(), csh.getServerString(),
-                                                kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(),
-                                                dhgexrpl.getHostKey(), kxs.dhgexParameters);
-                }
-                catch (IllegalArgumentException e) {
-                    throw(IOException) new IOException("KEX error.").initCause(e);
-                }
-
-                boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey);
-
-                if (res == false)
-                    throw new IOException("Hostkey signature sent by remote is wrong!");
-
-                kxs.K = kxs.dhgx.getK();
-                finishKex();
-                kxs.state = -1;
-                return;
-            }
-
-            throw new IllegalStateException("Illegal State in KEX Exchange!");
-        }
-
-        if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
-                || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")
-                || kxs.np.kex_algo.equals("ecdh-sha2-nistp256")
-                || kxs.np.kex_algo.equals("ecdh-sha2-nistp384")
-                || kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) {
-            if (kxs.state == 1) {
-                PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen);
-                kxs.hostkey = dhr.getHostKey();
-
-                if (verifier != null) {
-                    boolean vres = false;
-
-                    try {
-                        vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
-                    }
-                    catch (Exception e) {
-                        throw(IOException) new IOException(
-                            "The server hostkey was not accepted by the verifier callback.").initCause(e);
-                    }
-
-                    if (vres == false)
-                        throw new IOException("The server hostkey was not accepted by the verifier callback");
-                }
-
-                kxs.dhx.setF(dhr.getF());
-
-                try {
-                    kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
-                                               kxs.remoteKEX.getPayload(), dhr.getHostKey());
-                }
-                catch (IllegalArgumentException e) {
-                    throw(IOException) new IOException("KEX error.").initCause(e);
-                }
-
-                boolean res = verifySignature(dhr.getSignature(), kxs.hostkey);
-
-                if (res == false)
-                    throw new IOException("Hostkey signature sent by remote is wrong!");
-
-                kxs.K = kxs.dhx.getK();
-                finishKex();
-                kxs.state = -1;
-                return;
-            }
-        }
-
-        throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")");
-    }
-}
--- a/src/com/trilead/ssh2/transport/KexParameters.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package com.trilead.ssh2.transport;
-
-/**
- * KexParameters.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: KexParameters.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class KexParameters {
-    public byte[] cookie;
-    public String[] kex_algorithms;
-    public String[] server_host_key_algorithms;
-    public String[] encryption_algorithms_client_to_server;
-    public String[] encryption_algorithms_server_to_client;
-    public String[] mac_algorithms_client_to_server;
-    public String[] mac_algorithms_server_to_client;
-    public String[] compression_algorithms_client_to_server;
-    public String[] compression_algorithms_server_to_client;
-    public String[] languages_client_to_server;
-    public String[] languages_server_to_client;
-    public boolean first_kex_packet_follows;
-    public int reserved_field1;
-}
--- a/src/com/trilead/ssh2/transport/KexState.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-package com.trilead.ssh2.transport;
-
-
-import java.math.BigInteger;
-
-import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.crypto.dh.DhGroupExchange;
-import com.trilead.ssh2.crypto.dh.GenericDhExchange;
-import com.trilead.ssh2.packets.PacketKexInit;
-
-/**
- * KexState.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: KexState.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class KexState {
-    public PacketKexInit localKEX;
-    public PacketKexInit remoteKEX;
-    public NegotiatedParameters np;
-    public int state = 0;
-
-    public BigInteger K;
-    public byte[] H;
-
-    public byte[] hostkey;
-
-    public String hashAlgo;
-    public GenericDhExchange dhx;
-    public DhGroupExchange dhgx;
-    public DHGexParameters dhgexParameters;
-}
--- a/src/com/trilead/ssh2/transport/MessageHandler.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-
-/**
- * MessageHandler.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: MessageHandler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public interface MessageHandler {
-    public void handleMessage(byte[] msg, int msglen) throws IOException;
-}
--- a/src/com/trilead/ssh2/transport/NegotiateException.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-package com.trilead.ssh2.transport;
-
-/**
- * NegotiateException.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: NegotiateException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class NegotiateException extends Exception {
-    private static final long serialVersionUID = 3689910669428143157L;
-}
--- a/src/com/trilead/ssh2/transport/NegotiatedParameters.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-package com.trilead.ssh2.transport;
-
-/**
- * NegotiatedParameters.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: NegotiatedParameters.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class NegotiatedParameters {
-    public boolean guessOK;
-    public String kex_algo;
-    public String server_host_key_algo;
-    public String enc_algo_client_to_server;
-    public String enc_algo_server_to_client;
-    public String mac_algo_client_to_server;
-    public String mac_algo_server_to_client;
-    public String comp_algo_client_to_server;
-    public String comp_algo_server_to_client;
-    public String lang_client_to_server;
-    public String lang_server_to_client;
-}
--- a/src/com/trilead/ssh2/transport/TransportConnection.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,318 +0,0 @@
-
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.compression.ICompressor;
-import com.trilead.ssh2.crypto.cipher.BlockCipher;
-import com.trilead.ssh2.crypto.cipher.CipherInputStream;
-import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
-import com.trilead.ssh2.crypto.cipher.NullCipher;
-import com.trilead.ssh2.crypto.digest.MAC;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.Packets;
-
-
-/**
- * TransportConnection.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class TransportConnection {
-    private static final Logger log = Logger.getLogger(TransportConnection.class);
-
-    int send_seq_number = 0;
-
-    int recv_seq_number = 0;
-
-    CipherInputStream cis;
-
-    CipherOutputStream cos;
-
-    boolean useRandomPadding = false;
-
-    /* Depends on current MAC and CIPHER */
-
-    MAC send_mac;
-
-    byte[] send_mac_buffer;
-
-    int send_padd_blocksize = 8;
-
-    MAC recv_mac;
-
-    byte[] recv_mac_buffer;
-
-    byte[] recv_mac_buffer_cmp;
-
-    int recv_padd_blocksize = 8;
-
-    ICompressor recv_comp = null;
-
-    ICompressor send_comp = null;
-
-    boolean can_recv_compress = false;
-
-    boolean can_send_compress = false;
-
-    byte[] recv_comp_buffer;
-
-    byte[] send_comp_buffer;
-
-    /* won't change */
-
-    final byte[] send_padding_buffer = new byte[256];
-
-    final byte[] send_packet_header_buffer = new byte[5];
-
-    final byte[] recv_padding_buffer = new byte[256];
-
-    final byte[] recv_packet_header_buffer = new byte[5];
-
-    boolean recv_packet_header_present = false;
-
-    ClientServerHello csh;
-
-    final SecureRandom rnd;
-
-    public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd) {
-        this.cis = new CipherInputStream(new NullCipher(), is);
-        this.cos = new CipherOutputStream(new NullCipher(), os);
-        this.rnd = rnd;
-    }
-
-    public void changeRecvCipher(BlockCipher bc, MAC mac) {
-        cis.changeCipher(bc);
-        recv_mac = mac;
-        recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
-        recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
-        recv_padd_blocksize = bc.getBlockSize();
-
-        if (recv_padd_blocksize < 8)
-            recv_padd_blocksize = 8;
-    }
-
-    public void changeSendCipher(BlockCipher bc, MAC mac) {
-        if ((bc instanceof NullCipher) == false) {
-            /* Only use zero byte padding for the first few packets */
-            useRandomPadding = true;
-            /* Once we start encrypting, there is no way back */
-        }
-
-        cos.changeCipher(bc);
-        send_mac = mac;
-        send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
-        send_padd_blocksize = bc.getBlockSize();
-
-        if (send_padd_blocksize < 8)
-            send_padd_blocksize = 8;
-    }
-
-    public void changeRecvCompression(ICompressor comp) {
-        recv_comp = comp;
-
-        if (comp != null) {
-            recv_comp_buffer = new byte[comp.getBufferSize()];
-            can_recv_compress |= recv_comp.canCompressPreauth();
-        }
-    }
-
-    public void changeSendCompression(ICompressor comp) {
-        send_comp = comp;
-
-        if (comp != null) {
-            send_comp_buffer = new byte[comp.getBufferSize()];
-            can_send_compress |= send_comp.canCompressPreauth();
-        }
-    }
-
-    public void sendMessage(byte[] message) throws IOException {
-        sendMessage(message, 0, message.length, 0);
-    }
-
-    public void sendMessage(byte[] message, int off, int len) throws IOException {
-        sendMessage(message, off, len, 0);
-    }
-
-    public int getPacketOverheadEstimate() {
-        // return an estimate for the paket overhead (for send operations)
-        return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
-    }
-
-    public void sendMessage(byte[] message, int off, int len, int padd) throws IOException {
-        if (padd < 4)
-            padd = 4;
-        else if (padd > 64)
-            padd = 64;
-
-        if (send_comp != null && can_send_compress) {
-            if (send_comp_buffer.length < message.length + 1024)
-                send_comp_buffer = new byte[message.length + 1024];
-
-            len = send_comp.compress(message, off, len, send_comp_buffer);
-            message = send_comp_buffer;
-        }
-
-        int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
-        int slack = packet_len % send_padd_blocksize;
-
-        if (slack != 0) {
-            packet_len += (send_padd_blocksize - slack);
-        }
-
-        if (packet_len < 16)
-            packet_len = 16;
-
-        int padd_len = packet_len - (5 + len);
-
-        if (useRandomPadding) {
-            for (int i = 0; i < padd_len; i = i + 4) {
-                /*
-                 * don't waste calls to rnd.nextInt() (by using only 8bit of the
-                 * output). just believe me: even though we may write here up to 3
-                 * bytes which won't be used, there is no "buffer overflow" (i.e.,
-                 * arrayindexoutofbounds). the padding buffer is big enough =) (256
-                 * bytes, and that is bigger than any current cipher block size + 64).
-                 */
-                int r = rnd.nextInt();
-                send_padding_buffer[i] = (byte) r;
-                send_padding_buffer[i + 1] = (byte)(r >> 8);
-                send_padding_buffer[i + 2] = (byte)(r >> 16);
-                send_padding_buffer[i + 3] = (byte)(r >> 24);
-            }
-        }
-        else {
-            /* use zero padding for unencrypted traffic */
-            for (int i = 0; i < padd_len; i++)
-                send_padding_buffer[i] = 0;
-
-            /* Actually this code is paranoid: we never filled any
-             * bytes into the padding buffer so far, therefore it should
-             * consist of zeros only.
-             */
-        }
-
-        send_packet_header_buffer[0] = (byte)((packet_len - 4) >> 24);
-        send_packet_header_buffer[1] = (byte)((packet_len - 4) >> 16);
-        send_packet_header_buffer[2] = (byte)((packet_len - 4) >> 8);
-        send_packet_header_buffer[3] = (byte)((packet_len - 4));
-        send_packet_header_buffer[4] = (byte) padd_len;
-        cos.write(send_packet_header_buffer, 0, 5);
-        cos.write(message, off, len);
-        cos.write(send_padding_buffer, 0, padd_len);
-
-        if (send_mac != null) {
-            send_mac.initMac(send_seq_number);
-            send_mac.update(send_packet_header_buffer, 0, 5);
-            send_mac.update(message, off, len);
-            send_mac.update(send_padding_buffer, 0, padd_len);
-            send_mac.getMac(send_mac_buffer, 0);
-            cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
-        }
-
-        cos.flush();
-
-        if (log.isEnabled()) {
-            log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
-        }
-
-        send_seq_number++;
-    }
-
-    public int peekNextMessageLength() throws IOException {
-        if (recv_packet_header_present == false) {
-            cis.read(recv_packet_header_buffer, 0, 5);
-            recv_packet_header_present = true;
-        }
-
-        int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
-                            | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
-                            | ((recv_packet_header_buffer[3] & 0xff));
-        int padding_length = recv_packet_header_buffer[4] & 0xff;
-
-        if (packet_length > 35000 || packet_length < 12)
-            throw new IOException("Illegal packet size! (" + packet_length + ")");
-
-        int payload_length = packet_length - padding_length - 1;
-
-        if (payload_length < 0)
-            throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
-
-        return payload_length;
-    }
-
-    public int receiveMessage(byte buffer[], int off, int len) throws IOException {
-        if (recv_packet_header_present == false) {
-            cis.read(recv_packet_header_buffer, 0, 5);
-        }
-        else
-            recv_packet_header_present = false;
-
-        int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
-                            | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
-                            | ((recv_packet_header_buffer[3] & 0xff));
-        int padding_length = recv_packet_header_buffer[4] & 0xff;
-
-        if (packet_length > 35000 || packet_length < 12)
-            throw new IOException("Illegal packet size! (" + packet_length + ")");
-
-        int payload_length = packet_length - padding_length - 1;
-
-        if (payload_length < 0)
-            throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
-
-        if (payload_length >= len)
-            throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");
-
-        cis.read(buffer, off, payload_length);
-        cis.read(recv_padding_buffer, 0, padding_length);
-
-        if (recv_mac != null) {
-            cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
-            recv_mac.initMac(recv_seq_number);
-            recv_mac.update(recv_packet_header_buffer, 0, 5);
-            recv_mac.update(buffer, off, payload_length);
-            recv_mac.update(recv_padding_buffer, 0, padding_length);
-            recv_mac.getMac(recv_mac_buffer_cmp, 0);
-
-            for (int i = 0; i < recv_mac_buffer.length; i++) {
-                if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
-                    throw new IOException("Remote sent corrupt MAC.");
-            }
-        }
-
-        recv_seq_number++;
-
-        if (log.isEnabled()) {
-            log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
-                    + " bytes payload");
-        }
-
-        if (recv_comp != null && can_recv_compress) {
-            int[] uncomp_len = new int[] { payload_length };
-            buffer = recv_comp.uncompress(buffer, off, uncomp_len);
-
-            if (buffer == null) {
-                throw new IOException("Error while inflating remote data");
-            }
-            else {
-                return uncomp_len[0];
-            }
-        }
-        else {
-            return payload_length;
-        }
-    }
-
-    /**
-     *
-     */
-    public void startCompression() {
-        can_recv_compress = true;
-        can_send_compress = true;
-    }
-}
--- a/src/com/trilead/ssh2/transport/TransportManager.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,682 +0,0 @@
-
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
-import java.security.SecureRandom;
-import java.util.Vector;
-
-import com.trilead.ssh2.ConnectionInfo;
-import com.trilead.ssh2.ConnectionMonitor;
-import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.HTTPProxyData;
-import com.trilead.ssh2.HTTPProxyException;
-import com.trilead.ssh2.ProxyData;
-import com.trilead.ssh2.ServerHostKeyVerifier;
-import com.trilead.ssh2.compression.ICompressor;
-import com.trilead.ssh2.crypto.Base64;
-import com.trilead.ssh2.crypto.CryptoWishList;
-import com.trilead.ssh2.crypto.cipher.BlockCipher;
-import com.trilead.ssh2.crypto.digest.MAC;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.PacketDisconnect;
-import com.trilead.ssh2.packets.Packets;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.util.Tokenizer;
-
-
-/*
- * Yes, the "standard" is a big mess. On one side, the say that arbitary channel
- * packets are allowed during kex exchange, on the other side we need to blindly
- * ignore the next _packet_ if the KEX guess was wrong. Where do we know from that
- * the next packet is not a channel data packet? Yes, we could check if it is in
- * the KEX range. But the standard says nothing about this. The OpenSSH guys
- * block local "normal" traffic during KEX. That's fine - however, they assume
- * that the other side is doing the same. During re-key, if they receive traffic
- * other than KEX, they become horribly irritated and kill the connection. Since
- * we are very likely going to communicate with OpenSSH servers, we have to play
- * the same game - even though we could do better.
- *
- * btw: having stdout and stderr on the same channel, with a shared window, is
- * also a VERY good idea... =(
- */
-
-/**
- * TransportManager.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: TransportManager.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class TransportManager {
-    private static final Logger log = Logger.getLogger(TransportManager.class);
-
-    class HandlerEntry {
-        MessageHandler mh;
-        int low;
-        int high;
-    }
-
-    private final Vector<byte[]> asynchronousQueue = new Vector<byte[]>();
-    private Thread asynchronousThread = null;
-
-    class AsynchronousWorker extends Thread {
-        public void run() {
-            while (true) {
-                byte[] msg = null;
-
-                synchronized (asynchronousQueue) {
-                    if (asynchronousQueue.size() == 0) {
-                        /* After the queue is empty for about 2 seconds, stop this thread */
-                        try {
-                            asynchronousQueue.wait(2000);
-                        }
-                        catch (InterruptedException e) {
-                            /* OKOK, if somebody interrupts us, then we may die earlier. */
-                        }
-
-                        if (asynchronousQueue.size() == 0) {
-                            asynchronousThread = null;
-                            return;
-                        }
-                    }
-
-                    msg = asynchronousQueue.remove(0);
-                }
-
-                /* The following invocation may throw an IOException.
-                 * There is no point in handling it - it simply means
-                 * that the connection has a problem and we should stop
-                 * sending asynchronously messages. We do not need to signal that
-                 * we have exited (asynchronousThread = null): further
-                 * messages in the queue cannot be sent by this or any
-                 * other thread.
-                 * Other threads will sooner or later (when receiving or
-                 * sending the next message) get the same IOException and
-                 * get to the same conclusion.
-                 */
-
-                try {
-                    sendMessage(msg);
-                }
-                catch (IOException e) {
-                    return;
-                }
-            }
-        }
-    }
-
-    String hostname;
-    int port;
-    final Socket sock = new Socket();
-
-    Object connectionSemaphore = new Object();
-
-    boolean flagKexOngoing = false;
-    boolean connectionClosed = false;
-
-    Throwable reasonClosedCause = null;
-
-    TransportConnection tc;
-    KexManager km;
-
-    Vector<HandlerEntry> messageHandlers = new Vector<HandlerEntry>();
-
-    Thread receiveThread;
-
-    Vector connectionMonitors = new Vector();
-    boolean monitorsWereInformed = false;
-
-    /**
-     * There were reports that there are JDKs which use
-     * the resolver even though one supplies a dotted IP
-     * address in the Socket constructor. That is why we
-     * try to generate the InetAdress "by hand".
-     *
-     * @param host
-     * @return the InetAddress
-     * @throws UnknownHostException
-     */
-    private InetAddress createInetAddress(String host) throws UnknownHostException {
-        /* Check if it is a dotted IP4 address */
-        InetAddress addr = parseIPv4Address(host);
-
-        if (addr != null)
-            return addr;
-
-        return InetAddress.getByName(host);
-    }
-
-    private InetAddress parseIPv4Address(String host) throws UnknownHostException {
-        if (host == null)
-            return null;
-
-        String[] quad = Tokenizer.parseTokens(host, '.');
-
-        if ((quad == null) || (quad.length != 4))
-            return null;
-
-        byte[] addr = new byte[4];
-
-        for (int i = 0; i < 4; i++) {
-            int part = 0;
-
-            if ((quad[i].length() == 0) || (quad[i].length() > 3))
-                return null;
-
-            for (int k = 0; k < quad[i].length(); k++) {
-                char c = quad[i].charAt(k);
-
-                /* No, Character.isDigit is not the same */
-                if ((c < '0') || (c > '9'))
-                    return null;
-
-                part = part * 10 + (c - '0');
-            }
-
-            if (part > 255) /* 300.1.2.3 is invalid =) */
-                return null;
-
-            addr[i] = (byte) part;
-        }
-
-        return InetAddress.getByAddress(host, addr);
-    }
-
-    public TransportManager(String host, int port) throws IOException {
-        this.hostname = host;
-        this.port = port;
-    }
-
-    public int getPacketOverheadEstimate() {
-        return tc.getPacketOverheadEstimate();
-    }
-
-    public void setTcpNoDelay(boolean state) throws IOException {
-        sock.setTcpNoDelay(state);
-    }
-
-    public void setSoTimeout(int timeout) throws IOException {
-        sock.setSoTimeout(timeout);
-    }
-
-    public ConnectionInfo getConnectionInfo(int kexNumber) throws IOException {
-        return km.getOrWaitForConnectionInfo(kexNumber);
-    }
-
-    public Throwable getReasonClosedCause() {
-        synchronized (connectionSemaphore) {
-            return reasonClosedCause;
-        }
-    }
-
-    public byte[] getSessionIdentifier() {
-        return km.sessionId;
-    }
-
-    public void close(Throwable cause, boolean useDisconnectPacket) {
-        if (useDisconnectPacket == false) {
-            /* OK, hard shutdown - do not aquire the semaphore,
-             * perhaps somebody is inside (and waits until the remote
-             * side is ready to accept new data). */
-            try {
-                sock.close();
-            }
-            catch (IOException ignore) {
-            }
-
-            /* OK, whoever tried to send data, should now agree that
-             * there is no point in further waiting =)
-             * It is safe now to aquire the semaphore.
-             */
-        }
-
-        synchronized (connectionSemaphore) {
-            if (connectionClosed == false) {
-                if (useDisconnectPacket == true) {
-                    try {
-                        byte[] msg = new PacketDisconnect(Packets.SSH_DISCONNECT_BY_APPLICATION, cause.getMessage(), "")
-                        .getPayload();
-
-                        if (tc != null)
-                            tc.sendMessage(msg);
-                    }
-                    catch (IOException ignore) {
-                    }
-
-                    try {
-                        sock.close();
-                    }
-                    catch (IOException ignore) {
-                    }
-                }
-
-                connectionClosed = true;
-                reasonClosedCause = cause; /* may be null */
-            }
-
-            connectionSemaphore.notifyAll();
-        }
-
-        /* No check if we need to inform the monitors */
-        Vector monitors = null;
-
-        synchronized (this) {
-            /* Short term lock to protect "connectionMonitors"
-             * and "monitorsWereInformed"
-             * (they may be modified concurrently)
-             */
-            if (monitorsWereInformed == false) {
-                monitorsWereInformed = true;
-                monitors = (Vector) connectionMonitors.clone();
-            }
-        }
-
-        if (monitors != null) {
-            for (int i = 0; i < monitors.size(); i++) {
-                try {
-                    ConnectionMonitor cmon = (ConnectionMonitor) monitors.elementAt(i);
-                    cmon.connectionLost(reasonClosedCause);
-                }
-                catch (Exception ignore) {
-                }
-            }
-        }
-    }
-
-    private void establishConnection(ProxyData proxyData, int connectTimeout) throws IOException {
-        /* See the comment for createInetAddress() */
-        if (proxyData == null) {
-            InetAddress addr = createInetAddress(hostname);
-            sock.connect(new InetSocketAddress(addr, port), connectTimeout);
-            sock.setSoTimeout(0);
-            return;
-        }
-
-        if (proxyData instanceof HTTPProxyData) {
-            HTTPProxyData pd = (HTTPProxyData) proxyData;
-            /* At the moment, we only support HTTP proxies */
-            InetAddress addr = createInetAddress(pd.proxyHost);
-            sock.connect(new InetSocketAddress(addr, pd.proxyPort), connectTimeout);
-            sock.setSoTimeout(0);
-            /* OK, now tell the proxy where we actually want to connect to */
-            StringBuffer sb = new StringBuffer();
-            sb.append("CONNECT ");
-            sb.append(hostname);
-            sb.append(':');
-            sb.append(port);
-            sb.append(" HTTP/1.0\r\n");
-
-            if ((pd.proxyUser != null) && (pd.proxyPass != null)) {
-                String credentials = pd.proxyUser + ":" + pd.proxyPass;
-                char[] encoded = Base64.encode(credentials.getBytes("ISO-8859-1"));
-                sb.append("Proxy-Authorization: Basic ");
-                sb.append(encoded);
-                sb.append("\r\n");
-            }
-
-            if (pd.requestHeaderLines != null) {
-                for (int i = 0; i < pd.requestHeaderLines.length; i++) {
-                    if (pd.requestHeaderLines[i] != null) {
-                        sb.append(pd.requestHeaderLines[i]);
-                        sb.append("\r\n");
-                    }
-                }
-            }
-
-            sb.append("\r\n");
-            OutputStream out = sock.getOutputStream();
-            out.write(sb.toString().getBytes("ISO-8859-1"));
-            out.flush();
-            /* Now parse the HTTP response */
-            byte[] buffer = new byte[1024];
-            InputStream in = sock.getInputStream();
-            int len = ClientServerHello.readLineRN(in, buffer);
-            String httpReponse = new String(buffer, 0, len, "ISO-8859-1");
-
-            if (httpReponse.startsWith("HTTP/") == false)
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-
-            /* "HTTP/1.X XYZ X" => 14 characters minimum */
-
-            if ((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' '))
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-
-            int errorCode = 0;
-
-            try {
-                errorCode = Integer.parseInt(httpReponse.substring(9, 12));
-            }
-            catch (NumberFormatException ignore) {
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-            }
-
-            if ((errorCode < 0) || (errorCode > 999))
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-
-            if (errorCode != 200) {
-                throw new HTTPProxyException(httpReponse.substring(13), errorCode);
-            }
-
-            /* OK, read until empty line */
-
-            while (true) {
-                len = ClientServerHello.readLineRN(in, buffer);
-
-                if (len == 0)
-                    break;
-            }
-
-            return;
-        }
-
-        throw new IOException("Unsupported ProxyData");
-    }
-
-    public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
-                           int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException {
-        /* First, establish the TCP connection to the SSH-2 server */
-        establishConnection(proxyData, connectTimeout);
-        /* Parse the server line and say hello - important: this information is later needed for the
-         * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
-         * for later use.
-         */
-        ClientServerHello csh = new ClientServerHello(sock.getInputStream(), sock.getOutputStream());
-        tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
-        km = new KexManager(this, csh, cwl, hostname, port, verifier, rnd);
-        km.initiateKEX(cwl, dhgex);
-        receiveThread = new Thread(new Runnable() {
-            public void run() {
-                try {
-                    receiveLoop();
-                }
-                catch (IOException e) {
-                    close(e, false);
-
-                    if (log.isEnabled())
-                        log.log(10, "Receive thread: error in receiveLoop: " + e.getMessage());
-                }
-
-                if (log.isEnabled())
-                    log.log(50, "Receive thread: back from receiveLoop");
-
-                /* Tell all handlers that it is time to say goodbye */
-
-                if (km != null) {
-                    try {
-                        km.handleMessage(null, 0);
-                    }
-                    catch (IOException e) {
-                    }
-                }
-
-                for (int i = 0; i < messageHandlers.size(); i++) {
-                    HandlerEntry he = messageHandlers.elementAt(i);
-
-                    try {
-                        he.mh.handleMessage(null, 0);
-                    }
-                    catch (Exception ignore) {
-                    }
-                }
-            }
-        });
-        receiveThread.setDaemon(true);
-        receiveThread.start();
-    }
-
-    public void registerMessageHandler(MessageHandler mh, int low, int high) {
-        HandlerEntry he = new HandlerEntry();
-        he.mh = mh;
-        he.low = low;
-        he.high = high;
-
-        synchronized (messageHandlers) {
-            messageHandlers.addElement(he);
-        }
-    }
-
-    public void removeMessageHandler(MessageHandler mh, int low, int high) {
-        synchronized (messageHandlers) {
-            for (int i = 0; i < messageHandlers.size(); i++) {
-                HandlerEntry he = messageHandlers.elementAt(i);
-
-                if ((he.mh == mh) && (he.low == low) && (he.high == high)) {
-                    messageHandlers.removeElementAt(i);
-                    break;
-                }
-            }
-        }
-    }
-
-    public void sendKexMessage(byte[] msg) throws IOException {
-        synchronized (connectionSemaphore) {
-            if (connectionClosed) {
-                throw(IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
-            }
-
-            flagKexOngoing = true;
-
-            try {
-                tc.sendMessage(msg);
-            }
-            catch (IOException e) {
-                close(e, false);
-                throw e;
-            }
-        }
-    }
-
-    public void kexFinished() throws IOException {
-        synchronized (connectionSemaphore) {
-            flagKexOngoing = false;
-            connectionSemaphore.notifyAll();
-        }
-    }
-
-    public void forceKeyExchange(CryptoWishList cwl, DHGexParameters dhgex) throws IOException {
-        km.initiateKEX(cwl, dhgex);
-    }
-
-    public void changeRecvCipher(BlockCipher bc, MAC mac) {
-        tc.changeRecvCipher(bc, mac);
-    }
-
-    public void changeSendCipher(BlockCipher bc, MAC mac) {
-        tc.changeSendCipher(bc, mac);
-    }
-
-    /**
-     * @param comp
-     */
-    public void changeRecvCompression(ICompressor comp) {
-        tc.changeRecvCompression(comp);
-    }
-
-    /**
-     * @param comp
-     */
-    public void changeSendCompression(ICompressor comp) {
-        tc.changeSendCompression(comp);
-    }
-
-    /**
-     *
-     */
-    public void startCompression() {
-        tc.startCompression();
-    }
-
-    public void sendAsynchronousMessage(byte[] msg) throws IOException {
-        synchronized (asynchronousQueue) {
-            asynchronousQueue.addElement(msg);
-
-            /* This limit should be flexible enough. We need this, otherwise the peer
-             * can flood us with global requests (and other stuff where we have to reply
-             * with an asynchronous message) and (if the server just sends data and does not
-             * read what we send) this will probably put us in a low memory situation
-             * (our send queue would grow and grow and...) */
-
-            if (asynchronousQueue.size() > 100)
-                throw new IOException("Error: the peer is not consuming our asynchronous replies.");
-
-            /* Check if we have an asynchronous sending thread */
-
-            if (asynchronousThread == null) {
-                asynchronousThread = new AsynchronousWorker();
-                asynchronousThread.setDaemon(true);
-                asynchronousThread.start();
-                /* The thread will stop after 2 seconds of inactivity (i.e., empty queue) */
-            }
-        }
-    }
-
-    public void setConnectionMonitors(Vector monitors) {
-        synchronized (this) {
-            connectionMonitors = (Vector) monitors.clone();
-        }
-    }
-
-    public void sendMessage(byte[] msg) throws IOException {
-        if (Thread.currentThread() == receiveThread)
-            throw new IOException("Assertion error: sendMessage may never be invoked by the receiver thread!");
-
-        synchronized (connectionSemaphore) {
-            while (true) {
-                if (connectionClosed) {
-                    throw(IOException) new IOException("Sorry, this connection is closed.")
-                    .initCause(reasonClosedCause);
-                }
-
-                if (flagKexOngoing == false)
-                    break;
-
-                try {
-                    connectionSemaphore.wait();
-                }
-                catch (InterruptedException e) {
-                }
-            }
-
-            try {
-                tc.sendMessage(msg);
-            }
-            catch (IOException e) {
-                close(e, false);
-                throw e;
-            }
-        }
-    }
-
-    public void receiveLoop() throws IOException {
-        byte[] msg = new byte[35000];
-
-        while (true) {
-            int msglen = tc.receiveMessage(msg, 0, msg.length);
-            int type = msg[0] & 0xff;
-
-            if (type == Packets.SSH_MSG_IGNORE)
-                continue;
-
-            if (type == Packets.SSH_MSG_DEBUG) {
-                if (log.isEnabled()) {
-                    TypesReader tr = new TypesReader(msg, 0, msglen);
-                    tr.readByte();
-                    tr.readBoolean();
-                    StringBuffer debugMessageBuffer = new StringBuffer();
-                    debugMessageBuffer.append(tr.readString("UTF-8"));
-
-                    for (int i = 0; i < debugMessageBuffer.length(); i++) {
-                        char c = debugMessageBuffer.charAt(i);
-
-                        if ((c >= 32) && (c <= 126))
-                            continue;
-
-                        debugMessageBuffer.setCharAt(i, '\uFFFD');
-                    }
-
-                    log.log(50, "DEBUG Message from remote: '" + debugMessageBuffer.toString() + "'");
-                }
-
-                continue;
-            }
-
-            if (type == Packets.SSH_MSG_UNIMPLEMENTED) {
-                throw new IOException("Peer sent UNIMPLEMENTED message, that should not happen.");
-            }
-
-            if (type == Packets.SSH_MSG_DISCONNECT) {
-                TypesReader tr = new TypesReader(msg, 0, msglen);
-                tr.readByte();
-                int reason_code = tr.readUINT32();
-                StringBuffer reasonBuffer = new StringBuffer();
-                reasonBuffer.append(tr.readString("UTF-8"));
-
-                /*
-                 * Do not get fooled by servers that send abnormal long error
-                 * messages
-                 */
-
-                if (reasonBuffer.length() > 255) {
-                    reasonBuffer.setLength(255);
-                    reasonBuffer.setCharAt(254, '.');
-                    reasonBuffer.setCharAt(253, '.');
-                    reasonBuffer.setCharAt(252, '.');
-                }
-
-                /*
-                 * Also, check that the server did not send charcaters that may
-                 * screw up the receiver -> restrict to reasonable US-ASCII
-                 * subset -> "printable characters" (ASCII 32 - 126). Replace
-                 * all others with 0xFFFD (UNICODE replacement character).
-                 */
-
-                for (int i = 0; i < reasonBuffer.length(); i++) {
-                    char c = reasonBuffer.charAt(i);
-
-                    if ((c >= 32) && (c <= 126))
-                        continue;
-
-                    reasonBuffer.setCharAt(i, '\uFFFD');
-                }
-
-                throw new IOException("Peer sent DISCONNECT message (reason code " + reason_code + "): "
-                                      + reasonBuffer.toString());
-            }
-
-            /*
-             * Is it a KEX Packet?
-             */
-
-            if ((type == Packets.SSH_MSG_KEXINIT) || (type == Packets.SSH_MSG_NEWKEYS)
-                    || ((type >= 30) && (type <= 49))) {
-                km.handleMessage(msg, msglen);
-                continue;
-            }
-
-            if (type == Packets.SSH_MSG_USERAUTH_SUCCESS) {
-                tc.startCompression();
-            }
-
-            MessageHandler mh = null;
-
-            for (int i = 0; i < messageHandlers.size(); i++) {
-                HandlerEntry he = messageHandlers.elementAt(i);
-
-                if ((he.low <= type) && (type <= he.high)) {
-                    mh = he.mh;
-                    break;
-                }
-            }
-
-            if (mh == null)
-                throw new IOException("Unexpected SSH message (type " + type + ")");
-
-            mh.handleMessage(msg, msglen);
-        }
-    }
-}
--- a/src/com/trilead/ssh2/util/TimeoutService.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-
-package com.trilead.ssh2.util;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Collections;
-import java.util.LinkedList;
-
-import com.trilead.ssh2.log.Logger;
-
-
-/**
- * TimeoutService (beta). Here you can register a timeout.
- * <p>
- * Implemented having large scale programs in mind: if you open many concurrent SSH connections
- * that rely on timeouts, then there will be only one timeout thread. Once all timeouts
- * have expired/are cancelled, the thread will (sooner or later) exit.
- * Only after new timeouts arrive a new thread (singleton) will be instantiated.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: TimeoutService.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class TimeoutService {
-    private static final Logger log = Logger.getLogger(TimeoutService.class);
-
-    public static class TimeoutToken implements Comparable {
-        private long runTime;
-        private Runnable handler;
-
-        private TimeoutToken(long runTime, Runnable handler) {
-            this.runTime = runTime;
-            this.handler = handler;
-        }
-
-        public int compareTo(Object o) {
-            TimeoutToken t = (TimeoutToken) o;
-
-            if (runTime > t.runTime)
-                return 1;
-
-            if (runTime == t.runTime)
-                return 0;
-
-            return -1;
-        }
-    }
-
-    private static class TimeoutThread extends Thread {
-        public void run() {
-            synchronized (todolist) {
-                while (true) {
-                    if (todolist.size() == 0) {
-                        timeoutThread = null;
-                        return;
-                    }
-
-                    long now = System.currentTimeMillis();
-                    TimeoutToken tt = (TimeoutToken) todolist.getFirst();
-
-                    if (tt.runTime > now) {
-                        /* Not ready yet, sleep a little bit */
-                        try {
-                            todolist.wait(tt.runTime - now);
-                        }
-                        catch (InterruptedException e) {
-                        }
-
-                        /* We cannot simply go on, since it could be that the token
-                         * was removed (cancelled) or another one has been inserted in
-                         * the meantime.
-                         */
-                        continue;
-                    }
-
-                    todolist.removeFirst();
-
-                    try {
-                        tt.handler.run();
-                    }
-                    catch (Exception e) {
-                        StringWriter sw = new StringWriter();
-                        e.printStackTrace(new PrintWriter(sw));
-                        log.log(20, "Exeception in Timeout handler:" + e.getMessage() + "(" + sw.toString() + ")");
-                    }
-                }
-            }
-        }
-    }
-
-    /* The list object is also used for locking purposes */
-    private static final LinkedList todolist = new LinkedList();
-
-    private static Thread timeoutThread = null;
-
-    /**
-     * It is assumed that the passed handler will not execute for a long time.
-     *
-     * @param runTime
-     * @param handler
-     * @return a TimeoutToken that can be used to cancel the timeout.
-     */
-    public static final TimeoutToken addTimeoutHandler(long runTime, Runnable handler) {
-        TimeoutToken token = new TimeoutToken(runTime, handler);
-
-        synchronized (todolist) {
-            todolist.add(token);
-            Collections.sort(todolist);
-
-            if (timeoutThread != null)
-                timeoutThread.interrupt();
-            else {
-                timeoutThread = new TimeoutThread();
-                timeoutThread.setDaemon(true);
-                timeoutThread.start();
-            }
-        }
-
-        return token;
-    }
-
-    public static final void cancelTimeoutHandler(TimeoutToken token) {
-        synchronized (todolist) {
-            todolist.remove(token);
-
-            if (timeoutThread != null)
-                timeoutThread.interrupt();
-        }
-    }
-
-}
--- a/src/com/trilead/ssh2/util/Tokenizer.java	Thu Jul 17 22:09:05 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-
-package com.trilead.ssh2.util;
-
-/**
- * Tokenizer. Why? Because StringTokenizer is not available in J2ME.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Tokenizer.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class Tokenizer {
-    /**
-     * Exists because StringTokenizer is not available in J2ME.
-     * Returns an array with at least 1 entry.
-     *
-     * @param source must be non-null
-     * @param delimiter
-     * @return an array of Strings
-     */
-    public static String[] parseTokens(String source, char delimiter) {
-        int numtoken = 1;
-
-        for (int i = 0; i < source.length(); i++) {
-            if (source.charAt(i) == delimiter)
-                numtoken++;
-        }
-
-        String list[] = new String[numtoken];
-        int nextfield = 0;
-
-        for (int i = 0; i < numtoken; i++) {
-            if (nextfield >= source.length()) {
-                list[i] = "";
-            }
-            else {
-                int idx = source.indexOf(delimiter, nextfield);
-
-                if (idx == -1)
-                    idx = source.length();
-
-                list[i] = source.substring(nextfield, idx);
-                nextfield = idx + 1;
-            }
-        }
-
-        return list;
-    }
-}
--- a/src/de/mud/terminal/VDUBuffer.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/de/mud/terminal/VDUBuffer.java	Thu Jul 31 16:33:38 2014 -0700
@@ -159,7 +159,9 @@
 
     public void putChar(int c, int l, char ch, int attributes) {
         int ll = screenBase + l;
+
         if ((ll >= bufSize) || (c >= width)) return;    // ignore characters outside our buffer
+
         charArray[ll][c] = ch;
         charAttributes[ll][c] = attributes;
 
@@ -588,8 +590,8 @@
      * @param l line
      */
     public void setCursorPosition(int c, int l) {
-        cursorX = (c > width-1) ? width-1 : c;
-        cursorY = (l > height-1) ? height-1 : l;
+        cursorX = (c > width - 1) ? width - 1 : c;
+        cursorY = (l > height - 1) ? height - 1 : l;
     }
 
     /**
--- a/src/org/tn5250j/framework/tn5250/Screen5250.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/org/tn5250j/framework/tn5250/Screen5250.java	Thu Jul 31 16:33:38 2014 -0700
@@ -1327,6 +1327,7 @@
                 break;
 
             case HOME:
+
                 // position to the home position set
                 if (lastPos + numCols + 1 != homePos) {
                     goto_XY(homePos - numCols - 1);
@@ -2661,7 +2662,9 @@
 
         if (l >= 0) {
             lastPos = l * numCols + c;
+
             while (!isInField()) advancePos();
+
             setDirty(lastPos);
             fireCursorChanged();
         }
@@ -2673,12 +2676,14 @@
             setDirty(lastPos);
             setDirty(lastPos + cf.getLength());
             lastPos += data.length;
+
             if (!isInField()) {
                 gotoFieldNext();
                 isInField();
                 cf = screenFields.getCurrentField();
                 lastPos = cf.getStartPos();
             }
+
             setDirty(lastPos);
             fireCursorChanged();
         }
@@ -3568,7 +3573,9 @@
      */
     protected void changePos(int i) {
         lastPos += i;
+
         while (lastPos < 0)          lastPos += lenScreen;
+
         while (lastPos >= lenScreen) lastPos -= lenScreen;
     }
 
@@ -3745,7 +3752,7 @@
     }
 
     public void onFontSizeChanged(float size) {
-        fireScreenChanged(0, 0, numRows-1, numCols-1);
+        fireScreenChanged(0, 0, numRows - 1, numCols - 1);
     }
 
     /**
--- a/src/org/tn5250j/framework/tn5250/tnvt.java	Thu Jul 17 22:09:05 2014 -0700
+++ b/src/org/tn5250j/framework/tn5250/tnvt.java	Thu Jul 31 16:33:38 2014 -0700
@@ -243,6 +243,7 @@
 
     private String fixer(String value, String def) {
         if ((value == null) || (value.length() == 0)) return def;
+
         return value;
     }
 
@@ -1109,7 +1110,6 @@
                 cursorOn = false;
                 buffer.testChanged();
             }
-
         }
     }
 
--- a/xml/510connectbot.in	Thu Jul 17 22:09:05 2014 -0700
+++ b/xml/510connectbot.in	Thu Jul 31 16:33:38 2014 -0700
@@ -251,9 +251,6 @@
                 The tn5250 ssl/tls key storage should use the same storage mechanism
                 as the base ssh key storage.
             </para>
-            <para>
-                com.trilead.ssh2 should be updated to the ganymed fork at ch.ethz.ssh2.
-            </para>
         </refsect1>
 
         <refsect1 id='copyright.1'>
@@ -293,9 +290,29 @@
                 http://the-b.org and Jeffrey Sharkey http://jsharkey.org
             </para>
             <para>
-                Based on the Trilead SSH2 client provided
-                under a BSD-style license. Copyright (C) 2007 Trilead
-                AG. http://www.trilead.com
+                Based on the Ganymed SSH2 client provided under a BSD-style
+                license. Copyright (C) 2005 - 2006 Swiss Federal Institute of
+                Technology (ETH Zurich), Department of Computer Science
+                http://www.inf.ethz.ch, Christian Plattner.  Copyright (C) 2006 - 2013
+                Christian Plattner.  http://code.google.com/p/ganymed-ssh-2/ The Java
+                implementations of the AES, Blowfish and 3DES ciphers have been taken
+                (and slightly modified) from the cryptography package released by "The
+                Legion Of The Bouncy Castle".  Copyright (C) 2000 - 2004 The Legion Of
+                The Bouncy Castle http://www.bouncycastle.org The following disclaimer
+                applies:
+            </para>
+            <para>
+                THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+                AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+                IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+                ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+                LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+                SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+                INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+                CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+                ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+                POSSIBILITY OF SUCH DAMAGE.
             </para>
             <para>
                 Based on JTA Telnet/SSH client provided under the