[trilead-ssh2] 06/10: Imported Upstream version 6401+svn158

Emmanuel Bourg ebourg-guest at moszumanska.debian.org
Fri Dec 12 11:02:37 UTC 2014


This is an automated email from the git hooks/post-receive script.

ebourg-guest pushed a commit to branch master
in repository trilead-ssh2.

commit 3526a5db0abb2945c91e8e5a829c63577c2afa00
Author: Emmanuel Bourg <ebourg at apache.org>
Date:   Fri Dec 12 11:47:17 2014 +0100

    Imported Upstream version 6401+svn158
---
 .../java}/com/trilead/ssh2/ChannelCondition.java   |  122 +-
 .../java}/com/trilead/ssh2/Connection.java         | 3039 +++++++++---------
 .../java}/com/trilead/ssh2/ConnectionInfo.java     |  106 +-
 .../java}/com/trilead/ssh2/ConnectionMonitor.java  |   66 +-
 .../java}/com/trilead/ssh2/DHGexParameters.java    |  242 +-
 .../java}/com/trilead/ssh2/DebugLogger.java        |   46 +-
 .../java}/com/trilead/ssh2/HTTPProxyData.java      |  166 +-
 .../java}/com/trilead/ssh2/HTTPProxyException.java |   58 +-
 .../java/com/trilead/ssh2/IOWarningException.java  |   17 +
 .../com/trilead/ssh2/InteractiveCallback.java      |  110 +-
 .../java}/com/trilead/ssh2/KnownHosts.java         | 1704 +++++-----
 .../java}/com/trilead/ssh2/LocalPortForwarder.java |  126 +-
 .../com/trilead/ssh2/LocalStreamForwarder.java     |  156 +-
 .../java}/com/trilead/ssh2/ProxyData.java          |   30 +-
 .../java}/com/trilead/ssh2/SCPClient.java          | 1458 ++++-----
 .../java}/com/trilead/ssh2/SFTPException.java      |  182 +-
 .../java}/com/trilead/ssh2/SFTPv3Client.java       | 2776 ++++++++--------
 .../com/trilead/ssh2/SFTPv3DirectoryEntry.java     |   76 +-
 .../com/trilead/ssh2/SFTPv3FileAttributes.java     |  290 +-
 .../java}/com/trilead/ssh2/SFTPv3FileHandle.java   |   90 +-
 .../com/trilead/ssh2/ServerHostKeyVerifier.java    |   62 +-
 src/{ => main/java}/com/trilead/ssh2/Session.java  |  958 +++---
 .../java}/com/trilead/ssh2/StreamGobbler.java      |  458 +--
 .../java/com/trilead/ssh2/auth/AgentIdentity.java  |    7 +
 .../java/com/trilead/ssh2/auth/AgentProxy.java     |    7 +
 .../trilead/ssh2/auth/AuthenticationManager.java   |  890 +++---
 .../java}/com/trilead/ssh2/channel/Channel.java    |  414 +--
 .../trilead/ssh2/channel/ChannelInputStream.java   |  172 +-
 .../com/trilead/ssh2/channel/ChannelManager.java   | 3358 ++++++++++----------
 .../trilead/ssh2/channel/ChannelOutputStream.java  |  140 +-
 .../trilead/ssh2/channel/IChannelWorkerThread.java |   26 +-
 .../trilead/ssh2/channel/LocalAcceptThread.java    |  270 +-
 .../trilead/ssh2/channel/RemoteAcceptThread.java   |  206 +-
 .../trilead/ssh2/channel/RemoteForwardingData.java |   34 +-
 .../ssh2/channel/RemoteX11AcceptThread.java        |  480 +--
 .../com/trilead/ssh2/channel/StreamForwarder.java  |  222 +-
 .../com/trilead/ssh2/channel/X11ServerData.java    |   32 +-
 .../java}/com/trilead/ssh2/crypto/Base64.java      |  296 +-
 .../com/trilead/ssh2/crypto/CryptoWishList.java    |   46 +-
 .../java}/com/trilead/ssh2/crypto/KeyMaterial.java |  182 +-
 .../java}/com/trilead/ssh2/crypto/PEMDecoder.java  |  754 ++---
 .../com/trilead/ssh2/crypto/PEMStructure.java      |   32 +-
 .../com/trilead/ssh2/crypto/SimpleDERReader.java   |  320 +-
 .../java}/com/trilead/ssh2/crypto/cipher/AES.java  | 1396 ++++----
 .../trilead/ssh2/crypto/cipher/BlockCipher.java    |   32 +-
 .../ssh2/crypto/cipher/BlockCipherFactory.java     |  230 +-
 .../com/trilead/ssh2/crypto/cipher/BlowFish.java   |  806 ++---
 .../com/trilead/ssh2/crypto/cipher/CBCMode.java    |  156 +-
 .../com/trilead/ssh2/crypto/cipher/CTRMode.java    |  124 +-
 .../ssh2/crypto/cipher/CipherInputStream.java      |  288 +-
 .../ssh2/crypto/cipher/CipherOutputStream.java     |  284 +-
 .../java}/com/trilead/ssh2/crypto/cipher/DES.java  |  746 ++---
 .../com/trilead/ssh2/crypto/cipher/DESede.java     |  210 +-
 .../com/trilead/ssh2/crypto/cipher/NullCipher.java |   70 +-
 .../com/trilead/ssh2/crypto/dh/DhExchange.java     |  294 +-
 .../trilead/ssh2/crypto/dh/DhGroupExchange.java    |  226 +-
 .../com/trilead/ssh2/crypto/digest/Digest.java     |   50 +-
 .../java}/com/trilead/ssh2/crypto/digest/HMAC.java |  190 +-
 .../ssh2/crypto/digest/HashForSSH2Types.java       |  186 +-
 .../java}/com/trilead/ssh2/crypto/digest/MAC.java  |  176 +-
 .../java}/com/trilead/ssh2/crypto/digest/MD5.java  |  536 ++--
 .../java}/com/trilead/ssh2/crypto/digest/SHA1.java | 1328 ++++----
 .../java}/com/trilead/ssh2/log/Logger.java         |  108 +-
 .../packets/PacketChannelOpenConfirmation.java     |  132 +-
 .../ssh2/packets/PacketChannelOpenFailure.java     |  132 +-
 .../ssh2/packets/PacketChannelTrileadPing.java     |   70 +-
 .../ssh2/packets/PacketChannelWindowAdjust.java    |  114 +-
 .../com/trilead/ssh2/packets/PacketDisconnect.java |  114 +-
 .../packets/PacketGlobalCancelForwardRequest.java  |   84 +-
 .../ssh2/packets/PacketGlobalForwardRequest.java   |   82 +-
 .../ssh2/packets/PacketGlobalTrileadPing.java      |   64 +-
 .../com/trilead/ssh2/packets/PacketIgnore.java     |  118 +-
 .../com/trilead/ssh2/packets/PacketKexDHInit.java  |   66 +-
 .../com/trilead/ssh2/packets/PacketKexDHReply.java |  110 +-
 .../trilead/ssh2/packets/PacketKexDhGexGroup.java  |  100 +-
 .../trilead/ssh2/packets/PacketKexDhGexInit.java   |   66 +-
 .../trilead/ssh2/packets/PacketKexDhGexReply.java  |  112 +-
 .../ssh2/packets/PacketKexDhGexRequest.java        |   78 +-
 .../ssh2/packets/PacketKexDhGexRequestOld.java     |   68 +-
 .../com/trilead/ssh2/packets/PacketKexInit.java    |  330 +-
 .../com/trilead/ssh2/packets/PacketNewKeys.java    |   92 +-
 .../ssh2/packets/PacketOpenDirectTCPIPChannel.java |  112 +-
 .../ssh2/packets/PacketOpenSessionChannel.java     |  124 +-
 .../trilead/ssh2/packets/PacketServiceAccept.java  |  122 +-
 .../trilead/ssh2/packets/PacketServiceRequest.java |  104 +-
 .../ssh2/packets/PacketSessionExecCommand.java     |   78 +-
 .../ssh2/packets/PacketSessionPtyRequest.java      |  114 +-
 .../ssh2/packets/PacketSessionStartShell.java      |   72 +-
 .../packets/PacketSessionSubsystemRequest.java     |   80 +-
 .../ssh2/packets/PacketSessionX11Request.java      |  106 +-
 .../trilead/ssh2/packets/PacketUserauthBanner.java |  120 +-
 .../ssh2/packets/PacketUserauthFailure.java        |  106 +-
 .../ssh2/packets/PacketUserauthInfoRequest.java    |  168 +-
 .../ssh2/packets/PacketUserauthInfoResponse.java   |   70 +-
 .../packets/PacketUserauthRequestInteractive.java  |   84 +-
 .../ssh2/packets/PacketUserauthRequestNone.java    |  122 +-
 .../packets/PacketUserauthRequestPassword.java     |  134 +-
 .../packets/PacketUserauthRequestPublicKey.java    |  130 +-
 .../java}/com/trilead/ssh2/packets/Packets.java    |  298 +-
 .../com/trilead/ssh2/packets/TypesReader.java      |  354 +--
 .../com/trilead/ssh2/packets/TypesWriter.java      |  338 +-
 .../java}/com/trilead/ssh2/sftp/AttrTextHints.java |   76 +-
 .../java}/com/trilead/ssh2/sftp/AttribBits.java    |  258 +-
 .../java}/com/trilead/ssh2/sftp/AttribFlags.java   |  224 +-
 .../com/trilead/ssh2/sftp/AttribPermissions.java   |   64 +-
 .../java}/com/trilead/ssh2/sftp/AttribTypes.java   |   56 +-
 .../java}/com/trilead/ssh2/sftp/ErrorCodes.java    |  208 +-
 .../java}/com/trilead/ssh2/sftp/OpenFlags.java     |  446 +--
 .../java}/com/trilead/ssh2/sftp/Packet.java        |   86 +-
 .../com/trilead/ssh2/signature/DSAPrivateKey.java  |  114 +-
 .../com/trilead/ssh2/signature/DSAPublicKey.java   |   88 +-
 .../com/trilead/ssh2/signature/DSASHA1Verify.java  |  421 +--
 .../com/trilead/ssh2/signature/DSASignature.java   |   62 +-
 .../com/trilead/ssh2/signature/RSAPrivateKey.java  |   84 +-
 .../com/trilead/ssh2/signature/RSAPublicKey.java   |   60 +-
 .../com/trilead/ssh2/signature/RSASHA1Verify.java  |  571 ++--
 .../com/trilead/ssh2/signature/RSASignature.java   |   52 +-
 .../trilead/ssh2/transport/ClientServerHello.java  |  250 +-
 .../com/trilead/ssh2/transport/KexManager.java     | 1258 ++++----
 .../com/trilead/ssh2/transport/KexParameters.java  |   48 +-
 .../java}/com/trilead/ssh2/transport/KexState.java |   64 +-
 .../com/trilead/ssh2/transport/MessageHandler.java |   28 +-
 .../trilead/ssh2/transport/NegotiateException.java |   24 +-
 .../ssh2/transport/NegotiatedParameters.java       |   44 +-
 .../ssh2/transport/TransportConnection.java        |  568 ++--
 .../trilead/ssh2/transport/TransportManager.java   | 1569 ++++-----
 .../com/trilead/ssh2/util/TimeoutService.java      |  298 +-
 .../java}/com/trilead/ssh2/util/Tokenizer.java     |  102 +-
 128 files changed, 19845 insertions(+), 19708 deletions(-)

diff --git a/src/com/trilead/ssh2/ChannelCondition.java b/src/main/java/com/trilead/ssh2/ChannelCondition.java
similarity index 96%
rename from src/com/trilead/ssh2/ChannelCondition.java
rename to src/main/java/com/trilead/ssh2/ChannelCondition.java
index f3a0755..df1ad50 100644
--- a/src/com/trilead/ssh2/ChannelCondition.java
+++ b/src/main/java/com/trilead/ssh2/ChannelCondition.java
@@ -1,61 +1,61 @@
-
-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 at 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;
-
-}
+
+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 at 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;
+
+}
diff --git a/src/com/trilead/ssh2/Connection.java b/src/main/java/com/trilead/ssh2/Connection.java
similarity index 96%
rename from src/com/trilead/ssh2/Connection.java
rename to src/main/java/com/trilead/ssh2/Connection.java
index fff0966..3f21c73 100644
--- a/src/com/trilead/ssh2/Connection.java
+++ b/src/main/java/com/trilead/ssh2/Connection.java
@@ -1,1507 +1,1532 @@
-
-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.SecureRandom;
-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.ClientServerHello;
-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 at 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 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 connectionMonitors = new Vector();
-
-	/**
-	 * 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;
-	}
-
-	/**
-	 * 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);
-	}
-
-	/**
-	 * 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);
-
-		/*
-		 * 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);
-	}
-
-	/**
-	 * 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);
-	}
-	
-	public synchronized ClientServerHello getVersionInfo() throws IOException {
-		if (tm == null)
-			throw new IllegalStateException(
-					"Cannot get details of connection, you need to establish a connection first.");
-		return tm.getVersionInfo();
-	}
-
-
-	/**
-	 * 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 < 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 > 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);
-					}
-				};
-			}
-		}
-	}
-
-	/**
-	 * 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();
-	}
-}
+
+package com.trilead.ssh2;
+
+import com.trilead.ssh2.auth.AgentProxy;
+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.ClientServerHello;
+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;
+
+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.SecureRandom;
+import java.util.Vector;
+
+/**
+ * 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 at 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 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 connectionMonitors = new Vector();
+
+	/**
+	 * 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);
+	}
+
+	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
+	 * 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;
+	}
+
+	/**
+	 * 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);
+	}
+
+	/**
+	 * 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 {
+			return connect(verifier, connectTimeout, 0, kexTimeout);
+    }
+
+    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int readTimeout, 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);
+
+		/*
+		 * 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, readTimeout, 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);
+	}
+
+	/**
+	 * 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);
+	}
+	
+	public synchronized ClientServerHello getVersionInfo() throws IOException {
+		if (tm == null)
+			throw new IllegalStateException(
+					"Cannot get details of connection, you need to establish a connection first.");
+		return tm.getVersionInfo();
+	}
+
+
+	/**
+	 * 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 < 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 > 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);
+					}
+				};
+			}
+		}
+	}
+
+	/**
+	 * 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();
+	}
+}
diff --git a/src/com/trilead/ssh2/ConnectionInfo.java b/src/main/java/com/trilead/ssh2/ConnectionInfo.java
similarity index 96%
rename from src/com/trilead/ssh2/ConnectionInfo.java
rename to src/main/java/com/trilead/ssh2/ConnectionInfo.java
index 34d9453..b0ddbcf 100644
--- a/src/com/trilead/ssh2/ConnectionInfo.java
+++ b/src/main/java/com/trilead/ssh2/ConnectionInfo.java
@@ -1,53 +1,53 @@
-
-package com.trilead.ssh2;
-
-/**
- * In most cases you probably do not need the information contained in here.
- * 
- * @author Christian Plattner, plattner at 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;
-}
+
+package com.trilead.ssh2;
+
+/**
+ * In most cases you probably do not need the information contained in here.
+ * 
+ * @author Christian Plattner, plattner at 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;
+}
diff --git a/src/com/trilead/ssh2/ConnectionMonitor.java b/src/main/java/com/trilead/ssh2/ConnectionMonitor.java
similarity index 97%
rename from src/com/trilead/ssh2/ConnectionMonitor.java
rename to src/main/java/com/trilead/ssh2/ConnectionMonitor.java
index d378be5..2f96d25 100644
--- a/src/com/trilead/ssh2/ConnectionMonitor.java
+++ b/src/main/java/com/trilead/ssh2/ConnectionMonitor.java
@@ -1,34 +1,34 @@
-
-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 at 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);
+
+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 at 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
diff --git a/src/com/trilead/ssh2/DHGexParameters.java b/src/main/java/com/trilead/ssh2/DHGexParameters.java
similarity index 96%
rename from src/com/trilead/ssh2/DHGexParameters.java
rename to src/main/java/com/trilead/ssh2/DHGexParameters.java
index 94afb30..a2945ee 100644
--- a/src/com/trilead/ssh2/DHGexParameters.java
+++ b/src/main/java/com/trilead/ssh2/DHGexParameters.java
@@ -1,121 +1,121 @@
-
-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 at 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 <= 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 <= 8192. Furthermore,
-	 * min_group_len <= pref_group_len <= 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;
-	}
-}
+
+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 at 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 <= 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 <= 8192. Furthermore,
+	 * min_group_len <= pref_group_len <= 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/DebugLogger.java b/src/main/java/com/trilead/ssh2/DebugLogger.java
similarity index 96%
rename from src/com/trilead/ssh2/DebugLogger.java
rename to src/main/java/com/trilead/ssh2/DebugLogger.java
index 731fa28..adf2c8e 100644
--- a/src/com/trilead/ssh2/DebugLogger.java
+++ b/src/main/java/com/trilead/ssh2/DebugLogger.java
@@ -1,23 +1,23 @@
-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 at 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);	
-}
+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 at 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);	
+}
diff --git a/src/com/trilead/ssh2/HTTPProxyData.java b/src/main/java/com/trilead/ssh2/HTTPProxyData.java
similarity index 97%
rename from src/com/trilead/ssh2/HTTPProxyData.java
rename to src/main/java/com/trilead/ssh2/HTTPProxyData.java
index d99c28e..2edffe6 100644
--- a/src/com/trilead/ssh2/HTTPProxyData.java
+++ b/src/main/java/com/trilead/ssh2/HTTPProxyData.java
@@ -1,83 +1,83 @@
-
-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 at 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;
-	}
-}
+
+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 at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/HTTPProxyException.java b/src/main/java/com/trilead/ssh2/HTTPProxyException.java
similarity index 96%
rename from src/com/trilead/ssh2/HTTPProxyException.java
rename to src/main/java/com/trilead/ssh2/HTTPProxyException.java
index 3447be2..4687dd1 100644
--- a/src/com/trilead/ssh2/HTTPProxyException.java
+++ b/src/main/java/com/trilead/ssh2/HTTPProxyException.java
@@ -1,29 +1,29 @@
-
-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 at 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;
-	}
-}
+
+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 at 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;
+	}
+}
diff --git a/src/main/java/com/trilead/ssh2/IOWarningException.java b/src/main/java/com/trilead/ssh2/IOWarningException.java
new file mode 100644
index 0000000..3aebdd6
--- /dev/null
+++ b/src/main/java/com/trilead/ssh2/IOWarningException.java
@@ -0,0 +1,17 @@
+package com.trilead.ssh2;
+
+import java.io.IOException;
+
+/**
+ * @author Thomas Singer
+ */
+public final class IOWarningException extends IOException {
+
+    private static final long serialVersionUID = 1L;
+    
+	// Setup ==================================================================
+
+    public IOWarningException(String message) {
+		super(message);
+	}
+}
diff --git a/src/com/trilead/ssh2/InteractiveCallback.java b/src/main/java/com/trilead/ssh2/InteractiveCallback.java
similarity index 97%
rename from src/com/trilead/ssh2/InteractiveCallback.java
rename to src/main/java/com/trilead/ssh2/InteractiveCallback.java
index b81bb9f..3b83417 100644
--- a/src/com/trilead/ssh2/InteractiveCallback.java
+++ b/src/main/java/com/trilead/ssh2/InteractiveCallback.java
@@ -1,55 +1,55 @@
-
-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 at 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;
-}
+
+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 at 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;
+}
diff --git a/src/com/trilead/ssh2/KnownHosts.java b/src/main/java/com/trilead/ssh2/KnownHosts.java
similarity index 89%
rename from src/com/trilead/ssh2/KnownHosts.java
rename to src/main/java/com/trilead/ssh2/KnownHosts.java
index edca0a2..de56ca2 100644
--- a/src/com/trilead/ssh2/KnownHosts.java
+++ b/src/main/java/com/trilead/ssh2/KnownHosts.java
@@ -1,844 +1,860 @@
-
-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.SecureRandom;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Vector;
-
-import com.trilead.ssh2.crypto.Base64;
-import com.trilead.ssh2.crypto.digest.Digest;
-import com.trilead.ssh2.crypto.digest.HMAC;
-import com.trilead.ssh2.crypto.digest.MD5;
-import com.trilead.ssh2.crypto.digest.SHA1;
-import com.trilead.ssh2.signature.DSAPublicKey;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.RSAPublicKey;
-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 at 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;
-		Object key;
-
-		KnownHostsEntry(String[] patterns, Object key)
-		{
-			this.patterns = patterns;
-			this.key = key;
-		}
-	}
-
-	private LinkedList publicKeys = new LinkedList();
-
-	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
-			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)
-	{
-		SHA1 sha1 = new SHA1();
-
-		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)
-	{
-		SHA1 sha1 = new SHA1();
-
-		if (salt.length != sha1.getDigestLength())
-			throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")");
-
-		HMAC hmac = new HMAC(sha1, salt, salt.length);
-
-		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());
-		}
-		
-		byte[] dig = new byte[hmac.getDigestLength()];
-
-		hmac.digest(dig);
-
-		return dig;
-	}
-
-	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;
-		}
-
-		SHA1 sha1 = new SHA1();
-
-		if (salt.length != sha1.getDigestLength())
-			return false;
-
-		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, Object remoteKey)
-	{
-		int result = HOSTKEY_IS_NEW;
-
-		synchronized (publicKeys)
-		{
-			Iterator i = publicKeys.iterator();
-			
-			while (i.hasNext())
-			{
-				KnownHostsEntry ke = (KnownHostsEntry) 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 getAllKeys(String hostname)
-	{
-		Vector keys = new Vector();
-
-		synchronized (publicKeys)
-		{
-			Iterator i = publicKeys.iterator();
-
-			while (i.hasNext())
-			{
-				KnownHostsEntry ke = (KnownHostsEntry) 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();
-
-		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();
-
-				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(Object key1, Object key2)
-	{
-		if ((key1 instanceof RSAPublicKey) && (key2 instanceof RSAPublicKey))
-		{
-			RSAPublicKey savedRSAKey = (RSAPublicKey) key1;
-			RSAPublicKey remoteRSAKey = (RSAPublicKey) key2;
-
-			if (savedRSAKey.getE().equals(remoteRSAKey.getE()) == false)
-				return false;
-
-			if (savedRSAKey.getN().equals(remoteRSAKey.getN()) == false)
-				return false;
-
-			return true;
-		}
-
-		if ((key1 instanceof DSAPublicKey) && (key2 instanceof DSAPublicKey))
-		{
-			DSAPublicKey savedDSAKey = (DSAPublicKey) key1;
-			DSAPublicKey remoteDSAKey = (DSAPublicKey) key2;
-
-			if (savedDSAKey.getG().equals(remoteDSAKey.getG()) == false)
-				return false;
-
-			if (savedDSAKey.getP().equals(remoteDSAKey.getP()) == false)
-				return false;
-
-			if (savedDSAKey.getQ().equals(remoteDSAKey.getQ()) == false)
-				return false;
-
-			if (savedDSAKey.getY().equals(remoteDSAKey.getY()) == false)
-				return false;
-
-			return true;
-		}
-
-		return false;
-	}
-
-	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 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
-	{
-		Object remoteKey = null;
-
-		if ("ssh-rsa".equals(serverHostKeyAlgorithm))
-		{
-			remoteKey = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
-		}
-		else if ("ssh-dss".equals(serverHostKeyAlgorithm))
-		{
-			remoteKey = DSASHA1Verify.decodeSSHDSAPublicKey(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)
-	{
-		Digest dig = null;
-
-		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
-			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()];
-		dig.digest(res);
-		return res;
-	}
-
-	/**
-	 * 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);
-	}
-}
+
+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.Reader;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Vector;
+
+import com.trilead.ssh2.crypto.Base64;
+import com.trilead.ssh2.crypto.digest.Digest;
+import com.trilead.ssh2.crypto.digest.HMAC;
+import com.trilead.ssh2.crypto.digest.MD5;
+import com.trilead.ssh2.crypto.digest.SHA1;
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.signature.DSAPublicKey;
+import com.trilead.ssh2.signature.DSASHA1Verify;
+import com.trilead.ssh2.signature.RSAPublicKey;
+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 at trilead.com
+ * @version $Id: KnownHosts.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+
+public class KnownHosts
+{
+	private static final Logger LOGGER = Logger.getLogger(KnownHosts.class);
+
+	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;
+		Object key;
+
+		KnownHostsEntry(String[] patterns, Object key)
+		{
+			this.patterns = patterns;
+			this.key = key;
+		}
+	}
+
+	private LinkedList publicKeys = new LinkedList();
+
+	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)) {
+			final RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
+
+			synchronized (publicKeys) {
+				publicKeys.add(new KnownHostsEntry(hostnames, rpk));
+			}
+		}
+		else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
+			final DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
+
+			synchronized (publicKeys) {
+				publicKeys.add(new KnownHostsEntry(hostnames, dpk));
+			}
+		}
+		else {
+			throw new IOWarningException("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)
+	{
+		SHA1 sha1 = new SHA1();
+
+		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)
+	{
+		SHA1 sha1 = new SHA1();
+
+		if (salt.length != sha1.getDigestLength())
+			throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")");
+
+		HMAC hmac = new HMAC(sha1, salt, salt.length);
+
+		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());
+		}
+		
+		byte[] dig = new byte[hmac.getDigestLength()];
+
+		hmac.digest(dig);
+
+		return dig;
+	}
+
+	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;
+		}
+
+		SHA1 sha1 = new SHA1();
+
+		if (salt.length != sha1.getDigestLength())
+			return false;
+
+		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, Object remoteKey)
+	{
+		int result = HOSTKEY_IS_NEW;
+
+		synchronized (publicKeys)
+		{
+			Iterator i = publicKeys.iterator();
+			
+			while (i.hasNext())
+			{
+				KnownHostsEntry ke = (KnownHostsEntry) 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 getAllKeys(String hostname)
+	{
+		Vector keys = new Vector();
+
+		synchronized (publicKeys)
+		{
+			Iterator i = publicKeys.iterator();
+
+			while (i.hasNext())
+			{
+				KnownHostsEntry ke = (KnownHostsEntry) 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();
+
+		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();
+
+				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;
+				}
+				else
+				{
+					final int indexColon = pattern.indexOf(':');
+					final int indexLastColon = pattern.indexOf(':');
+					if (indexColon > 0 && indexColon < pattern.length() - 2 && indexColon == indexLastColon)
+					{
+						final String bracketizedHost = '[' + hostname + ']';
+						if (pattern.startsWith(bracketizedHost))
+						{
+							if (negate)
+								return false;
+							isMatch = true;
+						}
+					}
+				}
+			}
+		}
+
+		return isMatch;
+	}
+
+	private void initialize(char[] knownHostsData) throws IOException {
+		final BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
+		for (String line = br.readLine(); line != null; line = br.readLine()) {
+			line = line.trim();
+			if (line.startsWith("#")) {
+				continue;
+			}
+
+			final String[] arr = line.split(" "); 
+			if (arr.length < 3) {
+				continue;
+			}
+
+			final String serverHostKeyAlgorithm = arr[1];
+			if (!"ssh-rsa".equals(serverHostKeyAlgorithm) && !"ssh-dss".equals(serverHostKeyAlgorithm)) {
+				continue;
+			}
+
+			final String[] hostnames = arr[0].split(",");
+			final byte[] msg = Base64.decode(arr[2].toCharArray());
+
+			try {
+				addHostkey(hostnames, serverHostKeyAlgorithm, msg);
+			}
+			catch (IOWarningException ex) {
+				LOGGER.log(20, "Ignored invalid line '" + line + "'");
+			}
+		}
+	}
+
+	private void initialize(File knownHosts) throws IOException {
+		final char[] buffer = new char[512];
+
+		final CharArrayWriter charWriter = new CharArrayWriter();
+
+		knownHosts.createNewFile();
+
+		final Reader reader = new FileReader(knownHosts);
+		try {
+			while (true) {
+				final int readCharCount = reader.read(buffer);
+				if (readCharCount < 0) {
+					break;
+				}
+
+				charWriter.write(buffer, 0, readCharCount);
+			}
+		}
+		finally {
+			reader.close();
+		}
+
+		initialize(charWriter.toCharArray());
+	}
+
+	private final boolean matchKeys(Object key1, Object key2)
+	{
+		if ((key1 instanceof RSAPublicKey) && (key2 instanceof RSAPublicKey))
+		{
+			RSAPublicKey savedRSAKey = (RSAPublicKey) key1;
+			RSAPublicKey remoteRSAKey = (RSAPublicKey) key2;
+
+			if (savedRSAKey.getE().equals(remoteRSAKey.getE()) == false)
+				return false;
+
+			if (savedRSAKey.getN().equals(remoteRSAKey.getN()) == false)
+				return false;
+
+			return true;
+		}
+
+		if ((key1 instanceof DSAPublicKey) && (key2 instanceof DSAPublicKey))
+		{
+			DSAPublicKey savedDSAKey = (DSAPublicKey) key1;
+			DSAPublicKey remoteDSAKey = (DSAPublicKey) key2;
+
+			if (savedDSAKey.getG().equals(remoteDSAKey.getG()) == false)
+				return false;
+
+			if (savedDSAKey.getP().equals(remoteDSAKey.getP()) == false)
+				return false;
+
+			if (savedDSAKey.getQ().equals(remoteDSAKey.getQ()) == false)
+				return false;
+
+			if (savedDSAKey.getY().equals(remoteDSAKey.getY()) == false)
+				return false;
+
+			return true;
+		}
+
+		return false;
+	}
+
+	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 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
+	{
+		Object remoteKey = null;
+
+		if ("ssh-rsa".equals(serverHostKeyAlgorithm))
+		{
+			remoteKey = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
+		}
+		else if ("ssh-dss".equals(serverHostKeyAlgorithm))
+		{
+			remoteKey = DSASHA1Verify.decodeSSHDSAPublicKey(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)
+	{
+		Digest dig = null;
+
+		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
+			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()];
+		dig.digest(res);
+		return res;
+	}
+
+	/**
+	 * 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);
+	}
+}
diff --git a/src/com/trilead/ssh2/LocalPortForwarder.java b/src/main/java/com/trilead/ssh2/LocalPortForwarder.java
similarity index 96%
rename from src/com/trilead/ssh2/LocalPortForwarder.java
rename to src/main/java/com/trilead/ssh2/LocalPortForwarder.java
index c3183cb..96fa082 100644
--- a/src/com/trilead/ssh2/LocalPortForwarder.java
+++ b/src/main/java/com/trilead/ssh2/LocalPortForwarder.java
@@ -1,63 +1,63 @@
-
-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 at 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();
-	}
-}
+
+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 at 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();
+	}
+}
diff --git a/src/com/trilead/ssh2/LocalStreamForwarder.java b/src/main/java/com/trilead/ssh2/LocalStreamForwarder.java
similarity index 96%
rename from src/com/trilead/ssh2/LocalStreamForwarder.java
rename to src/main/java/com/trilead/ssh2/LocalStreamForwarder.java
index a841976..7899367 100644
--- a/src/com/trilead/ssh2/LocalStreamForwarder.java
+++ b/src/main/java/com/trilead/ssh2/LocalStreamForwarder.java
@@ -1,78 +1,78 @@
-
-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 at 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);
-	}
-}
+
+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 at 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);
+	}
+}
diff --git a/src/com/trilead/ssh2/ProxyData.java b/src/main/java/com/trilead/ssh2/ProxyData.java
similarity index 95%
rename from src/com/trilead/ssh2/ProxyData.java
rename to src/main/java/com/trilead/ssh2/ProxyData.java
index 3298a9d..059a6e3 100644
--- a/src/com/trilead/ssh2/ProxyData.java
+++ b/src/main/java/com/trilead/ssh2/ProxyData.java
@@ -1,15 +1,15 @@
-
-package com.trilead.ssh2;
-
-/**
- * An abstract marker interface implemented by all proxy data implementations.
- * 
- * @see HTTPProxyData
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: ProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public abstract interface ProxyData
-{
-}
+
+package com.trilead.ssh2;
+
+/**
+ * An abstract marker interface implemented by all proxy data implementations.
+ * 
+ * @see HTTPProxyData
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: ProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public abstract interface ProxyData
+{
+}
diff --git a/src/com/trilead/ssh2/SCPClient.java b/src/main/java/com/trilead/ssh2/SCPClient.java
similarity index 96%
rename from src/com/trilead/ssh2/SCPClient.java
rename to src/main/java/com/trilead/ssh2/SCPClient.java
index 8ea248a..b692750 100644
--- a/src/com/trilead/ssh2/SCPClient.java
+++ b/src/main/java/com/trilead/ssh2/SCPClient.java
@@ -1,729 +1,729 @@
-
-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 at 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();
-		}
-	}
-}
+
+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 at 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();
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/SFTPException.java b/src/main/java/com/trilead/ssh2/SFTPException.java
similarity index 95%
rename from src/com/trilead/ssh2/SFTPException.java
rename to src/main/java/com/trilead/ssh2/SFTPException.java
index f3b6d3e..d97723f 100644
--- a/src/com/trilead/ssh2/SFTPException.java
+++ b/src/main/java/com/trilead/ssh2/SFTPException.java
@@ -1,91 +1,91 @@
-
-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 at 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];
-	}
-}
+
+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 at 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];
+	}
+}
diff --git a/src/com/trilead/ssh2/SFTPv3Client.java b/src/main/java/com/trilead/ssh2/SFTPv3Client.java
similarity index 96%
rename from src/com/trilead/ssh2/SFTPv3Client.java
rename to src/main/java/com/trilead/ssh2/SFTPv3Client.java
index be2fa1c..06796e9 100644
--- a/src/com/trilead/ssh2/SFTPv3Client.java
+++ b/src/main/java/com/trilead/ssh2/SFTPv3Client.java
@@ -1,1388 +1,1388 @@
-
-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 at 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.
-	 */
-	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 = new Long(tr.readUINT64());
-		}
-
-		if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0)
-		{
-			if (debug != null)
-				debug.println("SSH_FILEXFER_ATTR_V3_UIDGID");
-			fa.uid = new Integer(tr.readUINT32());
-			fa.gid = new Integer(tr.readUINT32());
-		}
-
-		if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0)
-		{
-			if (debug != null)
-				debug.println("SSH_FILEXFER_ATTR_PERMISSIONS");
-			fa.permissions = new Integer(tr.readUINT32());
-		}
-
-		if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0)
-		{
-			if (debug != null)
-				debug.println("SSH_FILEXFER_ATTR_V3_ACMODTIME");
-			fa.atime = new Long(((long)tr.readUINT32()) & 0xffffffffl);
-			fa.mtime = new Long(((long)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 < len <= 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> > 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;
-		}
-	}
-}
+
+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 at 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.
+	 */
+	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 = new Long(tr.readUINT64());
+		}
+
+		if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0)
+		{
+			if (debug != null)
+				debug.println("SSH_FILEXFER_ATTR_V3_UIDGID");
+			fa.uid = new Integer(tr.readUINT32());
+			fa.gid = new Integer(tr.readUINT32());
+		}
+
+		if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0)
+		{
+			if (debug != null)
+				debug.println("SSH_FILEXFER_ATTR_PERMISSIONS");
+			fa.permissions = new Integer(tr.readUINT32());
+		}
+
+		if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0)
+		{
+			if (debug != null)
+				debug.println("SSH_FILEXFER_ATTR_V3_ACMODTIME");
+			fa.atime = new Long(((long)tr.readUINT32()) & 0xffffffffl);
+			fa.mtime = new Long(((long)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 < len <= 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> > 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;
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/SFTPv3DirectoryEntry.java b/src/main/java/com/trilead/ssh2/SFTPv3DirectoryEntry.java
similarity index 96%
rename from src/com/trilead/ssh2/SFTPv3DirectoryEntry.java
rename to src/main/java/com/trilead/ssh2/SFTPv3DirectoryEntry.java
index 370aefa..669ba87 100644
--- a/src/com/trilead/ssh2/SFTPv3DirectoryEntry.java
+++ b/src/main/java/com/trilead/ssh2/SFTPv3DirectoryEntry.java
@@ -1,38 +1,38 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>SFTPv3DirectoryEntry</code> as returned by {@link SFTPv3Client#ls(String)}.
- * 
- * @author Christian Plattner, plattner at 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;
-}
+
+package com.trilead.ssh2;
+
+/**
+ * A <code>SFTPv3DirectoryEntry</code> as returned by {@link SFTPv3Client#ls(String)}.
+ * 
+ * @author Christian Plattner, plattner at 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;
+}
diff --git a/src/com/trilead/ssh2/SFTPv3FileAttributes.java b/src/main/java/com/trilead/ssh2/SFTPv3FileAttributes.java
similarity index 96%
rename from src/com/trilead/ssh2/SFTPv3FileAttributes.java
rename to src/main/java/com/trilead/ssh2/SFTPv3FileAttributes.java
index 56c9c87..7b1d321 100644
--- a/src/com/trilead/ssh2/SFTPv3FileAttributes.java
+++ b/src/main/java/com/trilead/ssh2/SFTPv3FileAttributes.java
@@ -1,145 +1,145 @@
-
-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 at 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();
-	}
-}
+
+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 at 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();
+	}
+}
diff --git a/src/com/trilead/ssh2/SFTPv3FileHandle.java b/src/main/java/com/trilead/ssh2/SFTPv3FileHandle.java
similarity index 95%
rename from src/com/trilead/ssh2/SFTPv3FileHandle.java
rename to src/main/java/com/trilead/ssh2/SFTPv3FileHandle.java
index c8b5477..9b3dbb6 100644
--- a/src/com/trilead/ssh2/SFTPv3FileHandle.java
+++ b/src/main/java/com/trilead/ssh2/SFTPv3FileHandle.java
@@ -1,45 +1,45 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>SFTPv3FileHandle</code>.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2;
+
+/**
+ * A <code>SFTPv3FileHandle</code>.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/ServerHostKeyVerifier.java b/src/main/java/com/trilead/ssh2/ServerHostKeyVerifier.java
similarity index 97%
rename from src/com/trilead/ssh2/ServerHostKeyVerifier.java
rename to src/main/java/com/trilead/ssh2/ServerHostKeyVerifier.java
index 1c33454..ac65955 100644
--- a/src/com/trilead/ssh2/ServerHostKeyVerifier.java
+++ b/src/main/java/com/trilead/ssh2/ServerHostKeyVerifier.java
@@ -1,31 +1,31 @@
-
-package com.trilead.ssh2;
-
-/**
- * A callback interface used to implement a client specific method of checking
- * server host keys.
- * 
- * @author Christian Plattner, plattner at 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;
-}
+
+package com.trilead.ssh2;
+
+/**
+ * A callback interface used to implement a client specific method of checking
+ * server host keys.
+ * 
+ * @author Christian Plattner, plattner at 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;
+}
diff --git a/src/com/trilead/ssh2/Session.java b/src/main/java/com/trilead/ssh2/Session.java
similarity index 96%
rename from src/com/trilead/ssh2/Session.java
rename to src/main/java/com/trilead/ssh2/Session.java
index 4784537..4a74916 100644
--- a/src/com/trilead/ssh2/Session.java
+++ b/src/main/java/com/trilead/ssh2/Session.java
@@ -1,479 +1,479 @@
-
-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 at 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);
-	}
-
-	/**
-	 * 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);
-	}
-	
-	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)
-			{
-			}
-		}
-	}
-}
+
+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 at 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);
+	}
+
+	/**
+	 * 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);
+	}
+	
+	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)
+			{
+			}
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/StreamGobbler.java b/src/main/java/com/trilead/ssh2/StreamGobbler.java
similarity index 95%
rename from src/com/trilead/ssh2/StreamGobbler.java
rename to src/main/java/com/trilead/ssh2/StreamGobbler.java
index 1e87bb1..e93c388 100644
--- a/src/com/trilead/ssh2/StreamGobbler.java
+++ b/src/main/java/com/trilead/ssh2/StreamGobbler.java
@@ -1,229 +1,229 @@
-
-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 at 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;
-		}
-	}
-}
+
+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 at 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;
+		}
+	}
+}
diff --git a/src/main/java/com/trilead/ssh2/auth/AgentIdentity.java b/src/main/java/com/trilead/ssh2/auth/AgentIdentity.java
new file mode 100644
index 0000000..e6c6961
--- /dev/null
+++ b/src/main/java/com/trilead/ssh2/auth/AgentIdentity.java
@@ -0,0 +1,7 @@
+package com.trilead.ssh2.auth;
+
+public interface AgentIdentity {
+    public String getAlgName();
+    public byte[] getPublicKeyBlob();
+    public byte[] sign(byte[] data);
+}
diff --git a/src/main/java/com/trilead/ssh2/auth/AgentProxy.java b/src/main/java/com/trilead/ssh2/auth/AgentProxy.java
new file mode 100644
index 0000000..2e66f72
--- /dev/null
+++ b/src/main/java/com/trilead/ssh2/auth/AgentProxy.java
@@ -0,0 +1,7 @@
+package com.trilead.ssh2.auth;
+
+import java.util.Collection;
+
+public interface AgentProxy {
+    public Collection/*<AgentIdentity>*/ getIdentities();
+}
diff --git a/src/com/trilead/ssh2/auth/AuthenticationManager.java b/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java
similarity index 82%
rename from src/com/trilead/ssh2/auth/AuthenticationManager.java
rename to src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java
index 894a31f..90c0218 100644
--- a/src/com/trilead/ssh2/auth/AuthenticationManager.java
+++ b/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java
@@ -1,419 +1,471 @@
-
-package com.trilead.ssh2.auth;
-
-import java.io.IOException;
-import java.security.SecureRandom;
-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.DSAPrivateKey;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.DSASignature;
-import com.trilead.ssh2.signature.RSAPrivateKey;
-import com.trilead.ssh2.signature.RSASHA1Verify;
-import com.trilead.ssh2.signature.RSASignature;
-import com.trilead.ssh2.transport.MessageHandler;
-import com.trilead.ssh2.transport.TransportManager;
-
-
-/**
- * AuthenticationManager.
- * 
- * @author Christian Plattner, plattner at 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
-	{
-		try
-		{
-			initialize(user);
-
-			if (methodPossible("publickey") == false)
-				throw new IOException("Authentication method publickey not supported by the server at this stage.");
-
-			Object key = PEMDecoder.decode(PEMPrivateKey, password);
-
-			if (key instanceof DSAPrivateKey)
-			{
-				DSAPrivateKey pk = (DSAPrivateKey) key;
-
-				byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());
-
-				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();
-
-				DSASignature 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(pk.getPublicKey());
-
-				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();
-
-				RSASignature 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
-			{
-				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)
-		{
-			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.");
-			}
-		}
-	}
-}
+
+package com.trilead.ssh2.auth;
+
+import com.trilead.ssh2.InteractiveCallback;
+import com.trilead.ssh2.crypto.PEMDecoder;
+import com.trilead.ssh2.packets.*;
+import com.trilead.ssh2.signature.*;
+import com.trilead.ssh2.transport.MessageHandler;
+import com.trilead.ssh2.transport.TransportManager;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Iterator;
+import java.util.Vector;
+
+
+/**
+ * AuthenticationManager.
+ * 
+ * @author Christian Plattner, plattner at 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, AgentProxy proxy) throws IOException {
+		initialize(user);
+
+		boolean success = false;
+		Iterator agentIdentities = proxy.getIdentities().iterator();
+		while(agentIdentities.hasNext()) {
+			AgentIdentity identity = (AgentIdentity)agentIdentities.next();
+			success = authenticatePublicKey(user, proxy, identity);
+			if(success) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	boolean authenticatePublicKey(String user, AgentProxy proxy, AgentIdentity identity) throws IOException {
+
+		if (methodPossible("publickey") == false)
+			throw new IOException("Authentication method publickey not supported by the server at this stage.");
+
+		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[] 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] + ")");
+	}
+
+
+	public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
+			throws IOException
+	{
+		try
+		{
+			initialize(user);
+
+			if (methodPossible("publickey") == false)
+				throw new IOException("Authentication method publickey not supported by the server at this stage.");
+
+			Object key = PEMDecoder.decode(PEMPrivateKey, password);
+
+			if (key instanceof DSAPrivateKey)
+			{
+				DSAPrivateKey pk = (DSAPrivateKey) key;
+
+				byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());
+
+				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();
+
+				DSASignature 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(pk.getPublicKey());
+
+				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();
+
+				RSASignature 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
+			{
+				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)
+		{
+			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.");
+			}
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/channel/Channel.java b/src/main/java/com/trilead/ssh2/channel/Channel.java
similarity index 96%
rename from src/com/trilead/ssh2/channel/Channel.java
rename to src/main/java/com/trilead/ssh2/channel/Channel.java
index 4b19a48..8365f12 100644
--- a/src/com/trilead/ssh2/channel/Channel.java
+++ b/src/main/java/com/trilead/ssh2/channel/Channel.java
@@ -1,207 +1,207 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * Channel.
- * 
- * @author Christian Plattner, plattner at 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;
-		}
-	}
-}
+
+package com.trilead.ssh2.channel;
+
+/**
+ * Channel.
+ * 
+ * @author Christian Plattner, plattner at 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;
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/channel/ChannelInputStream.java b/src/main/java/com/trilead/ssh2/channel/ChannelInputStream.java
similarity index 94%
rename from src/com/trilead/ssh2/channel/ChannelInputStream.java
rename to src/main/java/com/trilead/ssh2/channel/ChannelInputStream.java
index a6d936f..f88522c 100644
--- a/src/com/trilead/ssh2/channel/ChannelInputStream.java
+++ b/src/main/java/com/trilead/ssh2/channel/ChannelInputStream.java
@@ -1,86 +1,86 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * ChannelInputStream.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * ChannelInputStream.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/channel/ChannelManager.java b/src/main/java/com/trilead/ssh2/channel/ChannelManager.java
similarity index 96%
rename from src/com/trilead/ssh2/channel/ChannelManager.java
rename to src/main/java/com/trilead/ssh2/channel/ChannelManager.java
index ebd7585..c33001e 100644
--- a/src/com/trilead/ssh2/channel/ChannelManager.java
+++ b/src/main/java/com/trilead/ssh2/channel/ChannelManager.java
@@ -1,1676 +1,1682 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Vector;
-
-import com.trilead.ssh2.ChannelCondition;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.PacketChannelOpenConfirmation;
-import com.trilead.ssh2.packets.PacketChannelOpenFailure;
-import com.trilead.ssh2.packets.PacketChannelTrileadPing;
-import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest;
-import com.trilead.ssh2.packets.PacketGlobalForwardRequest;
-import com.trilead.ssh2.packets.PacketGlobalTrileadPing;
-import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel;
-import com.trilead.ssh2.packets.PacketOpenSessionChannel;
-import com.trilead.ssh2.packets.PacketSessionExecCommand;
-import com.trilead.ssh2.packets.PacketSessionPtyRequest;
-import com.trilead.ssh2.packets.PacketSessionStartShell;
-import com.trilead.ssh2.packets.PacketSessionSubsystemRequest;
-import com.trilead.ssh2.packets.PacketSessionX11Request;
-import com.trilead.ssh2.packets.Packets;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.transport.MessageHandler;
-import com.trilead.ssh2.transport.TransportManager;
-
-/**
- * ChannelManager. Please read the comments in Channel.java.
- * <p>
- * Besides the crypto part, this is the core of the library.
- * 
- * @author Christian Plattner, plattner at 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 x11_magic_cookies = new HashMap();
-
-	private TransportManager tm;
-
-	private Vector channels = new Vector();
-	private int nextLocalChannel = 100;
-	private boolean shutdown = false;
-	private int globalSuccessCounter = 0;
-	private int globalFailedCounter = 0;
-
-	private HashMap remoteForwardings = new HashMap();
-
-	private Vector listenerThreads = new Vector();
-
-	private boolean listenerThreadsAllowed = true;
-
-	public ChannelManager(TransportManager tm)
-	{
-		this.tm = tm;
-		tm.registerMessageHandler(this, 80, 100);
-	}
-
-	private Channel getChannel(int id)
-	{
-		synchronized (channels)
-		{
-			for (int i = 0; i < channels.size(); i++)
-			{
-				Channel c = (Channel) channels.elementAt(i);
-				if (c.localID == id)
-					return c;
-			}
-		}
-		return null;
-	}
-
-	private void removeChannel(int id)
-	{
-		synchronized (channels)
-		{
-			for (int i = 0; i < channels.size(); i++)
-			{
-				Channel c = (Channel) channels.elementAt(i);
-				if (c.localID == id)
-				{
-					channels.removeElementAt(i);
-					break;
-				}
-			}
-		}
-	}
-
-	private int addChannel(Channel c)
-	{
-		synchronized (channels)
-		{
-			channels.addElement(c);
-			return nextLocalChannel++;
-		}
-	}
-
-	private void waitUntilChannelOpen(Channel c) throws IOException
-	{
-		synchronized (c)
-		{
-			while (c.state == Channel.STATE_OPENING)
-			{
-				try
-				{
-					c.wait();
-				}
-				catch (InterruptedException ignore)
-				{
-				}
-			}
-
-			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_copy;
-
-		synchronized (channels)
-		{
-			channel_copy = (Vector) channels.clone();
-		}
-
-		for (int i = 0; i < channel_copy.size(); i++)
-		{
-			Channel c = (Channel) channel_copy.elementAt(i);
-
-			synchronized (c)
-			{
-				if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
-					continue;
-			}
-
-			try
-			{
-				closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
-			}
-			catch (IOException e)
-			{
-			}
-		}
-	}
-
-	public X11ServerData checkX11Cookie(String hexFakeCookie)
-	{
-		synchronized (x11_magic_cookies)
-		{
-			if (hexFakeCookie != null)
-				return (X11ServerData) x11_magic_cookies.get(hexFakeCookie);
-		}
-		return null;
-	}
-
-	public void closeAllChannels()
-	{
-		if (log.isEnabled())
-			log.log(50, "Closing all channels");
-
-		Vector channel_copy;
-
-		synchronized (channels)
-		{
-			channel_copy = (Vector) channels.clone();
-		}
-
-		for (int i = 0; i < channel_copy.size(); i++)
-		{
-			Channel c = (Channel) channel_copy.elementAt(i);
-			try
-			{
-				closeChannel(c, "Closing all channels", true);
-			}
-			catch (IOException e)
-			{
-			}
-		}
-	}
-
-	public void closeChannel(Channel c, String reason, boolean force) throws IOException
-	{
-		byte msg[] = new byte[5];
-
-		synchronized (c)
-		{
-			if (force)
-			{
-				c.state = Channel.STATE_CLOSED;
-				c.EOF = true;
-			}
-
-			c.setReasonClosed(reason);
-
-			msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
-			msg[1] = (byte) (c.remoteID >> 24);
-			msg[2] = (byte) (c.remoteID >> 16);
-			msg[3] = (byte) (c.remoteID >> 8);
-			msg[4] = (byte) (c.remoteID);
-
-			c.notifyAll();
-		}
-
-		synchronized (c.channelSendLock)
-		{
-			if (c.closeMessageSent == true)
-				return;
-			tm.sendMessage(msg);
-			c.closeMessageSent = true;
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
-	}
-
-	public void sendEOF(Channel c) throws IOException
-	{
-		byte[] msg = new byte[5];
-
-		synchronized (c)
-		{
-			if (c.state != Channel.STATE_OPEN)
-				return;
-
-			msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
-			msg[1] = (byte) (c.remoteID >> 24);
-			msg[2] = (byte) (c.remoteID >> 16);
-			msg[3] = (byte) (c.remoteID >> 8);
-			msg[4] = (byte) (c.remoteID);
-		}
-
-		synchronized (c.channelSendLock)
-		{
-			if (c.closeMessageSent == true)
-				return;
-			tm.sendMessage(msg);
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
-	}
-
-	public void sendOpenConfirmation(Channel c) throws IOException
-	{
-		PacketChannelOpenConfirmation pcoc = null;
-
-		synchronized (c)
-		{
-			if (c.state != Channel.STATE_OPENING)
-				return;
-
-			c.state = Channel.STATE_OPEN;
-
-			pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
-		}
-
-		synchronized (c.channelSendLock)
-		{
-			if (c.closeMessageSent == true)
-				return;
-			tm.sendMessage(pcoc.getPayload());
-		}
-	}
-
-	public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException
-	{
-		while (len > 0)
-		{
-			int thislen = 0;
-			byte[] msg;
-
-			synchronized (c)
-			{
-				while (true)
-				{
-					if (c.state == Channel.STATE_CLOSED)
-						throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")");
-
-					if (c.state != Channel.STATE_OPEN)
-						throw new IOException("SSH channel in strange state. (" + c.state + ")");
-
-					if (c.remoteWindow != 0)
-						break;
-
-					try
-					{
-						c.wait();
-					}
-					catch (InterruptedException ignore)
-					{
-					}
-				}
-
-				/* len > 0, no sign extension can happen when comparing */
-
-				thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;
-
-				int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);
-
-				/* The worst case scenario =) a true bottleneck */
-
-				if (estimatedMaxDataLen <= 0)
-				{
-					estimatedMaxDataLen = 1;
-				}
-
-				if (thislen > estimatedMaxDataLen)
-					thislen = estimatedMaxDataLen;
-
-				c.remoteWindow -= thislen;
-
-				msg = new byte[1 + 8 + thislen];
-
-				msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
-				msg[1] = (byte) (c.remoteID >> 24);
-				msg[2] = (byte) (c.remoteID >> 16);
-				msg[3] = (byte) (c.remoteID >> 8);
-				msg[4] = (byte) (c.remoteID);
-				msg[5] = (byte) (thislen >> 24);
-				msg[6] = (byte) (thislen >> 16);
-				msg[7] = (byte) (thislen >> 8);
-				msg[8] = (byte) (thislen);
-
-				System.arraycopy(buffer, pos, msg, 9, thislen);
-			}
-
-			synchronized (c.channelSendLock)
-			{
-				if (c.closeMessageSent == true)
-					throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")");
-
-				tm.sendMessage(msg);
-			}
-
-			pos += thislen;
-			len -= thislen;
-		}
-	}
-
-	public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
-			throws IOException
-	{
-		RemoteForwardingData rfd = new RemoteForwardingData();
-
-		rfd.bindAddress = bindAddress;
-		rfd.bindPort = bindPort;
-		rfd.targetAddress = targetAddress;
-		rfd.targetPort = targetPort;
-
-		synchronized (remoteForwardings)
-		{
-			Integer key = new Integer(bindPort);
-
-			if (remoteForwardings.get(key) != null)
-			{
-				throw new IOException("There is already a forwarding for remote port " + bindPort);
-			}
-
-			remoteForwardings.put(key, rfd);
-		}
-
-		synchronized (channels)
-		{
-			globalSuccessCounter = globalFailedCounter = 0;
-		}
-
-		PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
-		tm.sendMessage(pgf.getPayload());
-
-		if (log.isEnabled())
-			log.log(50, "Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
-
-		try
-		{
-			if (waitForGlobalRequestResult() == false)
-				throw new IOException("The server denied the request (did you enable port forwarding?)");
-		}
-		catch (IOException e)
-		{
-			synchronized (remoteForwardings)
-			{
-				remoteForwardings.remove(rfd);
-			}
-			throw e;
-		}
-
-		return bindPort;
-	}
-
-	public void requestCancelGlobalForward(int bindPort) throws IOException
-	{
-		RemoteForwardingData rfd = null;
-
-		synchronized (remoteForwardings)
-		{
-			rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(bindPort));
-
-			if (rfd == null)
-				throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
-		}
-
-		synchronized (channels)
-		{
-			globalSuccessCounter = globalFailedCounter = 0;
-		}
-
-		PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
-				rfd.bindPort);
-		tm.sendMessage(pgcf.getPayload());
-
-		if (log.isEnabled())
-			log.log(50, "Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
-
-		try
-		{
-			if (waitForGlobalRequestResult() == false)
-				throw new IOException("The server denied the request.");
-		}
-		finally
-		{
-			synchronized (remoteForwardings)
-			{
-				/* Only now we are sure that no more forwarded connections will arrive */
-				remoteForwardings.remove(rfd);
-			}
-		}
-
-	}
-
-	public void registerThread(IChannelWorkerThread thr) throws IOException
-	{
-		synchronized (listenerThreads)
-		{
-			if (listenerThreadsAllowed == false)
-				throw new IOException("Too late, this connection is closed.");
-			listenerThreads.addElement(thr);
-		}
-	}
-
-	public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
-			int originator_port) throws IOException
-	{
-		Channel c = new Channel(this);
-
-		synchronized (c)
-		{
-			c.localID = addChannel(c);
-			// end of synchronized block forces writing out to main memory
-		}
-
-		PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
-				c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);
-
-		tm.sendMessage(dtc.getPayload());
-
-		waitUntilChannelOpen(c);
-
-		return c;
-	}
-
-	public Channel openSessionChannel() throws IOException
-	{
-		Channel c = new Channel(this);
-
-		synchronized (c)
-		{
-			c.localID = addChannel(c);
-			// end of synchronized block forces the writing out to main memory
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");
-
-		PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
-		tm.sendMessage(smo.getPayload());
-
-		waitUntilChannelOpen(c);
-
-		return c;
-	}
-
-	public void requestGlobalTrileadPing() throws IOException
-	{
-		synchronized (channels)
-		{
-			globalSuccessCounter = globalFailedCounter = 0;
-		}
-
-		PacketGlobalTrileadPing pgtp = new PacketGlobalTrileadPing();
-
-		tm.sendMessage(pgtp.getPayload());
-
-		if (log.isEnabled())
-			log.log(50, "Sending SSH_MSG_GLOBAL_REQUEST 'trilead-ping'.");
-
-		try
-		{
-			if (waitForGlobalRequestResult() == true)
-				throw new IOException("Your server is alive - but buggy. "
-						+ "It replied with SSH_MSG_REQUEST_SUCCESS when it actually should not.");
-
-		}
-		catch (IOException e)
-		{
-			throw (IOException) new IOException("The ping request failed.").initCause(e);
-		}
-	}
-
-	public void requestChannelTrileadPing(Channel c) throws IOException
-	{
-		PacketChannelTrileadPing pctp;
-
-		synchronized (c)
-		{
-			if (c.state != Channel.STATE_OPEN)
-				throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
-
-			pctp = new PacketChannelTrileadPing(c.remoteID);
-
-			c.successCounter = c.failedCounter = 0;
-		}
-
-		synchronized (c.channelSendLock)
-		{
-			if (c.closeMessageSent)
-				throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
-			tm.sendMessage(pctp.getPayload());
-		}
-
-		try
-		{
-			if (waitForChannelRequestResult(c) == true)
-				throw new IOException("Your server is alive - but buggy. "
-						+ "It replied with SSH_MSG_SESSION_SUCCESS when it actually should not.");
-
-		}
-		catch (IOException e)
-		{
-			throw (IOException) new IOException("The ping request failed.").initCause(e);
-		}
-	}
-
-	public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
-			int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
-	{
-		PacketSessionPtyRequest spr;
-
-		synchronized (c)
-		{
-			if (c.state != Channel.STATE_OPEN)
-				throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
-
-			spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
-					term_width_pixels, term_height_pixels, terminal_modes);
-
-			c.successCounter = c.failedCounter = 0;
-		}
-
-		synchronized (c.channelSendLock)
-		{
-			if (c.closeMessageSent)
-				throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
-			tm.sendMessage(spr.getPayload());
-		}
-
-		try
-		{
-			if (waitForChannelRequestResult(c) == false)
-				throw new IOException("The server denied the request.");
-		}
-		catch (IOException e)
-		{
-			throw (IOException) new IOException("PTY request failed").initCause(e);
-		}
-	}
-
-	public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
-			String x11AuthenticationCookie, int x11ScreenNumber) throws IOException
-	{
-		PacketSessionX11Request psr;
-
-		synchronized (c)
-		{
-			if (c.state != Channel.STATE_OPEN)
-				throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
-
-			psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
-					x11AuthenticationCookie, x11ScreenNumber);
-
-			c.successCounter = c.failedCounter = 0;
-		}
-
-		synchronized (c.channelSendLock)
-		{
-			if (c.closeMessageSent)
-				throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
-			tm.sendMessage(psr.getPayload());
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
-
-		try
-		{
-			if (waitForChannelRequestResult(c) == false)
-				throw new IOException("The server denied the request.");
-		}
-		catch (IOException e)
-		{
-			throw (IOException) new IOException("The X11 request failed.").initCause(e);
-		}
-	}
-
-	public void requestSubSystem(Channel c, String subSystemName) throws IOException
-	{
-		PacketSessionSubsystemRequest ssr;
-
-		synchronized (c)
-		{
-			if (c.state != Channel.STATE_OPEN)
-				throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
-
-			ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
-
-			c.successCounter = c.failedCounter = 0;
-		}
-
-		synchronized (c.channelSendLock)
-		{
-			if (c.closeMessageSent)
-				throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
-			tm.sendMessage(ssr.getPayload());
-		}
-
-		try
-		{
-			if (waitForChannelRequestResult(c) == false)
-				throw new IOException("The server denied the request.");
-		}
-		catch (IOException e)
-		{
-			throw (IOException) new IOException("The subsystem request failed.").initCause(e);
-		}
-	}
-
-	public void requestExecCommand(Channel c, String cmd) throws IOException
-	{
-		PacketSessionExecCommand sm;
-
-		synchronized (c)
-		{
-			if (c.state != Channel.STATE_OPEN)
-				throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
-
-			sm = new PacketSessionExecCommand(c.remoteID, true, cmd);
-
-			c.successCounter = c.failedCounter = 0;
-		}
-
-		synchronized (c.channelSendLock)
-		{
-			if (c.closeMessageSent)
-				throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
-			tm.sendMessage(sm.getPayload());
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Executing command (channel " + c.localID + ", '" + cmd + "')");
-
-		try
-		{
-			if (waitForChannelRequestResult(c) == false)
-				throw new IOException("The server denied the request.");
-		}
-		catch (IOException e)
-		{
-			throw (IOException) new IOException("The execute request failed.").initCause(e);
-		}
-	}
-
-	public void requestShell(Channel c) throws IOException
-	{
-		PacketSessionStartShell sm;
-
-		synchronized (c)
-		{
-			if (c.state != Channel.STATE_OPEN)
-				throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
-
-			sm = new PacketSessionStartShell(c.remoteID, true);
-
-			c.successCounter = c.failedCounter = 0;
-		}
-
-		synchronized (c.channelSendLock)
-		{
-			if (c.closeMessageSent)
-				throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
-			tm.sendMessage(sm.getPayload());
-		}
-
-		try
-		{
-			if (waitForChannelRequestResult(c) == false)
-				throw new IOException("The server denied the request.");
-		}
-		catch (IOException e)
-		{
-			throw (IOException) new IOException("The shell request failed.").initCause(e);
-		}
-	}
-
-	public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException
-	{
-		if (msglen <= 13)
-			throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")");
-
-		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-		int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
-		int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
-
-		Channel c = getChannel(id);
-
-		if (c == null)
-			throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
-
-		if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
-			throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
-
-		if (len != (msglen - 13))
-			throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13)
-					+ ", got " + len + ")");
-
-		if (log.isEnabled())
-			log.log(80, "Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
-
-		synchronized (c)
-		{
-			if (c.state == Channel.STATE_CLOSED)
-				return; // ignore
-
-			if (c.state != Channel.STATE_OPEN)
-				throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
-						+ c.state + ")");
-
-			if (c.localWindow < len)
-				throw new IOException("Remote sent too much data, does not fit into window.");
-
-			c.localWindow -= len;
-
-			System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
-			c.stderrWritepos += len;
-
-			c.notifyAll();
-		}
-	}
-
-	/**
-	 * Wait until for a condition.
-	 * 
-	 * @param c
-	 *            Channel
-	 * @param timeout
-	 *            in ms, 0 means no timeout.
-	 * @param condition_mask
-	 *            minimum event mask
-	 * @return all current events
-	 * 
-	 */
-	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 = (RemoteForwardingData) remoteForwardings.get(new Integer(remoteConnectedPort));
-			}
-
-			if (rfd == null)
-			{
-				PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
-						Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
-						"No thanks, unknown port in forwarded-tcpip request", "");
-
-				/* Always try to be polite. */
-
-				tm.sendAsynchronousMessage(pcof.getPayload());
-
-				if (log.isEnabled())
-					log.log(20, "Unexpected forwarded-tcpip request, denying it!");
-
-				return;
-			}
-
-			Channel c = new Channel(this);
-
-			synchronized (c)
-			{
-				c.remoteID = remoteID;
-				c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
-				c.remoteMaxPacketSize = remoteMaxPacketSize;
-				c.localID = addChannel(c);
-			}
-
-			/*
-			 * The open confirmation message will be sent from another thread.
-			 */
-
-			RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
-					remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
-
-			rat.setDaemon(true);
-			rat.start();
-
-			return;
-		}
-
-		/* Tell the server that we have no idea what it is talking about */
-
-		PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
-				"Unknown channel type", "");
-
-		tm.sendAsynchronousMessage(pcof.getPayload());
-
-		if (log.isEnabled())
-			log.log(20, "The peer tried to open an unsupported channel type (" + channelType + ")");
-	}
-
-	public void msgChannelRequest(byte[] msg, int msglen) throws IOException
-	{
-		TypesReader tr = new TypesReader(msg, 0, msglen);
-
-		tr.readByte(); // skip packet type
-		int id = tr.readUINT32();
-
-		Channel c = getChannel(id);
-
-		if (c == null)
-			throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
-
-		String type = tr.readString("US-ASCII");
-		boolean wantReply = tr.readBoolean();
-
-		if (log.isEnabled())
-			log.log(80, "Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
-
-		if (type.equals("exit-status"))
-		{
-			if (wantReply != false)
-				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
-
-			int exit_status = tr.readUINT32();
-
-			if (tr.remain() != 0)
-				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-
-			synchronized (c)
-			{
-				c.exit_status = new Integer(exit_status);
-				c.notifyAll();
-			}
-
-			if (log.isEnabled())
-				log.log(50, "Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
-
-			return;
-		}
-
-		if (type.equals("exit-signal"))
-		{
-			if (wantReply != false)
-				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
-
-			String signame = tr.readString("US-ASCII");
-			tr.readBoolean();
-			tr.readString();
-			tr.readString();
-
-			if (tr.remain() != 0)
-				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-
-			synchronized (c)
-			{
-				c.exit_signal = signame;
-				c.notifyAll();
-			}
-
-			if (log.isEnabled())
-				log.log(50, "Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
-
-			return;
-		}
-
-		/* We simply ignore unknown channel requests, however, if the server wants a reply,
-		 * then we signal that we have no idea what it is about.
-		 */
-
-		if (wantReply)
-		{
-			byte[] reply = new byte[5];
-
-			reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE;
-			reply[1] = (byte) (c.remoteID >> 24);
-			reply[2] = (byte) (c.remoteID >> 16);
-			reply[3] = (byte) (c.remoteID >> 8);
-			reply[4] = (byte) (c.remoteID);
-
-			tm.sendAsynchronousMessage(reply);
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Channel request '" + type + "' is not known, ignoring it");
-	}
-
-	public void msgChannelEOF(byte[] msg, int msglen) throws IOException
-	{
-		if (msglen != 5)
-			throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");
-
-		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-		Channel c = getChannel(id);
-
-		if (c == null)
-			throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
-
-		synchronized (c)
-		{
-			c.EOF = true;
-			c.notifyAll();
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
-	}
-
-	public void msgChannelClose(byte[] msg, int msglen) throws IOException
-	{
-		if (msglen != 5)
-			throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");
-
-		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-		Channel c = getChannel(id);
-
-		if (c == null)
-			throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
-
-		synchronized (c)
-		{
-			c.EOF = true;
-			c.state = Channel.STATE_CLOSED;
-			c.setReasonClosed("Close requested by remote");
-			c.closeMessageRecv = true;
-
-			removeChannel(c.localID);
-
-			c.notifyAll();
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
-	}
-
-	public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
-	{
-		if (msglen != 5)
-			throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");
-
-		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-		Channel c = getChannel(id);
-
-		if (c == null)
-			throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
-
-		synchronized (c)
-		{
-			c.successCounter++;
-			c.notifyAll();
-		}
-
-		if (log.isEnabled())
-			log.log(80, "Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
-	}
-
-	public void msgChannelFailure(byte[] msg, int msglen) throws IOException
-	{
-		if (msglen != 5)
-			throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");
-
-		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-		Channel c = getChannel(id);
-
-		if (c == null)
-			throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
-
-		synchronized (c)
-		{
-			c.failedCounter++;
-			c.notifyAll();
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
-	}
-
-	public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
-	{
-		PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);
-
-		Channel c = getChannel(sm.recipientChannelID);
-
-		if (c == null)
-			throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
-					+ sm.recipientChannelID);
-
-		synchronized (c)
-		{
-			if (c.state != Channel.STATE_OPENING)
-				throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
-						+ sm.recipientChannelID);
-
-			c.remoteID = sm.senderChannelID;
-			c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
-			c.remoteMaxPacketSize = sm.maxPacketSize;
-			c.state = Channel.STATE_OPEN;
-			c.notifyAll();
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
-					+ sm.senderChannelID + ")");
-	}
-
-	public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
-	{
-		if (msglen < 5)
-			throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");
-
-		TypesReader tr = new TypesReader(msg, 0, msglen);
-
-		tr.readByte(); // skip packet type
-		int id = tr.readUINT32(); /* sender channel */
-
-		Channel c = getChannel(id);
-
-		if (c == null)
-			throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
-
-		int reasonCode = tr.readUINT32();
-		String description = tr.readString("UTF-8");
-
-		String reasonCodeSymbolicName = null;
-
-		switch (reasonCode)
-		{
-		case 1:
-			reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
-			break;
-		case 2:
-			reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
-			break;
-		case 3:
-			reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
-			break;
-		case 4:
-			reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
-			break;
-		default:
-			reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
-		}
-
-		StringBuffer descriptionBuffer = new StringBuffer();
-		descriptionBuffer.append(description);
-
-		for (int i = 0; i < descriptionBuffer.length(); i++)
-		{
-			char cc = descriptionBuffer.charAt(i);
-
-			if ((cc >= 32) && (cc <= 126))
-				continue;
-			descriptionBuffer.setCharAt(i, '\uFFFD');
-		}
-
-		synchronized (c)
-		{
-			c.EOF = true;
-			c.state = Channel.STATE_CLOSED;
-			c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
-					+ descriptionBuffer.toString() + "')");
-			c.notifyAll();
-		}
-
-		if (log.isEnabled())
-			log.log(50, "Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
-	}
-
-	public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
-	{
-		/* Currently we do not support any kind of global request */
-
-		TypesReader tr = new TypesReader(msg, 0, msglen);
-
-		tr.readByte(); // skip packet type
-		String requestName = tr.readString();
-		boolean wantReply = tr.readBoolean();
-
-		if (wantReply)
-		{
-			byte[] reply_failure = new byte[1];
-			reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
-
-			tm.sendAsynchronousMessage(reply_failure);
-		}
-
-		/* We do not clean up the requestName String - that is OK for debug */
-
-		if (log.isEnabled())
-			log.log(80, "Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
-	}
-
-	public void msgGlobalSuccess() throws IOException
-	{
-		synchronized (channels)
-		{
-			globalSuccessCounter++;
-			channels.notifyAll();
-		}
-
-		if (log.isEnabled())
-			log.log(80, "Got SSH_MSG_REQUEST_SUCCESS");
-	}
-
-	public void msgGlobalFailure() throws IOException
-	{
-		synchronized (channels)
-		{
-			globalFailedCounter++;
-			channels.notifyAll();
-		}
-
-		if (log.isEnabled())
-			log.log(80, "Got SSH_MSG_REQUEST_FAILURE");
-	}
-
-	public void handleMessage(byte[] msg, int msglen) throws IOException
-	{
-		if (msg == null)
-		{
-			if (log.isEnabled())
-				log.log(50, "HandleMessage: got shutdown");
-
-			synchronized (listenerThreads)
-			{
-				for (int i = 0; i < listenerThreads.size(); i++)
-				{
-					IChannelWorkerThread lat = (IChannelWorkerThread) listenerThreads.elementAt(i);
-					lat.stopWorking();
-				}
-				listenerThreadsAllowed = false;
-			}
-
-			synchronized (channels)
-			{
-				shutdown = true;
-
-				for (int i = 0; i < channels.size(); i++)
-				{
-					Channel c = (Channel) channels.elementAt(i);
-					synchronized (c)
-					{
-						c.EOF = true;
-						c.state = Channel.STATE_CLOSED;
-						c.setReasonClosed("The connection is being shutdown");
-						c.closeMessageRecv = true; /*
-																															 * You never know, perhaps
-																															 * we are waiting for a
-																															 * pending close message
-																															 * from the server...
-																															 */
-						c.notifyAll();
-					}
-				}
-				/* Works with J2ME */
-				channels.setSize(0);
-				channels.trimToSize();
-				channels.notifyAll(); /* Notify global response waiters */
-				return;
-			}
-		}
-
-		switch (msg[0])
-		{
-		case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
-			msgChannelOpenConfirmation(msg, msglen);
-			break;
-		case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
-			msgChannelWindowAdjust(msg, msglen);
-			break;
-		case Packets.SSH_MSG_CHANNEL_DATA:
-			msgChannelData(msg, msglen);
-			break;
-		case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
-			msgChannelExtendedData(msg, msglen);
-			break;
-		case Packets.SSH_MSG_CHANNEL_REQUEST:
-			msgChannelRequest(msg, msglen);
-			break;
-		case Packets.SSH_MSG_CHANNEL_EOF:
-			msgChannelEOF(msg, msglen);
-			break;
-		case Packets.SSH_MSG_CHANNEL_OPEN:
-			msgChannelOpen(msg, msglen);
-			break;
-		case Packets.SSH_MSG_CHANNEL_CLOSE:
-			msgChannelClose(msg, msglen);
-			break;
-		case Packets.SSH_MSG_CHANNEL_SUCCESS:
-			msgChannelSuccess(msg, msglen);
-			break;
-		case Packets.SSH_MSG_CHANNEL_FAILURE:
-			msgChannelFailure(msg, msglen);
-			break;
-		case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
-			msgChannelOpenFailure(msg, msglen);
-			break;
-		case Packets.SSH_MSG_GLOBAL_REQUEST:
-			msgGlobalRequest(msg, msglen);
-			break;
-		case Packets.SSH_MSG_REQUEST_SUCCESS:
-			msgGlobalSuccess();
-			break;
-		case Packets.SSH_MSG_REQUEST_FAILURE:
-			msgGlobalFailure();
-			break;
-		default:
-			throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));
-		}
-	}
-}
+
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.HashMap;
+import java.util.Vector;
+
+import com.trilead.ssh2.ChannelCondition;
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.PacketChannelOpenConfirmation;
+import com.trilead.ssh2.packets.PacketChannelOpenFailure;
+import com.trilead.ssh2.packets.PacketChannelTrileadPing;
+import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest;
+import com.trilead.ssh2.packets.PacketGlobalForwardRequest;
+import com.trilead.ssh2.packets.PacketGlobalTrileadPing;
+import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel;
+import com.trilead.ssh2.packets.PacketOpenSessionChannel;
+import com.trilead.ssh2.packets.PacketSessionExecCommand;
+import com.trilead.ssh2.packets.PacketSessionPtyRequest;
+import com.trilead.ssh2.packets.PacketSessionStartShell;
+import com.trilead.ssh2.packets.PacketSessionSubsystemRequest;
+import com.trilead.ssh2.packets.PacketSessionX11Request;
+import com.trilead.ssh2.packets.Packets;
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.transport.MessageHandler;
+import com.trilead.ssh2.transport.TransportManager;
+
+/**
+ * ChannelManager. Please read the comments in Channel.java.
+ * <p>
+ * Besides the crypto part, this is the core of the library.
+ * 
+ * @author Christian Plattner, plattner at 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 x11_magic_cookies = new HashMap();
+
+	private TransportManager tm;
+
+	private Vector channels = new Vector();
+	private int nextLocalChannel = 100;
+	private boolean shutdown = false;
+	private int globalSuccessCounter = 0;
+	private int globalFailedCounter = 0;
+
+	private HashMap remoteForwardings = new HashMap();
+
+	private Vector listenerThreads = new Vector();
+
+	private boolean listenerThreadsAllowed = true;
+
+	public ChannelManager(TransportManager tm)
+	{
+		this.tm = tm;
+		tm.registerMessageHandler(this, 80, 100);
+	}
+
+	private Channel getChannel(int id)
+	{
+		synchronized (channels)
+		{
+			for (int i = 0; i < channels.size(); i++)
+			{
+				Channel c = (Channel) channels.elementAt(i);
+				if (c.localID == id)
+					return c;
+			}
+		}
+		return null;
+	}
+
+	private void removeChannel(int id)
+	{
+		synchronized (channels)
+		{
+			for (int i = 0; i < channels.size(); i++)
+			{
+				Channel c = (Channel) channels.elementAt(i);
+				if (c.localID == id)
+				{
+					channels.removeElementAt(i);
+					break;
+				}
+			}
+		}
+	}
+
+	private int addChannel(Channel c)
+	{
+		synchronized (channels)
+		{
+			channels.addElement(c);
+			return nextLocalChannel++;
+		}
+	}
+
+	private void waitUntilChannelOpen(Channel c) throws IOException
+	{
+		synchronized (c)
+		{
+			while (c.state == Channel.STATE_OPENING)
+			{
+				try
+				{
+					c.wait();
+				}
+				catch (InterruptedException ignore)
+				{
+					throw new InterruptedIOException();
+				}
+			}
+
+			if (c.state != Channel.STATE_OPEN)
+			{
+				removeChannel(c.localID);
+
+				String detail = c.getReasonClosed();
+
+				if (detail == null)
+					detail = "state: " + c.state;
+
+				throw new IOException("Could not open channel (" + detail + ")");
+			}
+		}
+	}
+
+	private final boolean waitForGlobalRequestResult() throws IOException
+	{
+		synchronized (channels)
+		{
+			while ((globalSuccessCounter == 0) && (globalFailedCounter == 0))
+			{
+				if (shutdown)
+				{
+					throw new IOException("The connection is being shutdown");
+				}
+
+				try
+				{
+					channels.wait();
+				}
+				catch (InterruptedException ignore)
+				{
+					throw new InterruptedIOException();
+        }
+			}
+
+			if ((globalFailedCounter == 0) && (globalSuccessCounter == 1))
+				return true;
+
+			if ((globalFailedCounter == 1) && (globalSuccessCounter == 0))
+				return false;
+
+			throw new IOException("Illegal state. The server sent " + globalSuccessCounter
+					+ " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages.");
+		}
+	}
+
+	private final boolean waitForChannelRequestResult(Channel c) throws IOException
+	{
+		synchronized (c)
+		{
+			while ((c.successCounter == 0) && (c.failedCounter == 0))
+			{
+				if (c.state != Channel.STATE_OPEN)
+				{
+					String detail = c.getReasonClosed();
+
+					if (detail == null)
+						detail = "state: " + c.state;
+
+					throw new IOException("This SSH2 channel is not open (" + detail + ")");
+				}
+
+				try
+				{
+					c.wait();
+				}
+				catch (InterruptedException ignore)
+				{
+					throw new InterruptedIOException();
+				}
+			}
+
+			if ((c.failedCounter == 0) && (c.successCounter == 1))
+				return true;
+
+			if ((c.failedCounter == 1) && (c.successCounter == 0))
+				return false;
+
+			throw new IOException("Illegal state. The server sent " + c.successCounter
+					+ " SSH_MSG_CHANNEL_SUCCESS and " + c.failedCounter + " SSH_MSG_CHANNEL_FAILURE messages.");
+		}
+	}
+
+	public void registerX11Cookie(String hexFakeCookie, X11ServerData data)
+	{
+		synchronized (x11_magic_cookies)
+		{
+			x11_magic_cookies.put(hexFakeCookie, data);
+		}
+	}
+
+	public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels)
+	{
+		if (hexFakeCookie == null)
+			throw new IllegalStateException("hexFakeCookie may not be null");
+
+		synchronized (x11_magic_cookies)
+		{
+			x11_magic_cookies.remove(hexFakeCookie);
+		}
+
+		if (killChannels == false)
+			return;
+
+		if (log.isEnabled())
+			log.log(50, "Closing all X11 channels for the given fake cookie");
+
+		Vector channel_copy;
+
+		synchronized (channels)
+		{
+			channel_copy = (Vector) channels.clone();
+		}
+
+		for (int i = 0; i < channel_copy.size(); i++)
+		{
+			Channel c = (Channel) channel_copy.elementAt(i);
+
+			synchronized (c)
+			{
+				if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
+					continue;
+			}
+
+			try
+			{
+				closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
+			}
+			catch (IOException e)
+			{
+			}
+		}
+	}
+
+	public X11ServerData checkX11Cookie(String hexFakeCookie)
+	{
+		synchronized (x11_magic_cookies)
+		{
+			if (hexFakeCookie != null)
+				return (X11ServerData) x11_magic_cookies.get(hexFakeCookie);
+		}
+		return null;
+	}
+
+	public void closeAllChannels()
+	{
+		if (log.isEnabled())
+			log.log(50, "Closing all channels");
+
+		Vector channel_copy;
+
+		synchronized (channels)
+		{
+			channel_copy = (Vector) channels.clone();
+		}
+
+		for (int i = 0; i < channel_copy.size(); i++)
+		{
+			Channel c = (Channel) channel_copy.elementAt(i);
+			try
+			{
+				closeChannel(c, "Closing all channels", true);
+			}
+			catch (IOException e)
+			{
+			}
+		}
+	}
+
+	public void closeChannel(Channel c, String reason, boolean force) throws IOException
+	{
+		byte msg[] = new byte[5];
+
+		synchronized (c)
+		{
+			if (force)
+			{
+				c.state = Channel.STATE_CLOSED;
+				c.EOF = true;
+			}
+
+			c.setReasonClosed(reason);
+
+			msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
+			msg[1] = (byte) (c.remoteID >> 24);
+			msg[2] = (byte) (c.remoteID >> 16);
+			msg[3] = (byte) (c.remoteID >> 8);
+			msg[4] = (byte) (c.remoteID);
+
+			c.notifyAll();
+		}
+
+		synchronized (c.channelSendLock)
+		{
+			if (c.closeMessageSent == true)
+				return;
+			tm.sendMessage(msg);
+			c.closeMessageSent = true;
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
+	}
+
+	public void sendEOF(Channel c) throws IOException
+	{
+		byte[] msg = new byte[5];
+
+		synchronized (c)
+		{
+			if (c.state != Channel.STATE_OPEN)
+				return;
+
+			msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
+			msg[1] = (byte) (c.remoteID >> 24);
+			msg[2] = (byte) (c.remoteID >> 16);
+			msg[3] = (byte) (c.remoteID >> 8);
+			msg[4] = (byte) (c.remoteID);
+		}
+
+		synchronized (c.channelSendLock)
+		{
+			if (c.closeMessageSent == true)
+				return;
+			tm.sendMessage(msg);
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
+	}
+
+	public void sendOpenConfirmation(Channel c) throws IOException
+	{
+		PacketChannelOpenConfirmation pcoc = null;
+
+		synchronized (c)
+		{
+			if (c.state != Channel.STATE_OPENING)
+				return;
+
+			c.state = Channel.STATE_OPEN;
+
+			pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
+		}
+
+		synchronized (c.channelSendLock)
+		{
+			if (c.closeMessageSent == true)
+				return;
+			tm.sendMessage(pcoc.getPayload());
+		}
+	}
+
+	public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException
+	{
+		while (len > 0)
+		{
+			int thislen = 0;
+			byte[] msg;
+
+			synchronized (c)
+			{
+				while (true)
+				{
+					if (c.state == Channel.STATE_CLOSED)
+						throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")");
+
+					if (c.state != Channel.STATE_OPEN)
+						throw new IOException("SSH channel in strange state. (" + c.state + ")");
+
+					if (c.remoteWindow != 0)
+						break;
+
+					try
+					{
+						c.wait();
+					}
+					catch (InterruptedException ignore)
+					{
+						throw new InterruptedIOException();
+					}
+				}
+
+				/* len > 0, no sign extension can happen when comparing */
+
+				thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;
+
+				int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);
+
+				/* The worst case scenario =) a true bottleneck */
+
+				if (estimatedMaxDataLen <= 0)
+				{
+					estimatedMaxDataLen = 1;
+				}
+
+				if (thislen > estimatedMaxDataLen)
+					thislen = estimatedMaxDataLen;
+
+				c.remoteWindow -= thislen;
+
+				msg = new byte[1 + 8 + thislen];
+
+				msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
+				msg[1] = (byte) (c.remoteID >> 24);
+				msg[2] = (byte) (c.remoteID >> 16);
+				msg[3] = (byte) (c.remoteID >> 8);
+				msg[4] = (byte) (c.remoteID);
+				msg[5] = (byte) (thislen >> 24);
+				msg[6] = (byte) (thislen >> 16);
+				msg[7] = (byte) (thislen >> 8);
+				msg[8] = (byte) (thislen);
+
+				System.arraycopy(buffer, pos, msg, 9, thislen);
+			}
+
+			synchronized (c.channelSendLock)
+			{
+				if (c.closeMessageSent == true)
+					throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")");
+
+				tm.sendMessage(msg);
+			}
+
+			pos += thislen;
+			len -= thislen;
+		}
+	}
+
+	public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
+			throws IOException
+	{
+		RemoteForwardingData rfd = new RemoteForwardingData();
+
+		rfd.bindAddress = bindAddress;
+		rfd.bindPort = bindPort;
+		rfd.targetAddress = targetAddress;
+		rfd.targetPort = targetPort;
+
+		synchronized (remoteForwardings)
+		{
+			Integer key = new Integer(bindPort);
+
+			if (remoteForwardings.get(key) != null)
+			{
+				throw new IOException("There is already a forwarding for remote port " + bindPort);
+			}
+
+			remoteForwardings.put(key, rfd);
+		}
+
+		synchronized (channels)
+		{
+			globalSuccessCounter = globalFailedCounter = 0;
+		}
+
+		PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
+		tm.sendMessage(pgf.getPayload());
+
+		if (log.isEnabled())
+			log.log(50, "Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
+
+		try
+		{
+			if (waitForGlobalRequestResult() == false)
+				throw new IOException("The server denied the request (did you enable port forwarding?)");
+		}
+		catch (IOException e)
+		{
+			synchronized (remoteForwardings)
+			{
+				remoteForwardings.remove(rfd);
+			}
+			throw e;
+		}
+
+		return bindPort;
+	}
+
+	public void requestCancelGlobalForward(int bindPort) throws IOException
+	{
+		RemoteForwardingData rfd = null;
+
+		synchronized (remoteForwardings)
+		{
+			rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(bindPort));
+
+			if (rfd == null)
+				throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
+		}
+
+		synchronized (channels)
+		{
+			globalSuccessCounter = globalFailedCounter = 0;
+		}
+
+		PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
+				rfd.bindPort);
+		tm.sendMessage(pgcf.getPayload());
+
+		if (log.isEnabled())
+			log.log(50, "Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
+
+		try
+		{
+			if (waitForGlobalRequestResult() == false)
+				throw new IOException("The server denied the request.");
+		}
+		finally
+		{
+			synchronized (remoteForwardings)
+			{
+				/* Only now we are sure that no more forwarded connections will arrive */
+				remoteForwardings.remove(rfd);
+			}
+		}
+
+	}
+
+	public void registerThread(IChannelWorkerThread thr) throws IOException
+	{
+		synchronized (listenerThreads)
+		{
+			if (listenerThreadsAllowed == false)
+				throw new IOException("Too late, this connection is closed.");
+			listenerThreads.addElement(thr);
+		}
+	}
+
+	public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
+			int originator_port) throws IOException
+	{
+		Channel c = new Channel(this);
+
+		synchronized (c)
+		{
+			c.localID = addChannel(c);
+			// end of synchronized block forces writing out to main memory
+		}
+
+		PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
+				c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);
+
+		tm.sendMessage(dtc.getPayload());
+
+		waitUntilChannelOpen(c);
+
+		return c;
+	}
+
+	public Channel openSessionChannel() throws IOException
+	{
+		Channel c = new Channel(this);
+
+		synchronized (c)
+		{
+			c.localID = addChannel(c);
+			// end of synchronized block forces the writing out to main memory
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");
+
+		PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
+		tm.sendMessage(smo.getPayload());
+
+		waitUntilChannelOpen(c);
+
+		return c;
+	}
+
+	public void requestGlobalTrileadPing() throws IOException
+	{
+		synchronized (channels)
+		{
+			globalSuccessCounter = globalFailedCounter = 0;
+		}
+
+		PacketGlobalTrileadPing pgtp = new PacketGlobalTrileadPing();
+
+		tm.sendMessage(pgtp.getPayload());
+
+		if (log.isEnabled())
+			log.log(50, "Sending SSH_MSG_GLOBAL_REQUEST 'trilead-ping'.");
+
+		try
+		{
+			if (waitForGlobalRequestResult() == true)
+				throw new IOException("Your server is alive - but buggy. "
+						+ "It replied with SSH_MSG_REQUEST_SUCCESS when it actually should not.");
+
+		}
+		catch (IOException e)
+		{
+			throw (IOException) new IOException("The ping request failed.").initCause(e);
+		}
+	}
+
+	public void requestChannelTrileadPing(Channel c) throws IOException
+	{
+		PacketChannelTrileadPing pctp;
+
+		synchronized (c)
+		{
+			if (c.state != Channel.STATE_OPEN)
+				throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
+
+			pctp = new PacketChannelTrileadPing(c.remoteID);
+
+			c.successCounter = c.failedCounter = 0;
+		}
+
+		synchronized (c.channelSendLock)
+		{
+			if (c.closeMessageSent)
+				throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
+			tm.sendMessage(pctp.getPayload());
+		}
+
+		try
+		{
+			if (waitForChannelRequestResult(c) == true)
+				throw new IOException("Your server is alive - but buggy. "
+						+ "It replied with SSH_MSG_SESSION_SUCCESS when it actually should not.");
+
+		}
+		catch (IOException e)
+		{
+			throw (IOException) new IOException("The ping request failed.").initCause(e);
+		}
+	}
+
+	public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
+			int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
+	{
+		PacketSessionPtyRequest spr;
+
+		synchronized (c)
+		{
+			if (c.state != Channel.STATE_OPEN)
+				throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
+
+			spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
+					term_width_pixels, term_height_pixels, terminal_modes);
+
+			c.successCounter = c.failedCounter = 0;
+		}
+
+		synchronized (c.channelSendLock)
+		{
+			if (c.closeMessageSent)
+				throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
+			tm.sendMessage(spr.getPayload());
+		}
+
+		try
+		{
+			if (waitForChannelRequestResult(c) == false)
+				throw new IOException("The server denied the request.");
+		}
+		catch (IOException e)
+		{
+			throw (IOException) new IOException("PTY request failed").initCause(e);
+		}
+	}
+
+	public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
+			String x11AuthenticationCookie, int x11ScreenNumber) throws IOException
+	{
+		PacketSessionX11Request psr;
+
+		synchronized (c)
+		{
+			if (c.state != Channel.STATE_OPEN)
+				throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
+
+			psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
+					x11AuthenticationCookie, x11ScreenNumber);
+
+			c.successCounter = c.failedCounter = 0;
+		}
+
+		synchronized (c.channelSendLock)
+		{
+			if (c.closeMessageSent)
+				throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
+			tm.sendMessage(psr.getPayload());
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
+
+		try
+		{
+			if (waitForChannelRequestResult(c) == false)
+				throw new IOException("The server denied the request.");
+		}
+		catch (IOException e)
+		{
+			throw (IOException) new IOException("The X11 request failed.").initCause(e);
+		}
+	}
+
+	public void requestSubSystem(Channel c, String subSystemName) throws IOException
+	{
+		PacketSessionSubsystemRequest ssr;
+
+		synchronized (c)
+		{
+			if (c.state != Channel.STATE_OPEN)
+				throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
+
+			ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
+
+			c.successCounter = c.failedCounter = 0;
+		}
+
+		synchronized (c.channelSendLock)
+		{
+			if (c.closeMessageSent)
+				throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
+			tm.sendMessage(ssr.getPayload());
+		}
+
+		try
+		{
+			if (waitForChannelRequestResult(c) == false)
+				throw new IOException("The server denied the request.");
+		}
+		catch (IOException e)
+		{
+			throw (IOException) new IOException("The subsystem request failed.").initCause(e);
+		}
+	}
+
+	public void requestExecCommand(Channel c, String cmd) throws IOException
+	{
+		PacketSessionExecCommand sm;
+
+		synchronized (c)
+		{
+			if (c.state != Channel.STATE_OPEN)
+				throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
+
+			sm = new PacketSessionExecCommand(c.remoteID, true, cmd);
+
+			c.successCounter = c.failedCounter = 0;
+		}
+
+		synchronized (c.channelSendLock)
+		{
+			if (c.closeMessageSent)
+				throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
+			tm.sendMessage(sm.getPayload());
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Executing command (channel " + c.localID + ", '" + cmd + "')");
+
+		try
+		{
+			if (waitForChannelRequestResult(c) == false)
+				throw new IOException("The server denied the request.");
+		}
+		catch (IOException e)
+		{
+			throw (IOException) new IOException("The execute request failed.").initCause(e);
+		}
+	}
+
+	public void requestShell(Channel c) throws IOException
+	{
+		PacketSessionStartShell sm;
+
+		synchronized (c)
+		{
+			if (c.state != Channel.STATE_OPEN)
+				throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
+
+			sm = new PacketSessionStartShell(c.remoteID, true);
+
+			c.successCounter = c.failedCounter = 0;
+		}
+
+		synchronized (c.channelSendLock)
+		{
+			if (c.closeMessageSent)
+				throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
+			tm.sendMessage(sm.getPayload());
+		}
+
+		try
+		{
+			if (waitForChannelRequestResult(c) == false)
+				throw new IOException("The server denied the request.");
+		}
+		catch (IOException e)
+		{
+			throw (IOException) new IOException("The shell request failed.").initCause(e);
+		}
+	}
+
+	public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException
+	{
+		if (msglen <= 13)
+			throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")");
+
+		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+		int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+		int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
+
+		Channel c = getChannel(id);
+
+		if (c == null)
+			throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
+
+		if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
+			throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
+
+		if (len != (msglen - 13))
+			throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13)
+					+ ", got " + len + ")");
+
+		if (log.isEnabled())
+			log.log(80, "Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
+
+		synchronized (c)
+		{
+			if (c.state == Channel.STATE_CLOSED)
+				return; // ignore
+
+			if (c.state != Channel.STATE_OPEN)
+				throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
+						+ c.state + ")");
+
+			if (c.localWindow < len)
+				throw new IOException("Remote sent too much data, does not fit into window.");
+
+			c.localWindow -= len;
+
+			System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
+			c.stderrWritepos += len;
+
+			c.notifyAll();
+		}
+	}
+
+	/**
+	 * Wait until for a condition.
+	 * 
+	 * @param c
+	 *            Channel
+	 * @param timeout
+	 *            in ms, 0 means no timeout.
+	 * @param condition_mask
+	 *            minimum event mask
+	 * @return all current events
+	 * 
+	 */
+	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)
+				{
+					throw new InterruptedIOException();
+				}
+			}
+
+			/* OK, there is some data. Return it. */
+
+			if (!extended)
+			{
+				copylen = (stdoutAvail > len) ? len : stdoutAvail;
+				System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
+				c.stdoutReadpos += copylen;
+
+				if (c.stdoutReadpos != c.stdoutWritepos)
+
+					System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
+							- c.stdoutReadpos);
+
+				c.stdoutWritepos -= c.stdoutReadpos;
+				c.stdoutReadpos = 0;
+			}
+			else
+			{
+				copylen = (stderrAvail > len) ? len : stderrAvail;
+				System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
+				c.stderrReadpos += copylen;
+
+				if (c.stderrReadpos != c.stderrWritepos)
+
+					System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
+							- c.stderrReadpos);
+
+				c.stderrWritepos -= c.stderrReadpos;
+				c.stderrReadpos = 0;
+			}
+
+			if (c.state != Channel.STATE_OPEN)
+				return copylen;
+
+			if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2))
+			{
+				int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos, Channel.CHANNEL_BUFFER_SIZE
+						- c.stderrWritepos);
+
+				increment = minFreeSpace - c.localWindow;
+				c.localWindow = minFreeSpace;
+			}
+
+			remoteID = c.remoteID; /* read while holding the lock */
+			localID = c.localID; /* read while holding the lock */
+		}
+
+		/*
+		 * If a consumer reads stdout and stdin in parallel, we may end up with
+		 * sending two msgWindowAdjust messages. Luckily, it
+		 * does not matter in which order they arrive at the server.
+		 */
+
+		if (increment > 0)
+		{
+			if (log.isEnabled())
+				log.log(80, "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
+
+			synchronized (c.channelSendLock)
+			{
+				byte[] msg = c.msgWindowAdjust;
+
+				msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
+				msg[1] = (byte) (remoteID >> 24);
+				msg[2] = (byte) (remoteID >> 16);
+				msg[3] = (byte) (remoteID >> 8);
+				msg[4] = (byte) (remoteID);
+				msg[5] = (byte) (increment >> 24);
+				msg[6] = (byte) (increment >> 16);
+				msg[7] = (byte) (increment >> 8);
+				msg[8] = (byte) (increment);
+
+				if (c.closeMessageSent == false)
+					tm.sendMessage(msg);
+			}
+		}
+
+		return copylen;
+	}
+
+	public void msgChannelData(byte[] msg, int msglen) throws IOException
+	{
+		if (msglen <= 9)
+			throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")");
+
+		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+		int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+
+		Channel c = getChannel(id);
+
+		if (c == null)
+			throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
+
+		if (len != (msglen - 9))
+			throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got "
+					+ len + ")");
+
+		if (log.isEnabled())
+			log.log(80, "Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");
+
+		synchronized (c)
+		{
+			if (c.state == Channel.STATE_CLOSED)
+				return; // ignore
+
+			if (c.state != Channel.STATE_OPEN)
+				throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");
+
+			if (c.localWindow < len)
+				throw new IOException("Remote sent too much data, does not fit into window.");
+
+			c.localWindow -= len;
+
+			System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
+			c.stdoutWritepos += len;
+
+			c.notifyAll();
+		}
+	}
+
+	public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException
+	{
+		if (msglen != 9)
+			throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")");
+
+		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+		int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+
+		Channel c = getChannel(id);
+
+		if (c == null)
+			throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);
+
+		synchronized (c)
+		{
+			final long huge = 0xFFFFffffL; /* 2^32 - 1 */
+
+			c.remoteWindow += (windowChange & huge); /* avoid sign extension */
+
+			/* TODO - is this a good heuristic? */
+
+			if ((c.remoteWindow > huge))
+				c.remoteWindow = huge;
+
+			c.notifyAll();
+		}
+
+		if (log.isEnabled())
+			log.log(80, "Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
+	}
+
+	public void msgChannelOpen(byte[] msg, int msglen) throws IOException
+	{
+		TypesReader tr = new TypesReader(msg, 0, msglen);
+
+		tr.readByte(); // skip packet type
+		String channelType = tr.readString();
+		int remoteID = tr.readUINT32(); /* sender channel */
+		int remoteWindow = tr.readUINT32(); /* initial window size */
+		int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
+
+		if ("x11".equals(channelType))
+		{
+			synchronized (x11_magic_cookies)
+			{
+				/* If we did not request X11 forwarding, then simply ignore this bogus request. */
+
+				if (x11_magic_cookies.size() == 0)
+				{
+					PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
+							Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");
+
+					tm.sendAsynchronousMessage(pcof.getPayload());
+
+					if (log.isEnabled())
+						log.log(20, "Unexpected X11 request, denying it!");
+
+					return;
+				}
+			}
+
+			String remoteOriginatorAddress = tr.readString();
+			int remoteOriginatorPort = tr.readUINT32();
+
+			Channel c = new Channel(this);
+
+			synchronized (c)
+			{
+				c.remoteID = remoteID;
+				c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
+				c.remoteMaxPacketSize = remoteMaxPacketSize;
+				c.localID = addChannel(c);
+			}
+
+			/*
+			 * The open confirmation message will be sent from another thread
+			 */
+
+			RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
+			rxat.setDaemon(true);
+			rxat.start();
+
+			return;
+		}
+
+		if ("forwarded-tcpip".equals(channelType))
+		{
+			String remoteConnectedAddress = tr.readString(); /* address that was connected */
+			int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
+			String remoteOriginatorAddress = tr.readString(); /* originator IP address */
+			int remoteOriginatorPort = tr.readUINT32(); /* originator port */
+
+			RemoteForwardingData rfd = null;
+
+			synchronized (remoteForwardings)
+			{
+				rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(remoteConnectedPort));
+			}
+
+			if (rfd == null)
+			{
+				PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
+						Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
+						"No thanks, unknown port in forwarded-tcpip request", "");
+
+				/* Always try to be polite. */
+
+				tm.sendAsynchronousMessage(pcof.getPayload());
+
+				if (log.isEnabled())
+					log.log(20, "Unexpected forwarded-tcpip request, denying it!");
+
+				return;
+			}
+
+			Channel c = new Channel(this);
+
+			synchronized (c)
+			{
+				c.remoteID = remoteID;
+				c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
+				c.remoteMaxPacketSize = remoteMaxPacketSize;
+				c.localID = addChannel(c);
+			}
+
+			/*
+			 * The open confirmation message will be sent from another thread.
+			 */
+
+			RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
+					remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
+
+			rat.setDaemon(true);
+			rat.start();
+
+			return;
+		}
+
+		/* Tell the server that we have no idea what it is talking about */
+
+		PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
+				"Unknown channel type", "");
+
+		tm.sendAsynchronousMessage(pcof.getPayload());
+
+		if (log.isEnabled())
+			log.log(20, "The peer tried to open an unsupported channel type (" + channelType + ")");
+	}
+
+	public void msgChannelRequest(byte[] msg, int msglen) throws IOException
+	{
+		TypesReader tr = new TypesReader(msg, 0, msglen);
+
+		tr.readByte(); // skip packet type
+		int id = tr.readUINT32();
+
+		Channel c = getChannel(id);
+
+		if (c == null)
+			throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
+
+		String type = tr.readString("US-ASCII");
+		boolean wantReply = tr.readBoolean();
+
+		if (log.isEnabled())
+			log.log(80, "Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
+
+		if (type.equals("exit-status"))
+		{
+			if (wantReply != false)
+				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
+
+			int exit_status = tr.readUINT32();
+
+			if (tr.remain() != 0)
+				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+
+			synchronized (c)
+			{
+				c.exit_status = new Integer(exit_status);
+				c.notifyAll();
+			}
+
+			if (log.isEnabled())
+				log.log(50, "Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
+
+			return;
+		}
+
+		if (type.equals("exit-signal"))
+		{
+			if (wantReply != false)
+				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
+
+			String signame = tr.readString("US-ASCII");
+			tr.readBoolean();
+			tr.readString();
+			tr.readString();
+
+			if (tr.remain() != 0)
+				throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+
+			synchronized (c)
+			{
+				c.exit_signal = signame;
+				c.notifyAll();
+			}
+
+			if (log.isEnabled())
+				log.log(50, "Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
+
+			return;
+		}
+
+		/* We simply ignore unknown channel requests, however, if the server wants a reply,
+		 * then we signal that we have no idea what it is about.
+		 */
+
+		if (wantReply)
+		{
+			byte[] reply = new byte[5];
+
+			reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE;
+			reply[1] = (byte) (c.remoteID >> 24);
+			reply[2] = (byte) (c.remoteID >> 16);
+			reply[3] = (byte) (c.remoteID >> 8);
+			reply[4] = (byte) (c.remoteID);
+
+			tm.sendAsynchronousMessage(reply);
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Channel request '" + type + "' is not known, ignoring it");
+	}
+
+	public void msgChannelEOF(byte[] msg, int msglen) throws IOException
+	{
+		if (msglen != 5)
+			throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");
+
+		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+		Channel c = getChannel(id);
+
+		if (c == null)
+			throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
+
+		synchronized (c)
+		{
+			c.EOF = true;
+			c.notifyAll();
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
+	}
+
+	public void msgChannelClose(byte[] msg, int msglen) throws IOException
+	{
+		if (msglen != 5)
+			throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");
+
+		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+		Channel c = getChannel(id);
+
+		if (c == null)
+			throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
+
+		synchronized (c)
+		{
+			c.EOF = true;
+			c.state = Channel.STATE_CLOSED;
+			c.setReasonClosed("Close requested by remote");
+			c.closeMessageRecv = true;
+
+			removeChannel(c.localID);
+
+			c.notifyAll();
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
+	}
+
+	public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
+	{
+		if (msglen != 5)
+			throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");
+
+		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+		Channel c = getChannel(id);
+
+		if (c == null)
+			throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
+
+		synchronized (c)
+		{
+			c.successCounter++;
+			c.notifyAll();
+		}
+
+		if (log.isEnabled())
+			log.log(80, "Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
+	}
+
+	public void msgChannelFailure(byte[] msg, int msglen) throws IOException
+	{
+		if (msglen != 5)
+			throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");
+
+		int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+		Channel c = getChannel(id);
+
+		if (c == null)
+			throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
+
+		synchronized (c)
+		{
+			c.failedCounter++;
+			c.notifyAll();
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
+	}
+
+	public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
+	{
+		PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);
+
+		Channel c = getChannel(sm.recipientChannelID);
+
+		if (c == null)
+			throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
+					+ sm.recipientChannelID);
+
+		synchronized (c)
+		{
+			if (c.state != Channel.STATE_OPENING)
+				throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
+						+ sm.recipientChannelID);
+
+			c.remoteID = sm.senderChannelID;
+			c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
+			c.remoteMaxPacketSize = sm.maxPacketSize;
+			c.state = Channel.STATE_OPEN;
+			c.notifyAll();
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
+					+ sm.senderChannelID + ")");
+	}
+
+	public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
+	{
+		if (msglen < 5)
+			throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");
+
+		TypesReader tr = new TypesReader(msg, 0, msglen);
+
+		tr.readByte(); // skip packet type
+		int id = tr.readUINT32(); /* sender channel */
+
+		Channel c = getChannel(id);
+
+		if (c == null)
+			throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
+
+		int reasonCode = tr.readUINT32();
+		String description = tr.readString("UTF-8");
+
+		String reasonCodeSymbolicName = null;
+
+		switch (reasonCode)
+		{
+		case 1:
+			reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
+			break;
+		case 2:
+			reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
+			break;
+		case 3:
+			reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
+			break;
+		case 4:
+			reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
+			break;
+		default:
+			reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
+		}
+
+		StringBuffer descriptionBuffer = new StringBuffer();
+		descriptionBuffer.append(description);
+
+		for (int i = 0; i < descriptionBuffer.length(); i++)
+		{
+			char cc = descriptionBuffer.charAt(i);
+
+			if ((cc >= 32) && (cc <= 126))
+				continue;
+			descriptionBuffer.setCharAt(i, '\uFFFD');
+		}
+
+		synchronized (c)
+		{
+			c.EOF = true;
+			c.state = Channel.STATE_CLOSED;
+			c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
+					+ descriptionBuffer.toString() + "')");
+			c.notifyAll();
+		}
+
+		if (log.isEnabled())
+			log.log(50, "Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
+	}
+
+	public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
+	{
+		/* Currently we do not support any kind of global request */
+
+		TypesReader tr = new TypesReader(msg, 0, msglen);
+
+		tr.readByte(); // skip packet type
+		String requestName = tr.readString();
+		boolean wantReply = tr.readBoolean();
+
+		if (wantReply)
+		{
+			byte[] reply_failure = new byte[1];
+			reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
+
+			tm.sendAsynchronousMessage(reply_failure);
+		}
+
+		/* We do not clean up the requestName String - that is OK for debug */
+
+		if (log.isEnabled())
+			log.log(80, "Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
+	}
+
+	public void msgGlobalSuccess() throws IOException
+	{
+		synchronized (channels)
+		{
+			globalSuccessCounter++;
+			channels.notifyAll();
+		}
+
+		if (log.isEnabled())
+			log.log(80, "Got SSH_MSG_REQUEST_SUCCESS");
+	}
+
+	public void msgGlobalFailure() throws IOException
+	{
+		synchronized (channels)
+		{
+			globalFailedCounter++;
+			channels.notifyAll();
+		}
+
+		if (log.isEnabled())
+			log.log(80, "Got SSH_MSG_REQUEST_FAILURE");
+	}
+
+	public void handleMessage(byte[] msg, int msglen) throws IOException
+	{
+		if (msg == null)
+		{
+			if (log.isEnabled())
+				log.log(50, "HandleMessage: got shutdown");
+
+			synchronized (listenerThreads)
+			{
+				for (int i = 0; i < listenerThreads.size(); i++)
+				{
+					IChannelWorkerThread lat = (IChannelWorkerThread) listenerThreads.elementAt(i);
+					lat.stopWorking();
+				}
+				listenerThreadsAllowed = false;
+			}
+
+			synchronized (channels)
+			{
+				shutdown = true;
+
+				for (int i = 0; i < channels.size(); i++)
+				{
+					Channel c = (Channel) channels.elementAt(i);
+					synchronized (c)
+					{
+						c.EOF = true;
+						c.state = Channel.STATE_CLOSED;
+						c.setReasonClosed("The connection is being shutdown");
+						c.closeMessageRecv = true; /*
+																															 * You never know, perhaps
+																															 * we are waiting for a
+																															 * pending close message
+																															 * from the server...
+																															 */
+						c.notifyAll();
+					}
+				}
+				/* Works with J2ME */
+				channels.setSize(0);
+				channels.trimToSize();
+				channels.notifyAll(); /* Notify global response waiters */
+				return;
+			}
+		}
+
+		switch (msg[0])
+		{
+		case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
+			msgChannelOpenConfirmation(msg, msglen);
+			break;
+		case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
+			msgChannelWindowAdjust(msg, msglen);
+			break;
+		case Packets.SSH_MSG_CHANNEL_DATA:
+			msgChannelData(msg, msglen);
+			break;
+		case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
+			msgChannelExtendedData(msg, msglen);
+			break;
+		case Packets.SSH_MSG_CHANNEL_REQUEST:
+			msgChannelRequest(msg, msglen);
+			break;
+		case Packets.SSH_MSG_CHANNEL_EOF:
+			msgChannelEOF(msg, msglen);
+			break;
+		case Packets.SSH_MSG_CHANNEL_OPEN:
+			msgChannelOpen(msg, msglen);
+			break;
+		case Packets.SSH_MSG_CHANNEL_CLOSE:
+			msgChannelClose(msg, msglen);
+			break;
+		case Packets.SSH_MSG_CHANNEL_SUCCESS:
+			msgChannelSuccess(msg, msglen);
+			break;
+		case Packets.SSH_MSG_CHANNEL_FAILURE:
+			msgChannelFailure(msg, msglen);
+			break;
+		case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
+			msgChannelOpenFailure(msg, msglen);
+			break;
+		case Packets.SSH_MSG_GLOBAL_REQUEST:
+			msgGlobalRequest(msg, msglen);
+			break;
+		case Packets.SSH_MSG_REQUEST_SUCCESS:
+			msgGlobalSuccess();
+			break;
+		case Packets.SSH_MSG_REQUEST_FAILURE:
+			msgGlobalFailure();
+			break;
+		default:
+			throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/channel/ChannelOutputStream.java b/src/main/java/com/trilead/ssh2/channel/ChannelOutputStream.java
similarity index 95%
rename from src/com/trilead/ssh2/channel/ChannelOutputStream.java
rename to src/main/java/com/trilead/ssh2/channel/ChannelOutputStream.java
index 3fd7214..7c6f6ec 100644
--- a/src/com/trilead/ssh2/channel/ChannelOutputStream.java
+++ b/src/main/java/com/trilead/ssh2/channel/ChannelOutputStream.java
@@ -1,70 +1,70 @@
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * ChannelOutputStream.
- * 
- * @author Christian Plattner, plattner at 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;
-
-	boolean isClosed = false;
-	
-	ChannelOutputStream(Channel c)
-	{
-		this.c = c;
-	}
-
-	public void write(int b) throws IOException
-	{	
-		byte[] buff = new byte[1];
-		
-		buff[0] = (byte) b;
-		
-		write(buff, 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);
-	}
-}
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * ChannelOutputStream.
+ * 
+ * @author Christian Plattner, plattner at 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;
+
+	boolean isClosed = false;
+	
+	ChannelOutputStream(Channel c)
+	{
+		this.c = c;
+	}
+
+	public void write(int b) throws IOException
+	{	
+		byte[] buff = new byte[1];
+		
+		buff[0] = (byte) b;
+		
+		write(buff, 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);
+	}
+}
diff --git a/src/com/trilead/ssh2/channel/IChannelWorkerThread.java b/src/main/java/com/trilead/ssh2/channel/IChannelWorkerThread.java
similarity index 95%
rename from src/com/trilead/ssh2/channel/IChannelWorkerThread.java
rename to src/main/java/com/trilead/ssh2/channel/IChannelWorkerThread.java
index c085421..bce9b1b 100644
--- a/src/com/trilead/ssh2/channel/IChannelWorkerThread.java
+++ b/src/main/java/com/trilead/ssh2/channel/IChannelWorkerThread.java
@@ -1,13 +1,13 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * IChannelWorkerThread.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: IChannelWorkerThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-interface IChannelWorkerThread
-{
-	public void stopWorking();
-}
+
+package com.trilead.ssh2.channel;
+
+/**
+ * IChannelWorkerThread.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: IChannelWorkerThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+interface IChannelWorkerThread
+{
+	public void stopWorking();
+}
diff --git a/src/com/trilead/ssh2/channel/LocalAcceptThread.java b/src/main/java/com/trilead/ssh2/channel/LocalAcceptThread.java
similarity index 95%
rename from src/com/trilead/ssh2/channel/LocalAcceptThread.java
rename to src/main/java/com/trilead/ssh2/channel/LocalAcceptThread.java
index 1b08d9c..6826215 100644
--- a/src/com/trilead/ssh2/channel/LocalAcceptThread.java
+++ b/src/main/java/com/trilead/ssh2/channel/LocalAcceptThread.java
@@ -1,135 +1,135 @@
-
-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 at 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, 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, "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)
-		{
-		}
-	}
-}
+
+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 at 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, 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, "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)
+		{
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/channel/RemoteAcceptThread.java b/src/main/java/com/trilead/ssh2/channel/RemoteAcceptThread.java
similarity index 95%
rename from src/com/trilead/ssh2/channel/RemoteAcceptThread.java
rename to src/main/java/com/trilead/ssh2/channel/RemoteAcceptThread.java
index 1ca9d76..665a4c8 100644
--- a/src/com/trilead/ssh2/channel/RemoteAcceptThread.java
+++ b/src/main/java/com/trilead/ssh2/channel/RemoteAcceptThread.java
@@ -1,103 +1,103 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.net.Socket;
-
-import com.trilead.ssh2.log.Logger;
-
-
-/**
- * RemoteAcceptThread.
- * 
- * @author Christian Plattner, plattner at 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, 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 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)
-			{
-			}
-		}
-	}
-}
+
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import com.trilead.ssh2.log.Logger;
+
+
+/**
+ * RemoteAcceptThread.
+ * 
+ * @author Christian Plattner, plattner at 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, 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 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)
+			{
+			}
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/channel/RemoteForwardingData.java b/src/main/java/com/trilead/ssh2/channel/RemoteForwardingData.java
similarity index 95%
rename from src/com/trilead/ssh2/channel/RemoteForwardingData.java
rename to src/main/java/com/trilead/ssh2/channel/RemoteForwardingData.java
index 0bafce2..d05378e 100644
--- a/src/com/trilead/ssh2/channel/RemoteForwardingData.java
+++ b/src/main/java/com/trilead/ssh2/channel/RemoteForwardingData.java
@@ -1,17 +1,17 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * RemoteForwardingData. Data about a requested remote forwarding.
- * 
- * @author Christian Plattner, plattner at 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;
-}
+
+package com.trilead.ssh2.channel;
+
+/**
+ * RemoteForwardingData. Data about a requested remote forwarding.
+ * 
+ * @author Christian Plattner, plattner at 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;
+}
diff --git a/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java b/src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
similarity index 96%
rename from src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
rename to src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
index 8ee05a2..a5e1a86 100644
--- a/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
+++ b/src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
@@ -1,240 +1,240 @@
-
-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 at 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, 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 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)
-			{
-			}
-		}
-	}
-}
+
+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 at 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, 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 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)
+			{
+			}
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/channel/StreamForwarder.java b/src/main/java/com/trilead/ssh2/channel/StreamForwarder.java
similarity index 94%
rename from src/com/trilead/ssh2/channel/StreamForwarder.java
rename to src/main/java/com/trilead/ssh2/channel/StreamForwarder.java
index 376a3a0..4a62b54 100644
--- a/src/com/trilead/ssh2/channel/StreamForwarder.java
+++ b/src/main/java/com/trilead/ssh2/channel/StreamForwarder.java
@@ -1,112 +1,112 @@
-
-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 at trilead.com
- * @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-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;
-	}
-
-	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)
-				{
-				}
-
-				try
-				{
-					if (s != null)
-						s.close();
-				}
-				catch (IOException e1)
-				{
-				}
-			}
-		}
-	}
+
+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 at trilead.com
+ * @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+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;
+	}
+
+	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)
+				{
+				}
+
+				try
+				{
+					if (s != null)
+						s.close();
+				}
+				catch (IOException e1)
+				{
+				}
+			}
+		}
+	}
 }
\ No newline at end of file
diff --git a/src/com/trilead/ssh2/channel/X11ServerData.java b/src/main/java/com/trilead/ssh2/channel/X11ServerData.java
similarity index 96%
rename from src/com/trilead/ssh2/channel/X11ServerData.java
rename to src/main/java/com/trilead/ssh2/channel/X11ServerData.java
index 0840376..041f9cb 100644
--- a/src/com/trilead/ssh2/channel/X11ServerData.java
+++ b/src/main/java/com/trilead/ssh2/channel/X11ServerData.java
@@ -1,16 +1,16 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * X11ServerData. Data regarding an x11 forwarding target.
- *
- * @author Christian Plattner, plattner at 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 */
-}
+
+package com.trilead.ssh2.channel;
+
+/**
+ * X11ServerData. Data regarding an x11 forwarding target.
+ *
+ * @author Christian Plattner, plattner at 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 */
+}
diff --git a/src/com/trilead/ssh2/crypto/Base64.java b/src/main/java/com/trilead/ssh2/crypto/Base64.java
similarity index 95%
rename from src/com/trilead/ssh2/crypto/Base64.java
rename to src/main/java/com/trilead/ssh2/crypto/Base64.java
index 18a524f..93770ac 100644
--- a/src/com/trilead/ssh2/crypto/Base64.java
+++ b/src/main/java/com/trilead/ssh2/crypto/Base64.java
@@ -1,148 +1,148 @@
-
-package com.trilead.ssh2.crypto;
-
-import java.io.CharArrayWriter;
-import java.io.IOException;
-
-/**
- * Basic Base64 Support.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.crypto;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+
+/**
+ * Basic Base64 Support.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/CryptoWishList.java b/src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java
similarity index 97%
rename from src/com/trilead/ssh2/crypto/CryptoWishList.java
rename to src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java
index c49befa..9654c47 100644
--- a/src/com/trilead/ssh2/crypto/CryptoWishList.java
+++ b/src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java
@@ -1,23 +1,23 @@
-
-package com.trilead.ssh2.crypto;
-
-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 at 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();
-}
+
+package com.trilead.ssh2.crypto;
+
+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 at 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();
+}
diff --git a/src/com/trilead/ssh2/crypto/KeyMaterial.java b/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java
similarity index 96%
rename from src/com/trilead/ssh2/crypto/KeyMaterial.java
rename to src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java
index 5dfb55e..c415c3c 100644
--- a/src/com/trilead/ssh2/crypto/KeyMaterial.java
+++ b/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java
@@ -1,91 +1,91 @@
-
-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 at 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 hashType, 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(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;
-	}
-}
+
+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 at 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 hashType, 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(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;
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/PEMDecoder.java b/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java
similarity index 96%
rename from src/com/trilead/ssh2/crypto/PEMDecoder.java
rename to src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java
index ac1b842..91daad7 100644
--- a/src/com/trilead/ssh2/crypto/PEMDecoder.java
+++ b/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java
@@ -1,377 +1,377 @@
-
-package com.trilead.ssh2.crypto;
-
-import java.io.BufferedReader;
-import java.io.CharArrayReader;
-import java.io.IOException;
-import java.math.BigInteger;
-
-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.crypto.digest.MD5;
-import com.trilead.ssh2.signature.DSAPrivateKey;
-import com.trilead.ssh2.signature.RSAPrivateKey;
-
-/**
- * PEM Support.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: PEMDecoder.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class PEMDecoder
-{
-	private static final int PEM_RSA_PRIVATE_KEY = 1;
-	private static final int PEM_DSA_PRIVATE_KEY = 2;
-
-	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.");
-
-		MD5 md5 = new MD5();
-
-		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;
-
-			md5.digest(tmp, 0);
-
-			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;
-	}
-
-	private 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;
-			}
-		}
-
-		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 Object decode(char[] pem, String password) throws IOException
-	{
-		PEMStructure ps = parsePEM(pem);
-
-		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.");
-
-			return new DSAPrivateKey(p, q, g, y, x);
-		}
-
-		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();
-
-			return new RSAPrivateKey(d, e, n);
-		}
-
-		throw new IOException("PEM problem: it is of unknown type");
-	}
-
-}
+
+package com.trilead.ssh2.crypto;
+
+import java.io.BufferedReader;
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.math.BigInteger;
+
+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.crypto.digest.MD5;
+import com.trilead.ssh2.signature.DSAPrivateKey;
+import com.trilead.ssh2.signature.RSAPrivateKey;
+
+/**
+ * PEM Support.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: PEMDecoder.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class PEMDecoder
+{
+	private static final int PEM_RSA_PRIVATE_KEY = 1;
+	private static final int PEM_DSA_PRIVATE_KEY = 2;
+
+	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.");
+
+		MD5 md5 = new MD5();
+
+		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;
+
+			md5.digest(tmp, 0);
+
+			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;
+	}
+
+	private 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;
+			}
+		}
+
+		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 Object decode(char[] pem, String password) throws IOException
+	{
+		PEMStructure ps = parsePEM(pem);
+
+		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.");
+
+			return new DSAPrivateKey(p, q, g, y, x);
+		}
+
+		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();
+
+			return new RSAPrivateKey(d, e, n);
+		}
+
+		throw new IOException("PEM problem: it is of unknown type");
+	}
+
+}
diff --git a/src/com/trilead/ssh2/crypto/PEMStructure.java b/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java
similarity index 94%
rename from src/com/trilead/ssh2/crypto/PEMStructure.java
rename to src/main/java/com/trilead/ssh2/crypto/PEMStructure.java
index 3bb4b5a..5d4485b 100644
--- a/src/com/trilead/ssh2/crypto/PEMStructure.java
+++ b/src/main/java/com/trilead/ssh2/crypto/PEMStructure.java
@@ -1,17 +1,17 @@
-
-package com.trilead.ssh2.crypto;
-
-/**
- * Parsed PEM structure.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: PEMStructure.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class PEMStructure
-{
-	int pemType;
-	String dekInfo[];
-	String procType[];
-	byte[] data;
+
+package com.trilead.ssh2.crypto;
+
+/**
+ * Parsed PEM structure.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: PEMStructure.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public class PEMStructure
+{
+	int pemType;
+	String dekInfo[];
+	String procType[];
+	byte[] data;
 }
\ No newline at end of file
diff --git a/src/com/trilead/ssh2/crypto/SimpleDERReader.java b/src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java
similarity index 94%
rename from src/com/trilead/ssh2/crypto/SimpleDERReader.java
rename to src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java
index 55c6c6a..9f626ad 100644
--- a/src/com/trilead/ssh2/crypto/SimpleDERReader.java
+++ b/src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java
@@ -1,160 +1,160 @@
-package com.trilead.ssh2.crypto;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * SimpleDERReader.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: SimpleDERReader.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class SimpleDERReader
-{
-	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 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 byte[] readOctetString() throws IOException
-	{
-		int type = readByte() & 0xff;
-		
-		if (type != 0x04)
-			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;
-	}
-
-}
+package com.trilead.ssh2.crypto;
+
+import java.io.IOException;
+
+import java.math.BigInteger;
+
+/**
+ * SimpleDERReader.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: SimpleDERReader.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class SimpleDERReader
+{
+	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 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 byte[] readOctetString() throws IOException
+	{
+		int type = readByte() & 0xff;
+		
+		if (type != 0x04)
+			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;
+	}
+
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/AES.java b/src/main/java/com/trilead/ssh2/crypto/cipher/AES.java
similarity index 98%
rename from src/com/trilead/ssh2/crypto/cipher/AES.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/AES.java
index 15fb410..e89e4a6 100644
--- a/src/com/trilead/ssh2/crypto/cipher/AES.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/AES.java
@@ -1,698 +1,698 @@
-
-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);
-	}
-}
+
+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);
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/BlockCipher.java b/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipher.java
similarity index 96%
rename from src/com/trilead/ssh2/crypto/cipher/BlockCipher.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipher.java
index de60952..4cc28ab 100644
--- a/src/com/trilead/ssh2/crypto/cipher/BlockCipher.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipher.java
@@ -1,16 +1,16 @@
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * BlockCipher.
- * 
- * @author Christian Plattner, plattner at 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);
-}
+package com.trilead.ssh2.crypto.cipher;
+
+/**
+ * BlockCipher.
+ * 
+ * @author Christian Plattner, plattner at 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);
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java b/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java
similarity index 96%
rename from src/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java
index b82664c..84bb38a 100644
--- a/src/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java
@@ -1,115 +1,115 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-import java.util.Vector;
-
-/**
- * BlockCipherFactory.
- * 
- * @author Christian Plattner, plattner at 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 ciphers = new Vector();
-
-	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 = (CipherEntry) 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 = (CipherEntry) 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;
-	}
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+import java.util.Vector;
+
+/**
+ * BlockCipherFactory.
+ * 
+ * @author Christian Plattner, plattner at 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 ciphers = new Vector();
+
+	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 = (CipherEntry) 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 = (CipherEntry) 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/BlowFish.java b/src/main/java/com/trilead/ssh2/crypto/cipher/BlowFish.java
similarity index 98%
rename from src/com/trilead/ssh2/crypto/cipher/BlowFish.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/BlowFish.java
index ea0f614..0b2f295 100644
--- a/src/com/trilead/ssh2/crypto/cipher/BlowFish.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/BlowFish.java
@@ -1,403 +1,403 @@
-
-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);
-	}
-
-}
+
+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);
+	}
+
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/CBCMode.java b/src/main/java/com/trilead/ssh2/crypto/cipher/CBCMode.java
similarity index 95%
rename from src/com/trilead/ssh2/crypto/cipher/CBCMode.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/CBCMode.java
index 60889f0..0ae51b3 100644
--- a/src/com/trilead/ssh2/crypto/cipher/CBCMode.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/CBCMode.java
@@ -1,78 +1,78 @@
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * CBCMode.
- * 
- * @author Christian Plattner, plattner at 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);
-	}
-}
+package com.trilead.ssh2.crypto.cipher;
+
+/**
+ * CBCMode.
+ * 
+ * @author Christian Plattner, plattner at 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);
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/CTRMode.java b/src/main/java/com/trilead/ssh2/crypto/cipher/CTRMode.java
similarity index 95%
rename from src/com/trilead/ssh2/crypto/cipher/CTRMode.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/CTRMode.java
index d456095..8541c8d 100644
--- a/src/com/trilead/ssh2/crypto/cipher/CTRMode.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/CTRMode.java
@@ -1,62 +1,62 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * This is CTR mode as described in draft-ietf-secsh-newmodes-XY.txt
- * 
- * @author Christian Plattner, plattner at 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;
-
-		}
-	}
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+/**
+ * This is CTR mode as described in draft-ietf-secsh-newmodes-XY.txt
+ * 
+ * @author Christian Plattner, plattner at 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;
+
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/CipherInputStream.java b/src/main/java/com/trilead/ssh2/crypto/cipher/CipherInputStream.java
similarity index 95%
rename from src/com/trilead/ssh2/crypto/cipher/CipherInputStream.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/CipherInputStream.java
index 34af1e7..c9055ab 100644
--- a/src/com/trilead/ssh2/crypto/cipher/CipherInputStream.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/CipherInputStream.java
@@ -1,144 +1,144 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * CipherInputStream.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * CipherInputStream.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java b/src/main/java/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java
similarity index 95%
rename from src/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java
index 9db88c2..cf0db4a 100644
--- a/src/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java
@@ -1,142 +1,142 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * CipherOutputStream.
- * 
- * @author Christian Plattner, plattner at 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);
-	}
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * CipherOutputStream.
+ * 
+ * @author Christian Plattner, plattner at 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);
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/DES.java b/src/main/java/com/trilead/ssh2/crypto/cipher/DES.java
similarity index 97%
rename from src/com/trilead/ssh2/crypto/cipher/DES.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/DES.java
index 20480d3..6588459 100644
--- a/src/com/trilead/ssh2/crypto/cipher/DES.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/DES.java
@@ -1,373 +1,373 @@
-
-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);
-	}
-}
+
+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);
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/DESede.java b/src/main/java/com/trilead/ssh2/crypto/cipher/DESede.java
similarity index 96%
rename from src/com/trilead/ssh2/crypto/cipher/DESede.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/DESede.java
index 6c0d797..f47a636 100644
--- a/src/com/trilead/ssh2/crypto/cipher/DESede.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/DESede.java
@@ -1,105 +1,105 @@
-
-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()
-	{
-	}
-}
+
+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()
+	{
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/NullCipher.java b/src/main/java/com/trilead/ssh2/crypto/cipher/NullCipher.java
similarity index 94%
rename from src/com/trilead/ssh2/crypto/cipher/NullCipher.java
rename to src/main/java/com/trilead/ssh2/crypto/cipher/NullCipher.java
index 5a03608..38f8215 100644
--- a/src/com/trilead/ssh2/crypto/cipher/NullCipher.java
+++ b/src/main/java/com/trilead/ssh2/crypto/cipher/NullCipher.java
@@ -1,35 +1,35 @@
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * NullCipher.
- * 
- * @author Christian Plattner, plattner at 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);
-	}
-}
+package com.trilead.ssh2.crypto.cipher;
+
+/**
+ * NullCipher.
+ * 
+ * @author Christian Plattner, plattner at 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);
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/dh/DhExchange.java b/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java
similarity index 96%
rename from src/com/trilead/ssh2/crypto/dh/DhExchange.java
rename to src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java
index 5622a72..f54e3d4 100644
--- a/src/com/trilead/ssh2/crypto/dh/DhExchange.java
+++ b/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java
@@ -1,147 +1,147 @@
-
-package com.trilead.ssh2.crypto.dh;
-
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
-import com.trilead.ssh2.log.Logger;
-
-
-/**
- * DhExchange.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class DhExchange
-{
-	private static final Logger log = Logger.getLogger(DhExchange.class);
-
-	/* Given by the standard */
-
-	static final BigInteger p1, p14;
-	static final BigInteger g;
-
-	BigInteger p;
-
-	/* Client public and private */
-
-	BigInteger e;
-	BigInteger x;
-
-	/* Server public */
-
-	BigInteger f;
-
-	/* Shared secret */
-
-	BigInteger k;
-
-	static
-	{
-		final String p1_string = "17976931348623159077083915679378745319786029604875"
-				+ "60117064444236841971802161585193689478337958649255415021805654859805036464"
-				+ "40548199239100050792877003355816639229553136239076508735759914822574862575"
-				+ "00742530207744771258955095793777842444242661733472762929938766870920560605"
-				+ "0270810842907692932019128194467627007";
-
-		final String p14_string = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129"
-				+ "024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0"
-				+ "A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB"
-				+ "6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A"
-				+ "163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208"
-				+ "552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36C"
-				+ "E3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF69558171"
-				+ "83995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF";
-
-		p1 = new BigInteger(p1_string);
-		p14 = new BigInteger(p14_string, 16);
-		g = new BigInteger("2");
-	}
-
-	public DhExchange()
-	{
-	}
-
-	public void init(int group, SecureRandom rnd)
-	{
-		k = null;
-
-		if (group == 1)
-			p = p1;
-		else if (group == 14)
-			p = p14;
-		else
-			throw new IllegalArgumentException("Unknown DH group " + group);
-
-		x = new BigInteger(p.bitLength() - 1, rnd);
-
-		e = g.modPow(x, p);
-	}
-
-	/**
-	 * @return Returns the e.
-	 * @throws IllegalStateException
-	 */
-	public BigInteger getE()
-	{
-		if (e == null)
-			throw new IllegalStateException("DhDsaExchange not initialized!");
-
-		return e;
-	}
-
-	/**
-	 * @return Returns the shared secret k.
-	 * @throws IllegalStateException
-	 */
-	public BigInteger getK()
-	{
-		if (k == null)
-			throw new IllegalStateException("Shared secret not yet known, need f first!");
-
-		return k;
-	}
-
-	/**
-	 * @param f
-	 */
-	public void setF(BigInteger f)
-	{
-		if (e == null)
-			throw new IllegalStateException("DhDsaExchange 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(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
-			byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException
-	{
-		HashForSSH2Types hash = new HashForSSH2Types("SHA1");
-
-		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.updateBigInt(e);
-		hash.updateBigInt(f);
-		hash.updateBigInt(k);
-
-		return hash.getDigest();
-	}
-}
+
+package com.trilead.ssh2.crypto.dh;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
+import com.trilead.ssh2.log.Logger;
+
+
+/**
+ * DhExchange.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class DhExchange
+{
+	private static final Logger log = Logger.getLogger(DhExchange.class);
+
+	/* Given by the standard */
+
+	static final BigInteger p1, p14;
+	static final BigInteger g;
+
+	BigInteger p;
+
+	/* Client public and private */
+
+	BigInteger e;
+	BigInteger x;
+
+	/* Server public */
+
+	BigInteger f;
+
+	/* Shared secret */
+
+	BigInteger k;
+
+	static
+	{
+		final String p1_string = "17976931348623159077083915679378745319786029604875"
+				+ "60117064444236841971802161585193689478337958649255415021805654859805036464"
+				+ "40548199239100050792877003355816639229553136239076508735759914822574862575"
+				+ "00742530207744771258955095793777842444242661733472762929938766870920560605"
+				+ "0270810842907692932019128194467627007";
+
+		final String p14_string = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129"
+				+ "024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0"
+				+ "A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB"
+				+ "6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A"
+				+ "163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208"
+				+ "552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36C"
+				+ "E3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF69558171"
+				+ "83995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF";
+
+		p1 = new BigInteger(p1_string);
+		p14 = new BigInteger(p14_string, 16);
+		g = new BigInteger("2");
+	}
+
+	public DhExchange()
+	{
+	}
+
+	public void init(int group, SecureRandom rnd)
+	{
+		k = null;
+
+		if (group == 1)
+			p = p1;
+		else if (group == 14)
+			p = p14;
+		else
+			throw new IllegalArgumentException("Unknown DH group " + group);
+
+		x = new BigInteger(p.bitLength() - 1, rnd);
+
+		e = g.modPow(x, p);
+	}
+
+	/**
+	 * @return Returns the e.
+	 * @throws IllegalStateException
+	 */
+	public BigInteger getE()
+	{
+		if (e == null)
+			throw new IllegalStateException("DhDsaExchange not initialized!");
+
+		return e;
+	}
+
+	/**
+	 * @return Returns the shared secret k.
+	 * @throws IllegalStateException
+	 */
+	public BigInteger getK()
+	{
+		if (k == null)
+			throw new IllegalStateException("Shared secret not yet known, need f first!");
+
+		return k;
+	}
+
+	/**
+	 * @param f
+	 */
+	public void setF(BigInteger f)
+	{
+		if (e == null)
+			throw new IllegalStateException("DhDsaExchange 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(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
+			byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException
+	{
+		HashForSSH2Types hash = new HashForSSH2Types("SHA1");
+
+		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.updateBigInt(e);
+		hash.updateBigInt(f);
+		hash.updateBigInt(k);
+
+		return hash.getDigest();
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java b/src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
similarity index 95%
rename from src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
rename to src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
index 8e798ee..2922284 100644
--- a/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
+++ b/src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
@@ -1,113 +1,113 @@
-
-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 at 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(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
-			byte[] serverKexPayload, byte[] hostKey, DHGexParameters para)
-	{
-		HashForSSH2Types hash = new HashForSSH2Types("SHA1");
-
-		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();
-	}
-}
+
+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 at 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(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
+			byte[] serverKexPayload, byte[] hostKey, DHGexParameters para)
+	{
+		HashForSSH2Types hash = new HashForSSH2Types("SHA1");
+
+		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();
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/digest/Digest.java b/src/main/java/com/trilead/ssh2/crypto/digest/Digest.java
similarity index 94%
rename from src/com/trilead/ssh2/crypto/digest/Digest.java
rename to src/main/java/com/trilead/ssh2/crypto/digest/Digest.java
index 6ed969c..10f8baa 100644
--- a/src/com/trilead/ssh2/crypto/digest/Digest.java
+++ b/src/main/java/com/trilead/ssh2/crypto/digest/Digest.java
@@ -1,25 +1,25 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-/**
- * Digest.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: Digest.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-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);
-
-	public void digest(byte[] out, int off);
-}
+
+package com.trilead.ssh2.crypto.digest;
+
+/**
+ * Digest.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: Digest.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+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);
+
+	public void digest(byte[] out, int off);
+}
diff --git a/src/com/trilead/ssh2/crypto/digest/HMAC.java b/src/main/java/com/trilead/ssh2/crypto/digest/HMAC.java
similarity index 93%
rename from src/com/trilead/ssh2/crypto/digest/HMAC.java
rename to src/main/java/com/trilead/ssh2/crypto/digest/HMAC.java
index 8d52758..b5bca11 100644
--- a/src/com/trilead/ssh2/crypto/digest/HMAC.java
+++ b/src/main/java/com/trilead/ssh2/crypto/digest/HMAC.java
@@ -1,95 +1,95 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-/**
- * HMAC.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: HMAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-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)
-	{
-		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)
-	{
-		digest(out, 0);
-	}
-
-	public final void digest(byte[] out, int off)
-	{
-		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);
-	}
-}
+
+package com.trilead.ssh2.crypto.digest;
+
+/**
+ * HMAC.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: HMAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+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)
+	{
+		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)
+	{
+		digest(out, 0);
+	}
+
+	public final void digest(byte[] out, int off)
+	{
+		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);
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java b/src/main/java/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
similarity index 94%
rename from src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
rename to src/main/java/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
index df84952..c17eaa7 100644
--- a/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
+++ b/src/main/java/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
@@ -1,93 +1,93 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-import java.math.BigInteger;
-
-/**
- * HashForSSH2Types.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: HashForSSH2Types.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-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("MD5"))
-		{
-			md = new MD5();
-		}
-		else
-			throw new IllegalArgumentException("Unknown 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)
-	{
-		md.digest(out, off);
-	}
-}
+
+package com.trilead.ssh2.crypto.digest;
+
+import java.math.BigInteger;
+
+/**
+ * HashForSSH2Types.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: HashForSSH2Types.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+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("MD5"))
+		{
+			md = new MD5();
+		}
+		else
+			throw new IllegalArgumentException("Unknown 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)
+	{
+		md.digest(out, off);
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/digest/MAC.java b/src/main/java/com/trilead/ssh2/crypto/digest/MAC.java
similarity index 95%
rename from src/com/trilead/ssh2/crypto/digest/MAC.java
rename to src/main/java/com/trilead/ssh2/crypto/digest/MAC.java
index 0433b63..76a220b 100644
--- a/src/com/trilead/ssh2/crypto/digest/MAC.java
+++ b/src/main/java/com/trilead/ssh2/crypto/digest/MAC.java
@@ -1,88 +1,88 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-/**
- * MAC.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: MAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public final class MAC
-{
-	Digest mac;
-	int size;
-
-	public final static String[] getMacList()
-	{
-		/* Higher Priority First */
-
-		return new String[] { "hmac-sha1-96", "hmac-sha1", "hmac-md5-96", "hmac-md5" };
-	}
-
-	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 (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;
-		throw new IllegalArgumentException("Unkown algorithm " + type);
-	}
-
-	public MAC(String type, byte[] key)
-	{
-		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
-			throw new IllegalArgumentException("Unkown algorithm " + type);
-
-		size = mac.getDigestLength();
-	}
-
-	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)
-	{
-		mac.digest(out, off);
-	}
-
-	public final int size()
-	{
-		return size;
-	}
-}
+
+package com.trilead.ssh2.crypto.digest;
+
+/**
+ * MAC.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: MAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public final class MAC
+{
+	Digest mac;
+	int size;
+
+	public final static String[] getMacList()
+	{
+		/* Higher Priority First */
+
+		return new String[] { "hmac-sha1-96", "hmac-sha1", "hmac-md5-96", "hmac-md5" };
+	}
+
+	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 (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;
+		throw new IllegalArgumentException("Unkown algorithm " + type);
+	}
+
+	public MAC(String type, byte[] key)
+	{
+		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
+			throw new IllegalArgumentException("Unkown algorithm " + type);
+
+		size = mac.getDigestLength();
+	}
+
+	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)
+	{
+		mac.digest(out, off);
+	}
+
+	public final int size()
+	{
+		return size;
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/digest/MD5.java b/src/main/java/com/trilead/ssh2/crypto/digest/MD5.java
similarity index 96%
rename from src/com/trilead/ssh2/crypto/digest/MD5.java
rename to src/main/java/com/trilead/ssh2/crypto/digest/MD5.java
index 567f926..ed01fd9 100644
--- a/src/com/trilead/ssh2/crypto/digest/MD5.java
+++ b/src/main/java/com/trilead/ssh2/crypto/digest/MD5.java
@@ -1,268 +1,268 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-/**
- * MD5. Based on the example code in RFC 1321. Optimized (...a little).
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: MD5.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-
-/*
- * The following disclaimer has been copied from RFC 1321:
- * 
- * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights
- * reserved.
- * 
- * License to copy and use this software is granted provided that it is
- * identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in
- * all material mentioning or referencing this software or this function.
- * 
- * License is also granted to make and use derivative works provided that such
- * works are identified as "derived from the RSA Data Security, Inc. MD5
- * Message-Digest Algorithm" in all material mentioning or referencing the
- * derived work.
- * 
- * RSA Data Security, Inc. makes no representations concerning either the
- * merchantability of this software or the suitability of this software for any
- * particular purpose. It is provided "as is" without express or implied
- * warranty of any kind.
- * 
- * These notices must be retained in any copies of any part of this
- * documentation and/or software.
- * 
- */
-
-public final class MD5 implements Digest
-{
-	private int state0, state1, state2, state3;
-	private long count;
-	private final byte[] block = new byte[64];
-	private final int x[] = new int[16];
-
-	private static final byte[] padding = new byte[] { (byte) 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-			0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
-	public MD5()
-	{
-		reset();
-	}
-
-	private static final int FF(int a, int b, int c, int d, int x, int s, int ac)
-	{
-		a += ((b & c) | ((~b) & d)) + x + ac;
-		return ((a << s) | (a >>> (32 - s))) + b;
-	}
-
-	private static final int GG(int a, int b, int c, int d, int x, int s, int ac)
-	{
-		a += ((b & d) | (c & (~d))) + x + ac;
-		return ((a << s) | (a >>> (32 - s))) + b;
-	}
-
-	private static final int HH(int a, int b, int c, int d, int x, int s, int ac)
-	{
-		a += (b ^ c ^ d) + x + ac;
-		return ((a << s) | (a >>> (32 - s))) + b;
-	}
-
-	private static final int II(int a, int b, int c, int d, int x, int s, int ac)
-	{
-		a += (c ^ (b | (~d))) + x + ac;
-		return ((a << s) | (a >>> (32 - s))) + b;
-	}
-
-	private static final void encode(byte[] dst, int dstoff, int word)
-	{
-		dst[dstoff] = (byte) (word);
-		dst[dstoff + 1] = (byte) (word >> 8);
-		dst[dstoff + 2] = (byte) (word >> 16);
-		dst[dstoff + 3] = (byte) (word >> 24);
-	}
-
-	private final void transform(byte[] src, int pos)
-	{
-		int a = state0;
-		int b = state1;
-		int c = state2;
-		int d = state3;
-
-		for (int i = 0; i < 16; i++, pos += 4)
-		{
-			x[i] = (src[pos] & 0xff) | ((src[pos + 1] & 0xff) << 8) | ((src[pos + 2] & 0xff) << 16)
-					| ((src[pos + 3] & 0xff) << 24);
-		}
-
-		/* Round 1 */
-
-		a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */
-		d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */
-		c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */
-		b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */
-		a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */
-		d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */
-		c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */
-		b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */
-		a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */
-		d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */
-		c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
-		b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
-		a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
-		d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
-		c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
-		b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
-
-		/* Round 2 */
-		a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */
-		d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */
-		c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
-		b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */
-		a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */
-		d = GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */
-		c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
-		b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */
-		a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */
-		d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
-		c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */
-		b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */
-		a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
-		d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */
-		c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */
-		b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
-
-		/* Round 3 */
-		a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */
-		d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */
-		c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
-		b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
-		a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */
-		d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */
-		c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */
-		b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
-		a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
-		d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */
-		c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */
-		b = HH(b, c, d, a, x[6], 23, 0x4881d05); /* 44 */
-		a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */
-		d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
-		c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
-		b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */
-
-		/* Round 4 */
-		a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */
-		d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */
-		c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
-		b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */
-		a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
-		d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */
-		c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
-		b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */
-		a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */
-		d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
-		c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */
-		b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
-		a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */
-		d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
-		c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */
-		b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */
-
-		state0 += a;
-		state1 += b;
-		state2 += c;
-		state3 += d;
-	}
-
-	public final void reset()
-	{
-		count = 0;
-
-		state0 = 0x67452301;
-		state1 = 0xefcdab89;
-		state2 = 0x98badcfe;
-		state3 = 0x10325476;
-
-		/* Clear traces in memory... */
-
-		for (int i = 0; i < 16; i++)
-			x[i] = 0;
-	}
-
-	public final void update(byte b)
-	{
-		final int space = 64 - ((int) (count & 0x3f));
-
-		count++;
-
-		block[64 - space] = b;
-
-		if (space == 1)
-			transform(block, 0);
-	}
-
-	public final void update(byte[] buff, int pos, int len)
-	{
-		int space = 64 - ((int) (count & 0x3f));
-
-		count += len;
-
-		while (len > 0)
-		{
-			if (len < space)
-			{
-				System.arraycopy(buff, pos, block, 64 - space, len);
-				break;
-			}
-
-			if (space == 64)
-			{
-				transform(buff, pos);
-			}
-			else
-			{
-				System.arraycopy(buff, pos, block, 64 - space, space);
-				transform(block, 0);
-			}
-
-			pos += space;
-			len -= space;
-			space = 64;
-		}
-	}
-
-	public final void update(byte[] b)
-	{
-		update(b, 0, b.length);
-	}
-
-	public final void digest(byte[] dst, int pos)
-	{
-		byte[] bits = new byte[8];
-
-		encode(bits, 0, (int) (count << 3));
-		encode(bits, 4, (int) (count >> 29));
-
-		int idx = (int) count & 0x3f;
-		int padLen = (idx < 56) ? (56 - idx) : (120 - idx);
-
-		update(padding, 0, padLen);
-		update(bits, 0, 8);
-
-		encode(dst, pos, state0);
-		encode(dst, pos + 4, state1);
-		encode(dst, pos + 8, state2);
-		encode(dst, pos + 12, state3);
-
-		reset();
-	}
-
-	public final void digest(byte[] dst)
-	{
-		digest(dst, 0);
-	}
-
-	public final int getDigestLength()
-	{
-		return 16;
-	}
-}
+
+package com.trilead.ssh2.crypto.digest;
+
+/**
+ * MD5. Based on the example code in RFC 1321. Optimized (...a little).
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: MD5.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+
+/*
+ * The following disclaimer has been copied from RFC 1321:
+ * 
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights
+ * reserved.
+ * 
+ * License to copy and use this software is granted provided that it is
+ * identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in
+ * all material mentioning or referencing this software or this function.
+ * 
+ * License is also granted to make and use derivative works provided that such
+ * works are identified as "derived from the RSA Data Security, Inc. MD5
+ * Message-Digest Algorithm" in all material mentioning or referencing the
+ * derived work.
+ * 
+ * RSA Data Security, Inc. makes no representations concerning either the
+ * merchantability of this software or the suitability of this software for any
+ * particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ * 
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ * 
+ */
+
+public final class MD5 implements Digest
+{
+	private int state0, state1, state2, state3;
+	private long count;
+	private final byte[] block = new byte[64];
+	private final int x[] = new int[16];
+
+	private static final byte[] padding = new byte[] { (byte) 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	public MD5()
+	{
+		reset();
+	}
+
+	private static final int FF(int a, int b, int c, int d, int x, int s, int ac)
+	{
+		a += ((b & c) | ((~b) & d)) + x + ac;
+		return ((a << s) | (a >>> (32 - s))) + b;
+	}
+
+	private static final int GG(int a, int b, int c, int d, int x, int s, int ac)
+	{
+		a += ((b & d) | (c & (~d))) + x + ac;
+		return ((a << s) | (a >>> (32 - s))) + b;
+	}
+
+	private static final int HH(int a, int b, int c, int d, int x, int s, int ac)
+	{
+		a += (b ^ c ^ d) + x + ac;
+		return ((a << s) | (a >>> (32 - s))) + b;
+	}
+
+	private static final int II(int a, int b, int c, int d, int x, int s, int ac)
+	{
+		a += (c ^ (b | (~d))) + x + ac;
+		return ((a << s) | (a >>> (32 - s))) + b;
+	}
+
+	private static final void encode(byte[] dst, int dstoff, int word)
+	{
+		dst[dstoff] = (byte) (word);
+		dst[dstoff + 1] = (byte) (word >> 8);
+		dst[dstoff + 2] = (byte) (word >> 16);
+		dst[dstoff + 3] = (byte) (word >> 24);
+	}
+
+	private final void transform(byte[] src, int pos)
+	{
+		int a = state0;
+		int b = state1;
+		int c = state2;
+		int d = state3;
+
+		for (int i = 0; i < 16; i++, pos += 4)
+		{
+			x[i] = (src[pos] & 0xff) | ((src[pos + 1] & 0xff) << 8) | ((src[pos + 2] & 0xff) << 16)
+					| ((src[pos + 3] & 0xff) << 24);
+		}
+
+		/* Round 1 */
+
+		a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */
+		d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */
+		c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */
+		b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */
+		a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */
+		d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */
+		c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */
+		b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */
+		a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */
+		d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */
+		c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
+		b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
+		a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
+		d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
+		c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
+		b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
+
+		/* Round 2 */
+		a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */
+		d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */
+		c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
+		b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */
+		a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */
+		d = GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */
+		c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
+		b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */
+		a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */
+		d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
+		c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */
+		b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */
+		a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
+		d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */
+		c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */
+		b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
+
+		/* Round 3 */
+		a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */
+		d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */
+		c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
+		b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
+		a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */
+		d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */
+		c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */
+		b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
+		a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
+		d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */
+		c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */
+		b = HH(b, c, d, a, x[6], 23, 0x4881d05); /* 44 */
+		a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */
+		d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
+		c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
+		b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */
+
+		/* Round 4 */
+		a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */
+		d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */
+		c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
+		b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */
+		a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
+		d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */
+		c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
+		b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */
+		a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */
+		d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
+		c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */
+		b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
+		a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */
+		d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
+		c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */
+		b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */
+
+		state0 += a;
+		state1 += b;
+		state2 += c;
+		state3 += d;
+	}
+
+	public final void reset()
+	{
+		count = 0;
+
+		state0 = 0x67452301;
+		state1 = 0xefcdab89;
+		state2 = 0x98badcfe;
+		state3 = 0x10325476;
+
+		/* Clear traces in memory... */
+
+		for (int i = 0; i < 16; i++)
+			x[i] = 0;
+	}
+
+	public final void update(byte b)
+	{
+		final int space = 64 - ((int) (count & 0x3f));
+
+		count++;
+
+		block[64 - space] = b;
+
+		if (space == 1)
+			transform(block, 0);
+	}
+
+	public final void update(byte[] buff, int pos, int len)
+	{
+		int space = 64 - ((int) (count & 0x3f));
+
+		count += len;
+
+		while (len > 0)
+		{
+			if (len < space)
+			{
+				System.arraycopy(buff, pos, block, 64 - space, len);
+				break;
+			}
+
+			if (space == 64)
+			{
+				transform(buff, pos);
+			}
+			else
+			{
+				System.arraycopy(buff, pos, block, 64 - space, space);
+				transform(block, 0);
+			}
+
+			pos += space;
+			len -= space;
+			space = 64;
+		}
+	}
+
+	public final void update(byte[] b)
+	{
+		update(b, 0, b.length);
+	}
+
+	public final void digest(byte[] dst, int pos)
+	{
+		byte[] bits = new byte[8];
+
+		encode(bits, 0, (int) (count << 3));
+		encode(bits, 4, (int) (count >> 29));
+
+		int idx = (int) count & 0x3f;
+		int padLen = (idx < 56) ? (56 - idx) : (120 - idx);
+
+		update(padding, 0, padLen);
+		update(bits, 0, 8);
+
+		encode(dst, pos, state0);
+		encode(dst, pos + 4, state1);
+		encode(dst, pos + 8, state2);
+		encode(dst, pos + 12, state3);
+
+		reset();
+	}
+
+	public final void digest(byte[] dst)
+	{
+		digest(dst, 0);
+	}
+
+	public final int getDigestLength()
+	{
+		return 16;
+	}
+}
diff --git a/src/com/trilead/ssh2/crypto/digest/SHA1.java b/src/main/java/com/trilead/ssh2/crypto/digest/SHA1.java
similarity index 96%
rename from src/com/trilead/ssh2/crypto/digest/SHA1.java
rename to src/main/java/com/trilead/ssh2/crypto/digest/SHA1.java
index bba4cc6..3ebc540 100644
--- a/src/com/trilead/ssh2/crypto/digest/SHA1.java
+++ b/src/main/java/com/trilead/ssh2/crypto/digest/SHA1.java
@@ -1,664 +1,664 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-/**
- * SHA-1 implementation based on FIPS PUB 180-1.
- * Highly optimized.
- * <p>
- * (http://www.itl.nist.gov/fipspubs/fip180-1.htm)
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: SHA1.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public final class SHA1 implements Digest
-{
-	private int H0, H1, H2, H3, H4;
-
-	private final int[] w = new int[80];
-	private int currentPos;
-	private long currentLen;
-
-	public SHA1()
-	{
-		reset();
-	}
-
-	public final int getDigestLength()
-	{
-		return 20;
-	}
-
-	public final void reset()
-	{
-		H0 = 0x67452301;
-		H1 = 0xEFCDAB89;
-		H2 = 0x98BADCFE;
-		H3 = 0x10325476;
-		H4 = 0xC3D2E1F0;
-
-		currentPos = 0;
-		currentLen = 0;
-
-		/* In case of complete paranoia, we should also wipe out the
-		 * information contained in the w[] array */
-	}
-
-	public final void update(byte b[])
-	{
-		update(b, 0, b.length);
-	}
-
-	public final void update(byte b[], int off, int len)
-	{
-		if (len >= 4)
-		{
-			int idx = currentPos >> 2;
-
-			switch (currentPos & 3)
-			{
-			case 0:
-				w[idx] = (((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
-				len -= 4;
-				currentPos += 4;
-				currentLen += 32;
-				if (currentPos == 64)
-				{
-					perform();
-					currentPos = 0;
-				}
-				break;
-			case 1:
-				w[idx] = (w[idx] << 24) | (((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
-				len -= 3;
-				currentPos += 3;
-				currentLen += 24;
-				if (currentPos == 64)
-				{
-					perform();
-					currentPos = 0;
-				}
-				break;
-			case 2:
-				w[idx] = (w[idx] << 16) | (((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
-				len -= 2;
-				currentPos += 2;
-				currentLen += 16;
-				if (currentPos == 64)
-				{
-					perform();
-					currentPos = 0;
-				}
-				break;
-			case 3:
-				w[idx] = (w[idx] << 8) | (b[off++] & 0xff);
-				len--;
-				currentPos++;
-				currentLen += 8;
-				if (currentPos == 64)
-				{
-					perform();
-					currentPos = 0;
-				}
-				break;
-			}
-
-			/* Now currentPos is a multiple of 4 - this is the place to be...*/
-
-			while (len >= 8)
-			{
-				w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
-						| (b[off++] & 0xff);
-				currentPos += 4;
-
-				if (currentPos == 64)
-				{
-					perform();
-					currentPos = 0;
-				}
-
-				w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
-						| (b[off++] & 0xff);
-
-				currentPos += 4;
-
-				if (currentPos == 64)
-				{
-					perform();
-					currentPos = 0;
-				}
-
-				currentLen += 64;
-				len -= 8;
-			}
-
-			while (len < 0) //(len >= 4)
-			{
-				w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
-						| (b[off++] & 0xff);
-				len -= 4;
-				currentPos += 4;
-				currentLen += 32;
-				if (currentPos == 64)
-				{
-					perform();
-					currentPos = 0;
-				}
-			}
-		}
-
-		/* Remaining bytes (1-3) */
-
-		while (len > 0)
-		{
-			/* Here is room for further improvements */
-			int idx = currentPos >> 2;
-			w[idx] = (w[idx] << 8) | (b[off++] & 0xff);
-
-			currentLen += 8;
-			currentPos++;
-
-			if (currentPos == 64)
-			{
-				perform();
-				currentPos = 0;
-			}
-			len--;
-		}
-	}
-
-	public final void update(byte b)
-	{
-		int idx = currentPos >> 2;
-		w[idx] = (w[idx] << 8) | (b & 0xff);
-
-		currentLen += 8;
-		currentPos++;
-
-		if (currentPos == 64)
-		{
-			perform();
-			currentPos = 0;
-		}
-	}
-
-	private final void putInt(byte[] b, int pos, int val)
-	{
-		b[pos] = (byte) (val >> 24);
-		b[pos + 1] = (byte) (val >> 16);
-		b[pos + 2] = (byte) (val >> 8);
-		b[pos + 3] = (byte) val;
-	}
-
-	public final void digest(byte[] out)
-	{
-		digest(out, 0);
-	}
-
-	public final void digest(byte[] out, int off)
-	{
-		/* Pad with a '1' and 7-31 zero bits... */
-
-		int idx = currentPos >> 2;
-		w[idx] = ((w[idx] << 8) | (0x80)) << ((3 - (currentPos & 3)) << 3);
-
-		currentPos = (currentPos & ~3) + 4;
-
-		if (currentPos == 64)
-		{
-			currentPos = 0;
-			perform();
-		}
-		else if (currentPos == 60)
-		{
-			currentPos = 0;
-			w[15] = 0;
-			perform();
-		}
-
-		/* Now currentPos is a multiple of 4 and we can do the remaining
-		 * padding much more efficiently, furthermore we are sure
-		 * that currentPos <= 56.
-		 */
-
-		for (int i = currentPos >> 2; i < 14; i++)
-			w[i] = 0;
-
-		w[14] = (int) (currentLen >> 32);
-		w[15] = (int) currentLen;
-
-		perform();
-
-		putInt(out, off, H0);
-		putInt(out, off + 4, H1);
-		putInt(out, off + 8, H2);
-		putInt(out, off + 12, H3);
-		putInt(out, off + 16, H4);
-
-		reset();
-	}
-
-	private final void perform()
-	{
-		for (int t = 16; t < 80; t++)
-		{
-			int x = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
-			w[t] = ((x << 1) | (x >>> 31));
-		}
-
-		int A = H0;
-		int B = H1;
-		int C = H2;
-		int D = H3;
-		int E = H4;
-
-		/* Here we use variable substitution and loop unrolling
-		 * 
-		 * === Original step:
-		 * 
-		 * T = s5(A) + f(B,C,D) + E + w[0] + K;
-		 * E = D; D = C; C = s30(B); B = A; A = T;
-		 * 
-		 * === Rewritten step:
-		 * 
-		 * T = s5(A + f(B,C,D) + E + w[0] + K;
-		 * B = s30(B);
-		 * E = D; D = C; C = B; B = A; A = T;
-		 * 
-		 * === Let's rewrite things, introducing new variables:
-		 * 
-		 * E0 = E; D0 = D; C0 = C; B0 = B; A0 = A;
-		 * 
-		 * T = s5(A0) + f(B0,C0,D0) + E0 + w[0] + K;
-		 * B0 = s30(B0);
-		 * E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = T;
-		 * 
-		 * T = s5(A1) + f(B1,C1,D1) + E1 + w[1] + K;
-		 * B1 = s30(B1);
-		 * E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = T;
-		 * 
-		 * E = E2; D = E2; C = C2; B = B2; A = A2;
-		 * 
-		 * === No need for 'T', we can write into 'Ex' instead since
-		 * after the calculation of 'T' nobody is interested
-		 * in 'Ex' anymore. 
-		 * 
-		 * E0 = E; D0 = D; C0 = C; B0 = B; A0 = A;
-		 * 
-		 * E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K;
-		 * B0 = s30(B0);
-		 * E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0;
-		 * 
-		 * E1 = E1 + s5(A1) + f(B1,C1,D1) + w[1] + K;
-		 * B1 = s30(B1);
-		 * E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = E1;
-		 * 
-		 * E = Ex; D = Ex; C = Cx; B = Bx; A = Ax;
-		 * 
-		 * === Further optimization: get rid of the swap operations
-		 * Idea: instead of swapping the variables, swap the names of
-		 * the used variables in the next step:
-		 * 
-		 * E0 = E; D0 = d; C0 = C; B0 = B; A0 = A;
-		 * 
-		 * E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K;
-		 * B0 = s30(B0);
-		 * // E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0;
-		 * 
-		 * D0 = D0 + s5(E0) + f(A0,B0,C0) + w[1] + K;
-		 * A0 = s30(A0);
-		 * E2 = C0; D2 = B0; C2 = A0; B2 = E0; A2 = D0;
-		 * 
-		 * E = E2; D = D2; C = C2; B = B2; A = A2;
-		 * 
-		 * === OK, let's do this several times, also, directly
-		 * use A (instead of A0) and B,C,D,E.
-		 * 
-		 * E = E + s5(A) + f(B,C,D) + w[0] + K;
-		 * B = s30(B);
-		 * // E1 = D; D1 = C; C1 = B; B1 = A; A1 = E;
-		 * 
-		 * D = D + s5(E) + f(A,B,C) + w[1] + K;
-		 * A = s30(A);
-		 * // E2 = C; D2 = B; C2 = A; B2 = E; A2 = D;
-		 * 
-		 * C = C + s5(D) + f(E,A,B) + w[2] + K;
-		 * E = s30(E);
-		 * // E3 = B; D3 = A; C3 = E; B3 = D; A3 = C;
-		 * 
-		 * B = B + s5(C) + f(D,E,A) + w[3] + K;
-		 * D = s30(D);
-		 * // E4 = A; D4 = E; C4 = D; B4 = C; A4 = B;
-		 * 
-		 * A = A + s5(B) + f(C,D,E) + w[4] + K;
-		 * C = s30(C);
-		 * // E5 = E; D5 = D; C5 = C; B5 = B; A5 = A;
-		 * 
-		 * //E = E5; D = D5; C = C5; B = B5; A = A5;
-		 * 
-		 * === Very nice, after 5 steps each variable
-		 * has the same contents as after 5 steps with
-		 * the original algorithm!
-		 * 
-		 * We therefore can easily unroll each interval,
-		 * as the number of steps in each interval is a
-		 * multiple of 5 (20 steps per interval).
-		 */
-
-		E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[0] + 0x5A827999;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[1] + 0x5A827999;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[2] + 0x5A827999;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[3] + 0x5A827999;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[4] + 0x5A827999;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[5] + 0x5A827999;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[6] + 0x5A827999;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[7] + 0x5A827999;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[8] + 0x5A827999;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[9] + 0x5A827999;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[10] + 0x5A827999;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[11] + 0x5A827999;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[12] + 0x5A827999;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[13] + 0x5A827999;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[14] + 0x5A827999;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[15] + 0x5A827999;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[16] + 0x5A827999;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[17] + 0x5A827999;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[18] + 0x5A827999;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[19] + 0x5A827999;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[20] + 0x6ED9EBA1;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[21] + 0x6ED9EBA1;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[22] + 0x6ED9EBA1;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[23] + 0x6ED9EBA1;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[24] + 0x6ED9EBA1;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[25] + 0x6ED9EBA1;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[26] + 0x6ED9EBA1;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[27] + 0x6ED9EBA1;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[28] + 0x6ED9EBA1;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[29] + 0x6ED9EBA1;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[30] + 0x6ED9EBA1;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[31] + 0x6ED9EBA1;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[32] + 0x6ED9EBA1;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[33] + 0x6ED9EBA1;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[34] + 0x6ED9EBA1;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[35] + 0x6ED9EBA1;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[36] + 0x6ED9EBA1;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[37] + 0x6ED9EBA1;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[38] + 0x6ED9EBA1;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[39] + 0x6ED9EBA1;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[40] + 0x8F1BBCDC;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[41] + 0x8F1BBCDC;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[42] + 0x8F1BBCDC;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[43] + 0x8F1BBCDC;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[44] + 0x8F1BBCDC;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[45] + 0x8F1BBCDC;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[46] + 0x8F1BBCDC;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[47] + 0x8F1BBCDC;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[48] + 0x8F1BBCDC;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[49] + 0x8F1BBCDC;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[50] + 0x8F1BBCDC;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[51] + 0x8F1BBCDC;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[52] + 0x8F1BBCDC;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[53] + 0x8F1BBCDC;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[54] + 0x8F1BBCDC;
-		C = ((C << 30) | (C >>> 2));
-
-		E = E + ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[55] + 0x8F1BBCDC;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[56] + 0x8F1BBCDC;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[57] + 0x8F1BBCDC;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[58] + 0x8F1BBCDC;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[59] + 0x8F1BBCDC;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[60] + 0xCA62C1D6;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[61] + 0xCA62C1D6;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[62] + 0xCA62C1D6;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[63] + 0xCA62C1D6;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[64] + 0xCA62C1D6;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[65] + 0xCA62C1D6;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[66] + 0xCA62C1D6;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[67] + 0xCA62C1D6;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[68] + 0xCA62C1D6;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[69] + 0xCA62C1D6;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[70] + 0xCA62C1D6;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[71] + 0xCA62C1D6;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[72] + 0xCA62C1D6;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[73] + 0xCA62C1D6;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[74] + 0xCA62C1D6;
-		C = ((C << 30) | (C >>> 2));
-
-		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[75] + 0xCA62C1D6;
-		B = ((B << 30) | (B >>> 2));
-
-		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[76] + 0xCA62C1D6;
-		A = ((A << 30) | (A >>> 2));
-
-		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[77] + 0xCA62C1D6;
-		E = ((E << 30) | (E >>> 2));
-
-		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[78] + 0xCA62C1D6;
-		D = ((D << 30) | (D >>> 2));
-
-		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[79] + 0xCA62C1D6;
-		C = ((C << 30) | (C >>> 2));
-
-		H0 += A;
-		H1 += B;
-		H2 += C;
-		H3 += D;
-		H4 += E;
-
-		// debug(80, H0, H1, H2, H3, H4);
-	}
-
-	private static final String toHexString(byte[] b)
-	{
-		final String hexChar = "0123456789ABCDEF";
-
-		StringBuffer sb = new StringBuffer();
-		for (int i = 0; i < b.length; i++)
-		{
-			sb.append(hexChar.charAt((b[i] >> 4) & 0x0f));
-			sb.append(hexChar.charAt(b[i] & 0x0f));
-		}
-		return sb.toString();
-	}
-
-	public static void main(String[] args)
-	{
-		SHA1 sha = new SHA1();
-
-		byte[] dig1 = new byte[20];
-		byte[] dig2 = new byte[20];
-		byte[] dig3 = new byte[20];
-
-		/*
-		 * We do not specify a charset name for getBytes(), since we assume that
-		 * the JVM's default encoder maps the _used_ ASCII characters exactly as
-		 * getBytes("US-ASCII") would do. (Ah, yes, too lazy to catch the
-		 * exception that can be thrown by getBytes("US-ASCII")). Note: This has
-		 * no effect on the SHA-1 implementation, this is just for the following
-		 * test code.
-		 */
-
-		sha.update("abc".getBytes());
-		sha.digest(dig1);
-
-		sha.update("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".getBytes());
-		sha.digest(dig2);
-
-		for (int i = 0; i < 1000000; i++)
-			sha.update((byte) 'a');
-		sha.digest(dig3);
-
-		String dig1_res = toHexString(dig1);
-		String dig2_res = toHexString(dig2);
-		String dig3_res = toHexString(dig3);
-
-		String dig1_ref = "A9993E364706816ABA3E25717850C26C9CD0D89D";
-		String dig2_ref = "84983E441C3BD26EBAAE4AA1F95129E5E54670F1";
-		String dig3_ref = "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F";
-
-		if (dig1_res.equals(dig1_ref))
-			System.out.println("SHA-1 Test 1 OK.");
-		else
-			System.out.println("SHA-1 Test 1 FAILED.");
-
-		if (dig2_res.equals(dig2_ref))
-			System.out.println("SHA-1 Test 2 OK.");
-		else
-			System.out.println("SHA-1 Test 2 FAILED.");
-
-		if (dig3_res.equals(dig3_ref))
-			System.out.println("SHA-1 Test 3 OK.");
-		else
-			System.out.println("SHA-1 Test 3 FAILED.");
-
-		if (dig3_res.equals(dig3_ref))
-			System.out.println("SHA-1 Test 3 OK.");
-		else
-			System.out.println("SHA-1 Test 3 FAILED.");
-	}
-}
+
+package com.trilead.ssh2.crypto.digest;
+
+/**
+ * SHA-1 implementation based on FIPS PUB 180-1.
+ * Highly optimized.
+ * <p>
+ * (http://www.itl.nist.gov/fipspubs/fip180-1.htm)
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: SHA1.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public final class SHA1 implements Digest
+{
+	private int H0, H1, H2, H3, H4;
+
+	private final int[] w = new int[80];
+	private int currentPos;
+	private long currentLen;
+
+	public SHA1()
+	{
+		reset();
+	}
+
+	public final int getDigestLength()
+	{
+		return 20;
+	}
+
+	public final void reset()
+	{
+		H0 = 0x67452301;
+		H1 = 0xEFCDAB89;
+		H2 = 0x98BADCFE;
+		H3 = 0x10325476;
+		H4 = 0xC3D2E1F0;
+
+		currentPos = 0;
+		currentLen = 0;
+
+		/* In case of complete paranoia, we should also wipe out the
+		 * information contained in the w[] array */
+	}
+
+	public final void update(byte b[])
+	{
+		update(b, 0, b.length);
+	}
+
+	public final void update(byte b[], int off, int len)
+	{
+		if (len >= 4)
+		{
+			int idx = currentPos >> 2;
+
+			switch (currentPos & 3)
+			{
+			case 0:
+				w[idx] = (((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
+				len -= 4;
+				currentPos += 4;
+				currentLen += 32;
+				if (currentPos == 64)
+				{
+					perform();
+					currentPos = 0;
+				}
+				break;
+			case 1:
+				w[idx] = (w[idx] << 24) | (((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
+				len -= 3;
+				currentPos += 3;
+				currentLen += 24;
+				if (currentPos == 64)
+				{
+					perform();
+					currentPos = 0;
+				}
+				break;
+			case 2:
+				w[idx] = (w[idx] << 16) | (((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
+				len -= 2;
+				currentPos += 2;
+				currentLen += 16;
+				if (currentPos == 64)
+				{
+					perform();
+					currentPos = 0;
+				}
+				break;
+			case 3:
+				w[idx] = (w[idx] << 8) | (b[off++] & 0xff);
+				len--;
+				currentPos++;
+				currentLen += 8;
+				if (currentPos == 64)
+				{
+					perform();
+					currentPos = 0;
+				}
+				break;
+			}
+
+			/* Now currentPos is a multiple of 4 - this is the place to be...*/
+
+			while (len >= 8)
+			{
+				w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
+						| (b[off++] & 0xff);
+				currentPos += 4;
+
+				if (currentPos == 64)
+				{
+					perform();
+					currentPos = 0;
+				}
+
+				w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
+						| (b[off++] & 0xff);
+
+				currentPos += 4;
+
+				if (currentPos == 64)
+				{
+					perform();
+					currentPos = 0;
+				}
+
+				currentLen += 64;
+				len -= 8;
+			}
+
+			while (len < 0) //(len >= 4)
+			{
+				w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
+						| (b[off++] & 0xff);
+				len -= 4;
+				currentPos += 4;
+				currentLen += 32;
+				if (currentPos == 64)
+				{
+					perform();
+					currentPos = 0;
+				}
+			}
+		}
+
+		/* Remaining bytes (1-3) */
+
+		while (len > 0)
+		{
+			/* Here is room for further improvements */
+			int idx = currentPos >> 2;
+			w[idx] = (w[idx] << 8) | (b[off++] & 0xff);
+
+			currentLen += 8;
+			currentPos++;
+
+			if (currentPos == 64)
+			{
+				perform();
+				currentPos = 0;
+			}
+			len--;
+		}
+	}
+
+	public final void update(byte b)
+	{
+		int idx = currentPos >> 2;
+		w[idx] = (w[idx] << 8) | (b & 0xff);
+
+		currentLen += 8;
+		currentPos++;
+
+		if (currentPos == 64)
+		{
+			perform();
+			currentPos = 0;
+		}
+	}
+
+	private final void putInt(byte[] b, int pos, int val)
+	{
+		b[pos] = (byte) (val >> 24);
+		b[pos + 1] = (byte) (val >> 16);
+		b[pos + 2] = (byte) (val >> 8);
+		b[pos + 3] = (byte) val;
+	}
+
+	public final void digest(byte[] out)
+	{
+		digest(out, 0);
+	}
+
+	public final void digest(byte[] out, int off)
+	{
+		/* Pad with a '1' and 7-31 zero bits... */
+
+		int idx = currentPos >> 2;
+		w[idx] = ((w[idx] << 8) | (0x80)) << ((3 - (currentPos & 3)) << 3);
+
+		currentPos = (currentPos & ~3) + 4;
+
+		if (currentPos == 64)
+		{
+			currentPos = 0;
+			perform();
+		}
+		else if (currentPos == 60)
+		{
+			currentPos = 0;
+			w[15] = 0;
+			perform();
+		}
+
+		/* Now currentPos is a multiple of 4 and we can do the remaining
+		 * padding much more efficiently, furthermore we are sure
+		 * that currentPos <= 56.
+		 */
+
+		for (int i = currentPos >> 2; i < 14; i++)
+			w[i] = 0;
+
+		w[14] = (int) (currentLen >> 32);
+		w[15] = (int) currentLen;
+
+		perform();
+
+		putInt(out, off, H0);
+		putInt(out, off + 4, H1);
+		putInt(out, off + 8, H2);
+		putInt(out, off + 12, H3);
+		putInt(out, off + 16, H4);
+
+		reset();
+	}
+
+	private final void perform()
+	{
+		for (int t = 16; t < 80; t++)
+		{
+			int x = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
+			w[t] = ((x << 1) | (x >>> 31));
+		}
+
+		int A = H0;
+		int B = H1;
+		int C = H2;
+		int D = H3;
+		int E = H4;
+
+		/* Here we use variable substitution and loop unrolling
+		 * 
+		 * === Original step:
+		 * 
+		 * T = s5(A) + f(B,C,D) + E + w[0] + K;
+		 * E = D; D = C; C = s30(B); B = A; A = T;
+		 * 
+		 * === Rewritten step:
+		 * 
+		 * T = s5(A + f(B,C,D) + E + w[0] + K;
+		 * B = s30(B);
+		 * E = D; D = C; C = B; B = A; A = T;
+		 * 
+		 * === Let's rewrite things, introducing new variables:
+		 * 
+		 * E0 = E; D0 = D; C0 = C; B0 = B; A0 = A;
+		 * 
+		 * T = s5(A0) + f(B0,C0,D0) + E0 + w[0] + K;
+		 * B0 = s30(B0);
+		 * E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = T;
+		 * 
+		 * T = s5(A1) + f(B1,C1,D1) + E1 + w[1] + K;
+		 * B1 = s30(B1);
+		 * E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = T;
+		 * 
+		 * E = E2; D = E2; C = C2; B = B2; A = A2;
+		 * 
+		 * === No need for 'T', we can write into 'Ex' instead since
+		 * after the calculation of 'T' nobody is interested
+		 * in 'Ex' anymore. 
+		 * 
+		 * E0 = E; D0 = D; C0 = C; B0 = B; A0 = A;
+		 * 
+		 * E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K;
+		 * B0 = s30(B0);
+		 * E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0;
+		 * 
+		 * E1 = E1 + s5(A1) + f(B1,C1,D1) + w[1] + K;
+		 * B1 = s30(B1);
+		 * E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = E1;
+		 * 
+		 * E = Ex; D = Ex; C = Cx; B = Bx; A = Ax;
+		 * 
+		 * === Further optimization: get rid of the swap operations
+		 * Idea: instead of swapping the variables, swap the names of
+		 * the used variables in the next step:
+		 * 
+		 * E0 = E; D0 = d; C0 = C; B0 = B; A0 = A;
+		 * 
+		 * E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K;
+		 * B0 = s30(B0);
+		 * // E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0;
+		 * 
+		 * D0 = D0 + s5(E0) + f(A0,B0,C0) + w[1] + K;
+		 * A0 = s30(A0);
+		 * E2 = C0; D2 = B0; C2 = A0; B2 = E0; A2 = D0;
+		 * 
+		 * E = E2; D = D2; C = C2; B = B2; A = A2;
+		 * 
+		 * === OK, let's do this several times, also, directly
+		 * use A (instead of A0) and B,C,D,E.
+		 * 
+		 * E = E + s5(A) + f(B,C,D) + w[0] + K;
+		 * B = s30(B);
+		 * // E1 = D; D1 = C; C1 = B; B1 = A; A1 = E;
+		 * 
+		 * D = D + s5(E) + f(A,B,C) + w[1] + K;
+		 * A = s30(A);
+		 * // E2 = C; D2 = B; C2 = A; B2 = E; A2 = D;
+		 * 
+		 * C = C + s5(D) + f(E,A,B) + w[2] + K;
+		 * E = s30(E);
+		 * // E3 = B; D3 = A; C3 = E; B3 = D; A3 = C;
+		 * 
+		 * B = B + s5(C) + f(D,E,A) + w[3] + K;
+		 * D = s30(D);
+		 * // E4 = A; D4 = E; C4 = D; B4 = C; A4 = B;
+		 * 
+		 * A = A + s5(B) + f(C,D,E) + w[4] + K;
+		 * C = s30(C);
+		 * // E5 = E; D5 = D; C5 = C; B5 = B; A5 = A;
+		 * 
+		 * //E = E5; D = D5; C = C5; B = B5; A = A5;
+		 * 
+		 * === Very nice, after 5 steps each variable
+		 * has the same contents as after 5 steps with
+		 * the original algorithm!
+		 * 
+		 * We therefore can easily unroll each interval,
+		 * as the number of steps in each interval is a
+		 * multiple of 5 (20 steps per interval).
+		 */
+
+		E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[0] + 0x5A827999;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[1] + 0x5A827999;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[2] + 0x5A827999;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[3] + 0x5A827999;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[4] + 0x5A827999;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[5] + 0x5A827999;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[6] + 0x5A827999;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[7] + 0x5A827999;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[8] + 0x5A827999;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[9] + 0x5A827999;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[10] + 0x5A827999;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[11] + 0x5A827999;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[12] + 0x5A827999;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[13] + 0x5A827999;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[14] + 0x5A827999;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[15] + 0x5A827999;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[16] + 0x5A827999;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[17] + 0x5A827999;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[18] + 0x5A827999;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[19] + 0x5A827999;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[20] + 0x6ED9EBA1;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[21] + 0x6ED9EBA1;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[22] + 0x6ED9EBA1;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[23] + 0x6ED9EBA1;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[24] + 0x6ED9EBA1;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[25] + 0x6ED9EBA1;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[26] + 0x6ED9EBA1;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[27] + 0x6ED9EBA1;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[28] + 0x6ED9EBA1;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[29] + 0x6ED9EBA1;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[30] + 0x6ED9EBA1;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[31] + 0x6ED9EBA1;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[32] + 0x6ED9EBA1;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[33] + 0x6ED9EBA1;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[34] + 0x6ED9EBA1;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[35] + 0x6ED9EBA1;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[36] + 0x6ED9EBA1;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[37] + 0x6ED9EBA1;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[38] + 0x6ED9EBA1;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[39] + 0x6ED9EBA1;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[40] + 0x8F1BBCDC;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[41] + 0x8F1BBCDC;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[42] + 0x8F1BBCDC;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[43] + 0x8F1BBCDC;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[44] + 0x8F1BBCDC;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[45] + 0x8F1BBCDC;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[46] + 0x8F1BBCDC;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[47] + 0x8F1BBCDC;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[48] + 0x8F1BBCDC;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[49] + 0x8F1BBCDC;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[50] + 0x8F1BBCDC;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[51] + 0x8F1BBCDC;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[52] + 0x8F1BBCDC;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[53] + 0x8F1BBCDC;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[54] + 0x8F1BBCDC;
+		C = ((C << 30) | (C >>> 2));
+
+		E = E + ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[55] + 0x8F1BBCDC;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[56] + 0x8F1BBCDC;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[57] + 0x8F1BBCDC;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[58] + 0x8F1BBCDC;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[59] + 0x8F1BBCDC;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[60] + 0xCA62C1D6;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[61] + 0xCA62C1D6;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[62] + 0xCA62C1D6;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[63] + 0xCA62C1D6;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[64] + 0xCA62C1D6;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[65] + 0xCA62C1D6;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[66] + 0xCA62C1D6;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[67] + 0xCA62C1D6;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[68] + 0xCA62C1D6;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[69] + 0xCA62C1D6;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[70] + 0xCA62C1D6;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[71] + 0xCA62C1D6;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[72] + 0xCA62C1D6;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[73] + 0xCA62C1D6;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[74] + 0xCA62C1D6;
+		C = ((C << 30) | (C >>> 2));
+
+		E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[75] + 0xCA62C1D6;
+		B = ((B << 30) | (B >>> 2));
+
+		D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[76] + 0xCA62C1D6;
+		A = ((A << 30) | (A >>> 2));
+
+		C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[77] + 0xCA62C1D6;
+		E = ((E << 30) | (E >>> 2));
+
+		B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[78] + 0xCA62C1D6;
+		D = ((D << 30) | (D >>> 2));
+
+		A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[79] + 0xCA62C1D6;
+		C = ((C << 30) | (C >>> 2));
+
+		H0 += A;
+		H1 += B;
+		H2 += C;
+		H3 += D;
+		H4 += E;
+
+		// debug(80, H0, H1, H2, H3, H4);
+	}
+
+	private static final String toHexString(byte[] b)
+	{
+		final String hexChar = "0123456789ABCDEF";
+
+		StringBuffer sb = new StringBuffer();
+		for (int i = 0; i < b.length; i++)
+		{
+			sb.append(hexChar.charAt((b[i] >> 4) & 0x0f));
+			sb.append(hexChar.charAt(b[i] & 0x0f));
+		}
+		return sb.toString();
+	}
+
+	public static void main(String[] args)
+	{
+		SHA1 sha = new SHA1();
+
+		byte[] dig1 = new byte[20];
+		byte[] dig2 = new byte[20];
+		byte[] dig3 = new byte[20];
+
+		/*
+		 * We do not specify a charset name for getBytes(), since we assume that
+		 * the JVM's default encoder maps the _used_ ASCII characters exactly as
+		 * getBytes("US-ASCII") would do. (Ah, yes, too lazy to catch the
+		 * exception that can be thrown by getBytes("US-ASCII")). Note: This has
+		 * no effect on the SHA-1 implementation, this is just for the following
+		 * test code.
+		 */
+
+		sha.update("abc".getBytes());
+		sha.digest(dig1);
+
+		sha.update("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".getBytes());
+		sha.digest(dig2);
+
+		for (int i = 0; i < 1000000; i++)
+			sha.update((byte) 'a');
+		sha.digest(dig3);
+
+		String dig1_res = toHexString(dig1);
+		String dig2_res = toHexString(dig2);
+		String dig3_res = toHexString(dig3);
+
+		String dig1_ref = "A9993E364706816ABA3E25717850C26C9CD0D89D";
+		String dig2_ref = "84983E441C3BD26EBAAE4AA1F95129E5E54670F1";
+		String dig3_ref = "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F";
+
+		if (dig1_res.equals(dig1_ref))
+			System.out.println("SHA-1 Test 1 OK.");
+		else
+			System.out.println("SHA-1 Test 1 FAILED.");
+
+		if (dig2_res.equals(dig2_ref))
+			System.out.println("SHA-1 Test 2 OK.");
+		else
+			System.out.println("SHA-1 Test 2 FAILED.");
+
+		if (dig3_res.equals(dig3_ref))
+			System.out.println("SHA-1 Test 3 OK.");
+		else
+			System.out.println("SHA-1 Test 3 FAILED.");
+
+		if (dig3_res.equals(dig3_ref))
+			System.out.println("SHA-1 Test 3 OK.");
+		else
+			System.out.println("SHA-1 Test 3 FAILED.");
+	}
+}
diff --git a/src/com/trilead/ssh2/log/Logger.java b/src/main/java/com/trilead/ssh2/log/Logger.java
similarity index 95%
rename from src/com/trilead/ssh2/log/Logger.java
rename to src/main/java/com/trilead/ssh2/log/Logger.java
index fe388f7..20ab397 100644
--- a/src/com/trilead/ssh2/log/Logger.java
+++ b/src/main/java/com/trilead/ssh2/log/Logger.java
@@ -1,54 +1,54 @@
-
-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 at 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);
-	}
-}
+
+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 at 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);
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java b/src/main/java/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java
rename to src/main/java/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java
index 111f6a2..bd2ea3f 100644
--- a/src/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java
@@ -1,66 +1,66 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketChannelOpenConfirmation.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketChannelOpenConfirmation.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketChannelOpenFailure.java b/src/main/java/com/trilead/ssh2/packets/PacketChannelOpenFailure.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketChannelOpenFailure.java
rename to src/main/java/com/trilead/ssh2/packets/PacketChannelOpenFailure.java
index 5436ce3..1370355 100644
--- a/src/com/trilead/ssh2/packets/PacketChannelOpenFailure.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketChannelOpenFailure.java
@@ -1,66 +1,66 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketChannelOpenFailure.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketChannelOpenFailure.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketChannelTrileadPing.java b/src/main/java/com/trilead/ssh2/packets/PacketChannelTrileadPing.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketChannelTrileadPing.java
rename to src/main/java/com/trilead/ssh2/packets/PacketChannelTrileadPing.java
index 18002aa..c337930 100644
--- a/src/com/trilead/ssh2/packets/PacketChannelTrileadPing.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketChannelTrileadPing.java
@@ -1,35 +1,35 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketChannelTrileadPing.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketChannelTrileadPing.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java b/src/main/java/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java
rename to src/main/java/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java
index bc0ac83..37ec081 100644
--- a/src/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java
@@ -1,57 +1,57 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketChannelWindowAdjust.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketChannelWindowAdjust.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketDisconnect.java b/src/main/java/com/trilead/ssh2/packets/PacketDisconnect.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketDisconnect.java
rename to src/main/java/com/trilead/ssh2/packets/PacketDisconnect.java
index 1811fb3..50d6ec2 100644
--- a/src/com/trilead/ssh2/packets/PacketDisconnect.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketDisconnect.java
@@ -1,57 +1,57 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketDisconnect.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketDisconnect.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java b/src/main/java/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java
rename to src/main/java/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java
index bc54b2e..20bd558 100644
--- a/src/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java
@@ -1,42 +1,42 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketGlobalCancelForwardRequest.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketGlobalCancelForwardRequest.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java b/src/main/java/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java
rename to src/main/java/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java
index a97c4e3..55257e9 100644
--- a/src/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java
@@ -1,41 +1,41 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketGlobalForwardRequest.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketGlobalForwardRequest.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java b/src/main/java/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java
similarity index 94%
rename from src/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java
rename to src/main/java/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java
index 5b9013d..3d8930e 100644
--- a/src/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java
@@ -1,32 +1,32 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketGlobalTrileadPing.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketGlobalTrileadPing.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketIgnore.java b/src/main/java/com/trilead/ssh2/packets/PacketIgnore.java
similarity index 94%
rename from src/com/trilead/ssh2/packets/PacketIgnore.java
rename to src/main/java/com/trilead/ssh2/packets/PacketIgnore.java
index 591ed49..2b4d917 100644
--- a/src/com/trilead/ssh2/packets/PacketIgnore.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketIgnore.java
@@ -1,59 +1,59 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketIgnore.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketIgnore.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDHInit.java b/src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java
similarity index 94%
rename from src/com/trilead/ssh2/packets/PacketKexDHInit.java
rename to src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java
index 26e14f6..cf39e33 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDHInit.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java
@@ -1,33 +1,33 @@
-package com.trilead.ssh2.packets;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDHInit.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: PacketKexDHInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDHInit
-{
-	byte[] payload;
-
-	BigInteger e;
-
-	public PacketKexDHInit(BigInteger e)
-	{
-		this.e = e;
-	}
-
-	public byte[] getPayload()
-	{
-		if (payload == null)
-		{
-			TypesWriter tw = new TypesWriter();
-			tw.writeByte(Packets.SSH_MSG_KEXDH_INIT);
-			tw.writeMPInt(e);
-			payload = tw.getBytes();
-		}
-		return payload;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.math.BigInteger;
+
+/**
+ * PacketKexDHInit.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: PacketKexDHInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketKexDHInit
+{
+	byte[] payload;
+
+	BigInteger e;
+
+	public PacketKexDHInit(BigInteger e)
+	{
+		this.e = e;
+	}
+
+	public byte[] getPayload()
+	{
+		if (payload == null)
+		{
+			TypesWriter tw = new TypesWriter();
+			tw.writeByte(Packets.SSH_MSG_KEXDH_INIT);
+			tw.writeMPInt(e);
+			payload = tw.getBytes();
+		}
+		return payload;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDHReply.java b/src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketKexDHReply.java
rename to src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java
index 0803ff9..27f9f91 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDHReply.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java
@@ -1,55 +1,55 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDHReply.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: PacketKexDHReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDHReply
-{
-	byte[] payload;
-
-	byte[] hostKey;
-	BigInteger f;
-	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();
-		f = tr.readMPINT();
-		signature = tr.readByteString();
-
-		if (tr.remain() != 0) throw new IOException("PADDING IN SSH_MSG_KEXDH_REPLY!");
-	}
-
-	public BigInteger getF()
-	{
-		return f;
-	}
-	
-	public byte[] getHostKey()
-	{
-		return hostKey;
-	}
-
-	public byte[] getSignature()
-	{
-		return signature;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+import java.math.BigInteger;
+
+/**
+ * PacketKexDHReply.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: PacketKexDHReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketKexDHReply
+{
+	byte[] payload;
+
+	byte[] hostKey;
+	BigInteger f;
+	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();
+		f = tr.readMPINT();
+		signature = tr.readByteString();
+
+		if (tr.remain() != 0) throw new IOException("PADDING IN SSH_MSG_KEXDH_REPLY!");
+	}
+
+	public BigInteger getF()
+	{
+		return f;
+	}
+	
+	public byte[] getHostKey()
+	{
+		return hostKey;
+	}
+
+	public byte[] getSignature()
+	{
+		return signature;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDhGexGroup.java b/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexGroup.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketKexDhGexGroup.java
rename to src/main/java/com/trilead/ssh2/packets/PacketKexDhGexGroup.java
index d531dad..db85b61 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexGroup.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexGroup.java
@@ -1,50 +1,50 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDhGexGroup.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+import java.math.BigInteger;
+
+/**
+ * PacketKexDhGexGroup.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDhGexInit.java b/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexInit.java
similarity index 94%
rename from src/com/trilead/ssh2/packets/PacketKexDhGexInit.java
rename to src/main/java/com/trilead/ssh2/packets/PacketKexDhGexInit.java
index f8f0916..8b34230 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexInit.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexInit.java
@@ -1,33 +1,33 @@
-package com.trilead.ssh2.packets;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDhGexInit.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.math.BigInteger;
+
+/**
+ * PacketKexDhGexInit.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDhGexReply.java b/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexReply.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketKexDhGexReply.java
rename to src/main/java/com/trilead/ssh2/packets/PacketKexDhGexReply.java
index ae225fb..382b3b7 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexReply.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexReply.java
@@ -1,56 +1,56 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDhGexReply.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+import java.math.BigInteger;
+
+/**
+ * PacketKexDhGexReply.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDhGexRequest.java b/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequest.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketKexDhGexRequest.java
rename to src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequest.java
index 75fc2af..50369df 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexRequest.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequest.java
@@ -1,39 +1,39 @@
-package com.trilead.ssh2.packets;
-
-import com.trilead.ssh2.DHGexParameters;
-
-/**
- * PacketKexDhGexRequest.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import com.trilead.ssh2.DHGexParameters;
+
+/**
+ * PacketKexDhGexRequest.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java b/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java
rename to src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java
index d89ec02..327f379 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java
@@ -1,34 +1,34 @@
-
-package com.trilead.ssh2.packets;
-
-import com.trilead.ssh2.DHGexParameters;
-
-/**
- * PacketKexDhGexRequestOld.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+import com.trilead.ssh2.DHGexParameters;
+
+/**
+ * PacketKexDhGexRequestOld.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexInit.java b/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketKexInit.java
rename to src/main/java/com/trilead/ssh2/packets/PacketKexInit.java
index 965ef06..fbd09ef 100644
--- a/src/com/trilead/ssh2/packets/PacketKexInit.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java
@@ -1,165 +1,165 @@
-
-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 at 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, 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 = new String[] { "none" };
-		kp.compression_algorithms_server_to_client = new String[] { "none" };
-		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;
-	}
-}
+
+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 at 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, 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 = new String[] { "none" };
+		kp.compression_algorithms_server_to_client = new String[] { "none" };
+		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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketNewKeys.java b/src/main/java/com/trilead/ssh2/packets/PacketNewKeys.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketNewKeys.java
rename to src/main/java/com/trilead/ssh2/packets/PacketNewKeys.java
index 34ed7e7..3ca6503 100644
--- a/src/com/trilead/ssh2/packets/PacketNewKeys.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketNewKeys.java
@@ -1,46 +1,46 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketNewKeys.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketNewKeys.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java b/src/main/java/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java
rename to src/main/java/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java
index 289e70e..da6cbef 100644
--- a/src/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java
@@ -1,56 +1,56 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketOpenDirectTCPIPChannel.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+
+/**
+ * PacketOpenDirectTCPIPChannel.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketOpenSessionChannel.java b/src/main/java/com/trilead/ssh2/packets/PacketOpenSessionChannel.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketOpenSessionChannel.java
rename to src/main/java/com/trilead/ssh2/packets/PacketOpenSessionChannel.java
index ea69ad1..a75ea63 100644
--- a/src/com/trilead/ssh2/packets/PacketOpenSessionChannel.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketOpenSessionChannel.java
@@ -1,62 +1,62 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketOpenSessionChannel.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketOpenSessionChannel.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketServiceAccept.java b/src/main/java/com/trilead/ssh2/packets/PacketServiceAccept.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketServiceAccept.java
rename to src/main/java/com/trilead/ssh2/packets/PacketServiceAccept.java
index 5081651..d5c9a90 100644
--- a/src/com/trilead/ssh2/packets/PacketServiceAccept.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketServiceAccept.java
@@ -1,61 +1,61 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketServiceAccept.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketServiceAccept.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketServiceRequest.java b/src/main/java/com/trilead/ssh2/packets/PacketServiceRequest.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketServiceRequest.java
rename to src/main/java/com/trilead/ssh2/packets/PacketServiceRequest.java
index df5b8b4..c2d2065 100644
--- a/src/com/trilead/ssh2/packets/PacketServiceRequest.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketServiceRequest.java
@@ -1,52 +1,52 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketServiceRequest.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketServiceRequest.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketSessionExecCommand.java b/src/main/java/com/trilead/ssh2/packets/PacketSessionExecCommand.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketSessionExecCommand.java
rename to src/main/java/com/trilead/ssh2/packets/PacketSessionExecCommand.java
index 5f459cf..84efa5d 100644
--- a/src/com/trilead/ssh2/packets/PacketSessionExecCommand.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketSessionExecCommand.java
@@ -1,39 +1,39 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketSessionExecCommand.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+
+/**
+ * PacketSessionExecCommand.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketSessionPtyRequest.java b/src/main/java/com/trilead/ssh2/packets/PacketSessionPtyRequest.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketSessionPtyRequest.java
rename to src/main/java/com/trilead/ssh2/packets/PacketSessionPtyRequest.java
index 93dd5ed..d9c3d59 100644
--- a/src/com/trilead/ssh2/packets/PacketSessionPtyRequest.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketSessionPtyRequest.java
@@ -1,57 +1,57 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketSessionPtyRequest.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+
+/**
+ * PacketSessionPtyRequest.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketSessionStartShell.java b/src/main/java/com/trilead/ssh2/packets/PacketSessionStartShell.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketSessionStartShell.java
rename to src/main/java/com/trilead/ssh2/packets/PacketSessionStartShell.java
index edfc85b..e5add01 100644
--- a/src/com/trilead/ssh2/packets/PacketSessionStartShell.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketSessionStartShell.java
@@ -1,36 +1,36 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketSessionStartShell.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketSessionStartShell.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java b/src/main/java/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java
rename to src/main/java/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java
index 3aa77ba..cdc3a8c 100644
--- a/src/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java
@@ -1,40 +1,40 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketSessionSubsystemRequest.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+
+/**
+ * PacketSessionSubsystemRequest.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketSessionX11Request.java b/src/main/java/com/trilead/ssh2/packets/PacketSessionX11Request.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketSessionX11Request.java
rename to src/main/java/com/trilead/ssh2/packets/PacketSessionX11Request.java
index 5cc1d14..26479c7 100644
--- a/src/com/trilead/ssh2/packets/PacketSessionX11Request.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketSessionX11Request.java
@@ -1,53 +1,53 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketSessionX11Request.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketSessionX11Request.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthBanner.java b/src/main/java/com/trilead/ssh2/packets/PacketUserauthBanner.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketUserauthBanner.java
rename to src/main/java/com/trilead/ssh2/packets/PacketUserauthBanner.java
index 2eafc5e..8ad8c3b 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthBanner.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketUserauthBanner.java
@@ -1,60 +1,60 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthBanner.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthBanner.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthFailure.java b/src/main/java/com/trilead/ssh2/packets/PacketUserauthFailure.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketUserauthFailure.java
rename to src/main/java/com/trilead/ssh2/packets/PacketUserauthFailure.java
index 77e9cf9..fd4a726 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthFailure.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketUserauthFailure.java
@@ -1,53 +1,53 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthBanner.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthBanner.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java b/src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java
similarity index 94%
rename from src/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java
rename to src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java
index 75a0730..e1606d1 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java
@@ -1,84 +1,84 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthInfoRequest.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthInfoRequest.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java b/src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java
rename to src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java
index b06f0b5..e8795d4 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java
@@ -1,35 +1,35 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketUserauthInfoResponse.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketUserauthInfoResponse.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java b/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java
rename to src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java
index e0efd29..83e9f49 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java
@@ -1,42 +1,42 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketUserauthRequestInteractive.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketUserauthRequestInteractive.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthRequestNone.java b/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestNone.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketUserauthRequestNone.java
rename to src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestNone.java
index 93f89d9..d786003 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestNone.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestNone.java
@@ -1,61 +1,61 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthRequestPassword.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthRequestPassword.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java b/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java
rename to src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java
index df7b36b..83047dd 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java
@@ -1,67 +1,67 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthRequestPassword.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthRequestPassword.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java b/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java
similarity index 96%
rename from src/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java
rename to src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java
index 1e38673..6462864 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java
+++ b/src/main/java/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java
@@ -1,65 +1,65 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthRequestPublicKey.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthRequestPublicKey.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/packets/Packets.java b/src/main/java/com/trilead/ssh2/packets/Packets.java
similarity index 97%
rename from src/com/trilead/ssh2/packets/Packets.java
rename to src/main/java/com/trilead/ssh2/packets/Packets.java
index bc9a6c0..6989286 100644
--- a/src/com/trilead/ssh2/packets/Packets.java
+++ b/src/main/java/com/trilead/ssh2/packets/Packets.java
@@ -1,149 +1,149 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * Packets.
- * 
- * @author Christian Plattner, plattner at 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();
-	//	}
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * Packets.
+ * 
+ * @author Christian Plattner, plattner at 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();
+	//	}
+}
diff --git a/src/com/trilead/ssh2/packets/TypesReader.java b/src/main/java/com/trilead/ssh2/packets/TypesReader.java
similarity index 95%
rename from src/com/trilead/ssh2/packets/TypesReader.java
rename to src/main/java/com/trilead/ssh2/packets/TypesReader.java
index c8d73e7..28f5363 100644
--- a/src/com/trilead/ssh2/packets/TypesReader.java
+++ b/src/main/java/com/trilead/ssh2/packets/TypesReader.java
@@ -1,177 +1,177 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-import java.math.BigInteger;
-
-import com.trilead.ssh2.util.Tokenizer;
-
-
-/**
- * TypesReader.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import com.trilead.ssh2.util.Tokenizer;
+
+
+/**
+ * TypesReader.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+
+}
diff --git a/src/com/trilead/ssh2/packets/TypesWriter.java b/src/main/java/com/trilead/ssh2/packets/TypesWriter.java
similarity index 94%
rename from src/com/trilead/ssh2/packets/TypesWriter.java
rename to src/main/java/com/trilead/ssh2/packets/TypesWriter.java
index f2e5ef3..9f7336b 100644
--- a/src/com/trilead/ssh2/packets/TypesWriter.java
+++ b/src/main/java/com/trilead/ssh2/packets/TypesWriter.java
@@ -1,169 +1,169 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-
-/**
- * TypesWriter.
- * 
- * @author Christian Plattner, plattner at 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());
-	}
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+
+/**
+ * TypesWriter.
+ * 
+ * @author Christian Plattner, plattner at 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());
+	}
+}
diff --git a/src/com/trilead/ssh2/sftp/AttrTextHints.java b/src/main/java/com/trilead/ssh2/sftp/AttrTextHints.java
similarity index 96%
rename from src/com/trilead/ssh2/sftp/AttrTextHints.java
rename to src/main/java/com/trilead/ssh2/sftp/AttrTextHints.java
index 7bac60f..19f0525 100644
--- a/src/com/trilead/ssh2/sftp/AttrTextHints.java
+++ b/src/main/java/com/trilead/ssh2/sftp/AttrTextHints.java
@@ -1,38 +1,38 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- * 
- * Values for the 'text-hint' field in the SFTP ATTRS data type.
- *
- * @author Christian Plattner, plattner at 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;
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ * 
+ * Values for the 'text-hint' field in the SFTP ATTRS data type.
+ *
+ * @author Christian Plattner, plattner at 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;
+}
diff --git a/src/com/trilead/ssh2/sftp/AttribBits.java b/src/main/java/com/trilead/ssh2/sftp/AttribBits.java
similarity index 97%
rename from src/com/trilead/ssh2/sftp/AttribBits.java
rename to src/main/java/com/trilead/ssh2/sftp/AttribBits.java
index f040673..b143613 100644
--- a/src/com/trilead/ssh2/sftp/AttribBits.java
+++ b/src/main/java/com/trilead/ssh2/sftp/AttribBits.java
@@ -1,129 +1,129 @@
-
-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 at 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;
-
-}
+
+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 at 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;
+
+}
diff --git a/src/com/trilead/ssh2/sftp/AttribFlags.java b/src/main/java/com/trilead/ssh2/sftp/AttribFlags.java
similarity index 96%
rename from src/com/trilead/ssh2/sftp/AttribFlags.java
rename to src/main/java/com/trilead/ssh2/sftp/AttribFlags.java
index 28ac613..ea27871 100644
--- a/src/com/trilead/ssh2/sftp/AttribFlags.java
+++ b/src/main/java/com/trilead/ssh2/sftp/AttribFlags.java
@@ -1,112 +1,112 @@
-
-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 at 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;
-}
+
+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 at 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;
+}
diff --git a/src/com/trilead/ssh2/sftp/AttribPermissions.java b/src/main/java/com/trilead/ssh2/sftp/AttribPermissions.java
similarity index 96%
rename from src/com/trilead/ssh2/sftp/AttribPermissions.java
rename to src/main/java/com/trilead/ssh2/sftp/AttribPermissions.java
index 0c946c9..558aa6f 100644
--- a/src/com/trilead/ssh2/sftp/AttribPermissions.java
+++ b/src/main/java/com/trilead/ssh2/sftp/AttribPermissions.java
@@ -1,32 +1,32 @@
-
-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 at 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;
-}
+
+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 at 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;
+}
diff --git a/src/com/trilead/ssh2/sftp/AttribTypes.java b/src/main/java/com/trilead/ssh2/sftp/AttribTypes.java
similarity index 97%
rename from src/com/trilead/ssh2/sftp/AttribTypes.java
rename to src/main/java/com/trilead/ssh2/sftp/AttribTypes.java
index 8cacc3e..e2f4169 100644
--- a/src/com/trilead/ssh2/sftp/AttribTypes.java
+++ b/src/main/java/com/trilead/ssh2/sftp/AttribTypes.java
@@ -1,28 +1,28 @@
-
-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 at 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;
-}
+
+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 at 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;
+}
diff --git a/src/com/trilead/ssh2/sftp/ErrorCodes.java b/src/main/java/com/trilead/ssh2/sftp/ErrorCodes.java
similarity index 98%
rename from src/com/trilead/ssh2/sftp/ErrorCodes.java
rename to src/main/java/com/trilead/ssh2/sftp/ErrorCodes.java
index 58a0174..7317a00 100644
--- a/src/com/trilead/ssh2/sftp/ErrorCodes.java
+++ b/src/main/java/com/trilead/ssh2/sftp/ErrorCodes.java
@@ -1,104 +1,104 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * SFTP Error Codes
- *
- * @author Christian Plattner, plattner at 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];
-	}
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ *
+ * SFTP Error Codes
+ *
+ * @author Christian Plattner, plattner at 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];
+	}
+}
diff --git a/src/com/trilead/ssh2/sftp/OpenFlags.java b/src/main/java/com/trilead/ssh2/sftp/OpenFlags.java
similarity index 97%
rename from src/com/trilead/ssh2/sftp/OpenFlags.java
rename to src/main/java/com/trilead/ssh2/sftp/OpenFlags.java
index bad30c3..b2979b9 100644
--- a/src/com/trilead/ssh2/sftp/OpenFlags.java
+++ b/src/main/java/com/trilead/ssh2/sftp/OpenFlags.java
@@ -1,223 +1,223 @@
-
-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 at 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;
-}
+
+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 at 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;
+}
diff --git a/src/com/trilead/ssh2/sftp/Packet.java b/src/main/java/com/trilead/ssh2/sftp/Packet.java
similarity index 97%
rename from src/com/trilead/ssh2/sftp/Packet.java
rename to src/main/java/com/trilead/ssh2/sftp/Packet.java
index 295055c..444af90 100644
--- a/src/com/trilead/ssh2/sftp/Packet.java
+++ b/src/main/java/com/trilead/ssh2/sftp/Packet.java
@@ -1,43 +1,43 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * SFTP Paket Types
- *
- * @author Christian Plattner, plattner at 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;
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ *
+ * SFTP Paket Types
+ *
+ * @author Christian Plattner, plattner at 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;
+}
diff --git a/src/com/trilead/ssh2/signature/DSAPrivateKey.java b/src/main/java/com/trilead/ssh2/signature/DSAPrivateKey.java
similarity index 93%
rename from src/com/trilead/ssh2/signature/DSAPrivateKey.java
rename to src/main/java/com/trilead/ssh2/signature/DSAPrivateKey.java
index d2a63ae..54173aa 100644
--- a/src/com/trilead/ssh2/signature/DSAPrivateKey.java
+++ b/src/main/java/com/trilead/ssh2/signature/DSAPrivateKey.java
@@ -1,58 +1,58 @@
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * DSAPrivateKey.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: DSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DSAPrivateKey
-{
-	private BigInteger p;
-	private BigInteger q;
-	private BigInteger g;
-	private BigInteger x;
-	private BigInteger y;
-
-	public DSAPrivateKey(BigInteger p, BigInteger q, BigInteger g,
-			BigInteger y, BigInteger x)
-	{
-		this.p = p;
-		this.q = q;
-		this.g = g;
-		this.y = y;
-		this.x = x;
-	}
-
-	public BigInteger getP()
-	{
-		return p;
-	}
-
-	public BigInteger getQ()
-	{
-		return q;
-	}
-	
-	public BigInteger getG()
-	{
-		return g;
-	}
-
-	public BigInteger getY()
-	{
-		return y;
-	}
-	
-	public BigInteger getX()
-	{
-		return x;
-	}
-	
-	public DSAPublicKey getPublicKey()
-	{
-		return new DSAPublicKey(p, q, g, y);
-	}
+package com.trilead.ssh2.signature;
+
+import java.math.BigInteger;
+
+/**
+ * DSAPrivateKey.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: DSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class DSAPrivateKey
+{
+	private BigInteger p;
+	private BigInteger q;
+	private BigInteger g;
+	private BigInteger x;
+	private BigInteger y;
+
+	public DSAPrivateKey(BigInteger p, BigInteger q, BigInteger g,
+			BigInteger y, BigInteger x)
+	{
+		this.p = p;
+		this.q = q;
+		this.g = g;
+		this.y = y;
+		this.x = x;
+	}
+
+	public BigInteger getP()
+	{
+		return p;
+	}
+
+	public BigInteger getQ()
+	{
+		return q;
+	}
+	
+	public BigInteger getG()
+	{
+		return g;
+	}
+
+	public BigInteger getY()
+	{
+		return y;
+	}
+	
+	public BigInteger getX()
+	{
+		return x;
+	}
+	
+	public DSAPublicKey getPublicKey()
+	{
+		return new DSAPublicKey(p, q, g, y);
+	}
 }
\ No newline at end of file
diff --git a/src/com/trilead/ssh2/signature/DSAPublicKey.java b/src/main/java/com/trilead/ssh2/signature/DSAPublicKey.java
similarity index 93%
rename from src/com/trilead/ssh2/signature/DSAPublicKey.java
rename to src/main/java/com/trilead/ssh2/signature/DSAPublicKey.java
index f8351ff..cf72228 100644
--- a/src/com/trilead/ssh2/signature/DSAPublicKey.java
+++ b/src/main/java/com/trilead/ssh2/signature/DSAPublicKey.java
@@ -1,45 +1,45 @@
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * DSAPublicKey.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: DSAPublicKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DSAPublicKey
-{
-	private BigInteger p;
-	private BigInteger q;
-	private BigInteger g;
-	private BigInteger y;
-
-	public DSAPublicKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y)
-	{
-		this.p = p;
-		this.q = q;
-		this.g = g;
-		this.y = y;
-	}
-
-	public BigInteger getP()
-	{
-		return p;
-	}
-
-	public BigInteger getQ()
-	{
-		return q;
-	}
-
-	public BigInteger getG()
-	{
-		return g;
-	}
-
-	public BigInteger getY()
-	{
-		return y;
-	}
+package com.trilead.ssh2.signature;
+
+import java.math.BigInteger;
+
+/**
+ * DSAPublicKey.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: DSAPublicKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class DSAPublicKey
+{
+	private BigInteger p;
+	private BigInteger q;
+	private BigInteger g;
+	private BigInteger y;
+
+	public DSAPublicKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y)
+	{
+		this.p = p;
+		this.q = q;
+		this.g = g;
+		this.y = y;
+	}
+
+	public BigInteger getP()
+	{
+		return p;
+	}
+
+	public BigInteger getQ()
+	{
+		return q;
+	}
+
+	public BigInteger getG()
+	{
+		return g;
+	}
+
+	public BigInteger getY()
+	{
+		return y;
+	}
 }
\ No newline at end of file
diff --git a/src/com/trilead/ssh2/signature/DSASHA1Verify.java b/src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java
similarity index 89%
rename from src/com/trilead/ssh2/signature/DSASHA1Verify.java
rename to src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java
index c838ebd..29a2e19 100644
--- a/src/com/trilead/ssh2/signature/DSASHA1Verify.java
+++ b/src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java
@@ -1,210 +1,211 @@
-
-package com.trilead.ssh2.signature;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.crypto.digest.SHA1;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-
-
-/**
- * DSASHA1Verify.
- * 
- * @author Christian Plattner, plattner at 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!");
-
-		return new DSAPublicKey(p, q, g, y);
-	}
-
-	public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk) throws IOException
-	{
-		TypesWriter tw = new TypesWriter();
-
-		tw.writeString("ssh-dss");
-		tw.writeMPInt(pk.getP());
-		tw.writeMPInt(pk.getQ());
-		tw.writeMPInt(pk.getG());
-		tw.writeMPInt(pk.getY());
-
-		return tw.getBytes();
-	}
-
-	public static byte[] encodeSSHDSASignature(DSASignature ds)
-	{
-		TypesWriter tw = new TypesWriter();
-
-		tw.writeString("ssh-dss");
-
-		byte[] r = ds.getR().toByteArray();
-		byte[] s = ds.getS().toByteArray();
-
-		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 DSASignature decodeSSHDSASignature(byte[] sig) throws IOException
-	{
-		byte[] rsArray = null;
-		
-		if (sig.length == 40)
-		{
-			/* OK, another broken SSH server. */
-			rsArray = sig;	
-		}
-		else
-		{
-			/* Hopefully a server obeing 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!");
-		}
-
-		/* Remember, s and r are unsigned ints. */
-
-		byte[] tmp = new byte[20];
-
-		System.arraycopy(rsArray, 0, tmp, 0, 20);
-		BigInteger r = new BigInteger(1, tmp);
-
-		System.arraycopy(rsArray, 20, tmp, 0, 20);
-		BigInteger s = new BigInteger(1, tmp);
-
-		if (log.isEnabled())
-		{
-			log.log(30, "decoded ssh-dss signature: first bytes r(" + ((rsArray[0]) & 0xff) + "), s("
-					+ ((rsArray[20]) & 0xff) + ")");
-		}
-
-		return new DSASignature(r, s);
-	}
-
-	public static boolean verifySignature(byte[] message, DSASignature ds, DSAPublicKey dpk) throws IOException
-	{
-		/* Inspired by Bouncycastle's DSASigner class */
-
-		SHA1 md = new SHA1();
-		md.update(message);
-		byte[] sha_message = new byte[md.getDigestLength()];
-		md.digest(sha_message);
-
-		BigInteger m = new BigInteger(1, sha_message);
-
-		BigInteger r = ds.getR();
-		BigInteger s = ds.getS();
-
-		BigInteger g = dpk.getG();
-		BigInteger p = dpk.getP();
-		BigInteger q = dpk.getQ();
-		BigInteger y = dpk.getY();
-
-		BigInteger zero = BigInteger.ZERO;
-
-		if (log.isEnabled())
-		{
-			log.log(60, "ssh-dss signature: m: " + m.toString(16));
-			log.log(60, "ssh-dss signature: r: " + r.toString(16));
-			log.log(60, "ssh-dss signature: s: " + s.toString(16));
-			log.log(60, "ssh-dss signature: g: " + g.toString(16));
-			log.log(60, "ssh-dss signature: p: " + p.toString(16));
-			log.log(60, "ssh-dss signature: q: " + q.toString(16));
-			log.log(60, "ssh-dss signature: y: " + y.toString(16));
-		}
-
-		if (zero.compareTo(r) >= 0 || q.compareTo(r) <= 0)
-		{
-			log.log(20, "ssh-dss signature: zero.compareTo(r) >= 0 || q.compareTo(r) <= 0");
-			return false;
-		}
-
-		if (zero.compareTo(s) >= 0 || q.compareTo(s) <= 0)
-		{
-			log.log(20, "ssh-dss signature: zero.compareTo(s) >= 0 || q.compareTo(s) <= 0");
-			return false;
-		}
-
-		BigInteger w = s.modInverse(q);
-
-		BigInteger u1 = m.multiply(w).mod(q);
-		BigInteger u2 = r.multiply(w).mod(q);
-
-		u1 = g.modPow(u1, p);
-		u2 = y.modPow(u2, p);
-
-		BigInteger v = u1.multiply(u2).mod(p).mod(q);
-
-		return v.equals(r);
-	}
-
-	public static DSASignature generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd)
-	{
-		SHA1 md = new SHA1();
-		md.update(message);
-		byte[] sha_message = new byte[md.getDigestLength()];
-		md.digest(sha_message);
-
-		BigInteger m = new BigInteger(1, sha_message);
-		BigInteger k;
-		int qBitLength = pk.getQ().bitLength();
-
-		do
-		{
-			k = new BigInteger(qBitLength, rnd);
-		}
-		while (k.compareTo(pk.getQ()) >= 0);
-
-		BigInteger r = pk.getG().modPow(k, pk.getP()).mod(pk.getQ());
-
-		k = k.modInverse(pk.getQ()).multiply(m.add((pk).getX().multiply(r)));
-
-		BigInteger s = k.mod(pk.getQ());
-
-		return new DSASignature(r, s);
-	}
-}
+
+package com.trilead.ssh2.signature;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import com.trilead.ssh2.IOWarningException;
+import com.trilead.ssh2.crypto.digest.SHA1;
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.packets.TypesWriter;
+
+
+/**
+ * DSASHA1Verify.
+ * 
+ * @author Christian Plattner, plattner at 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 {
+		final TypesReader tr = new TypesReader(key);
+
+		final String key_format = tr.readString();
+		if (!key_format.equals("ssh-dss")) {
+			throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting ssh-dss");
+		}
+
+		final BigInteger p = tr.readMPINT();
+		final BigInteger q = tr.readMPINT();
+		final BigInteger g = tr.readMPINT();
+		final BigInteger y = tr.readMPINT();
+
+		if (tr.remain() != 0) {
+			throw new IOException("Padding in DSA public key!");
+		}
+
+		return new DSAPublicKey(p, q, g, y);
+	}
+
+	public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk) throws IOException
+	{
+		TypesWriter tw = new TypesWriter();
+
+		tw.writeString("ssh-dss");
+		tw.writeMPInt(pk.getP());
+		tw.writeMPInt(pk.getQ());
+		tw.writeMPInt(pk.getG());
+		tw.writeMPInt(pk.getY());
+
+		return tw.getBytes();
+	}
+
+	public static byte[] encodeSSHDSASignature(DSASignature ds)
+	{
+		TypesWriter tw = new TypesWriter();
+
+		tw.writeString("ssh-dss");
+
+		byte[] r = ds.getR().toByteArray();
+		byte[] s = ds.getS().toByteArray();
+
+		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 DSASignature decodeSSHDSASignature(byte[] sig) throws IOException
+	{
+		byte[] rsArray = null;
+		
+		if (sig.length == 40)
+		{
+			/* OK, another broken SSH server. */
+			rsArray = sig;	
+		}
+		else
+		{
+			/* Hopefully a server obeing 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!");
+		}
+
+		/* Remember, s and r are unsigned ints. */
+
+		byte[] tmp = new byte[20];
+
+		System.arraycopy(rsArray, 0, tmp, 0, 20);
+		BigInteger r = new BigInteger(1, tmp);
+
+		System.arraycopy(rsArray, 20, tmp, 0, 20);
+		BigInteger s = new BigInteger(1, tmp);
+
+		if (log.isEnabled())
+		{
+			log.log(30, "decoded ssh-dss signature: first bytes r(" + ((rsArray[0]) & 0xff) + "), s("
+					+ ((rsArray[20]) & 0xff) + ")");
+		}
+
+		return new DSASignature(r, s);
+	}
+
+	public static boolean verifySignature(byte[] message, DSASignature ds, DSAPublicKey dpk) throws IOException
+	{
+		/* Inspired by Bouncycastle's DSASigner class */
+
+		SHA1 md = new SHA1();
+		md.update(message);
+		byte[] sha_message = new byte[md.getDigestLength()];
+		md.digest(sha_message);
+
+		BigInteger m = new BigInteger(1, sha_message);
+
+		BigInteger r = ds.getR();
+		BigInteger s = ds.getS();
+
+		BigInteger g = dpk.getG();
+		BigInteger p = dpk.getP();
+		BigInteger q = dpk.getQ();
+		BigInteger y = dpk.getY();
+
+		BigInteger zero = BigInteger.ZERO;
+
+		if (log.isEnabled())
+		{
+			log.log(60, "ssh-dss signature: m: " + m.toString(16));
+			log.log(60, "ssh-dss signature: r: " + r.toString(16));
+			log.log(60, "ssh-dss signature: s: " + s.toString(16));
+			log.log(60, "ssh-dss signature: g: " + g.toString(16));
+			log.log(60, "ssh-dss signature: p: " + p.toString(16));
+			log.log(60, "ssh-dss signature: q: " + q.toString(16));
+			log.log(60, "ssh-dss signature: y: " + y.toString(16));
+		}
+
+		if (zero.compareTo(r) >= 0 || q.compareTo(r) <= 0)
+		{
+			log.log(20, "ssh-dss signature: zero.compareTo(r) >= 0 || q.compareTo(r) <= 0");
+			return false;
+		}
+
+		if (zero.compareTo(s) >= 0 || q.compareTo(s) <= 0)
+		{
+			log.log(20, "ssh-dss signature: zero.compareTo(s) >= 0 || q.compareTo(s) <= 0");
+			return false;
+		}
+
+		BigInteger w = s.modInverse(q);
+
+		BigInteger u1 = m.multiply(w).mod(q);
+		BigInteger u2 = r.multiply(w).mod(q);
+
+		u1 = g.modPow(u1, p);
+		u2 = y.modPow(u2, p);
+
+		BigInteger v = u1.multiply(u2).mod(p).mod(q);
+
+		return v.equals(r);
+	}
+
+	public static DSASignature generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd)
+	{
+		SHA1 md = new SHA1();
+		md.update(message);
+		byte[] sha_message = new byte[md.getDigestLength()];
+		md.digest(sha_message);
+
+		BigInteger m = new BigInteger(1, sha_message);
+		BigInteger k;
+		int qBitLength = pk.getQ().bitLength();
+
+		do
+		{
+			k = new BigInteger(qBitLength, rnd);
+		}
+		while (k.compareTo(pk.getQ()) >= 0);
+
+		BigInteger r = pk.getG().modPow(k, pk.getP()).mod(pk.getQ());
+
+		k = k.modInverse(pk.getQ()).multiply(m.add((pk).getX().multiply(r)));
+
+		BigInteger s = k.mod(pk.getQ());
+
+		return new DSASignature(r, s);
+	}
+}
diff --git a/src/com/trilead/ssh2/signature/DSASignature.java b/src/main/java/com/trilead/ssh2/signature/DSASignature.java
similarity index 93%
rename from src/com/trilead/ssh2/signature/DSASignature.java
rename to src/main/java/com/trilead/ssh2/signature/DSASignature.java
index eff84cd..313f4a5 100644
--- a/src/com/trilead/ssh2/signature/DSASignature.java
+++ b/src/main/java/com/trilead/ssh2/signature/DSASignature.java
@@ -1,31 +1,31 @@
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * DSASignature.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: DSASignature.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DSASignature
-{
-	private BigInteger r;
-	private BigInteger s;
-
-	public DSASignature(BigInteger r, BigInteger s)
-	{
-		this.r = r;
-		this.s = s;
-	}
-
-	public BigInteger getR()
-	{
-		return r;
-	}
-
-	public BigInteger getS()
-	{
-		return s;
-	}
-}
+package com.trilead.ssh2.signature;
+
+import java.math.BigInteger;
+
+/**
+ * DSASignature.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: DSASignature.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class DSASignature
+{
+	private BigInteger r;
+	private BigInteger s;
+
+	public DSASignature(BigInteger r, BigInteger s)
+	{
+		this.r = r;
+		this.s = s;
+	}
+
+	public BigInteger getR()
+	{
+		return r;
+	}
+
+	public BigInteger getS()
+	{
+		return s;
+	}
+}
diff --git a/src/com/trilead/ssh2/signature/RSAPrivateKey.java b/src/main/java/com/trilead/ssh2/signature/RSAPrivateKey.java
similarity index 93%
rename from src/com/trilead/ssh2/signature/RSAPrivateKey.java
rename to src/main/java/com/trilead/ssh2/signature/RSAPrivateKey.java
index 5d5e606..8f7e6f6 100644
--- a/src/com/trilead/ssh2/signature/RSAPrivateKey.java
+++ b/src/main/java/com/trilead/ssh2/signature/RSAPrivateKey.java
@@ -1,43 +1,43 @@
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * RSAPrivateKey.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class RSAPrivateKey
-{
-	private BigInteger d;
-	private BigInteger e;
-	private BigInteger n;
-
-	public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
-	{
-		this.d = d;
-		this.e = e;
-		this.n = n;
-	}
-
-	public BigInteger getD()
-	{
-		return d;
-	}
-	
-	public BigInteger getE()
-	{
-		return e;
-	}
-
-	public BigInteger getN()
-	{
-		return n;
-	}
-	
-	public RSAPublicKey getPublicKey()
-	{
-		return new RSAPublicKey(e, n);
-	}
+package com.trilead.ssh2.signature;
+
+import java.math.BigInteger;
+
+/**
+ * RSAPrivateKey.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class RSAPrivateKey
+{
+	private BigInteger d;
+	private BigInteger e;
+	private BigInteger n;
+
+	public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
+	{
+		this.d = d;
+		this.e = e;
+		this.n = n;
+	}
+
+	public BigInteger getD()
+	{
+		return d;
+	}
+	
+	public BigInteger getE()
+	{
+		return e;
+	}
+
+	public BigInteger getN()
+	{
+		return n;
+	}
+	
+	public RSAPublicKey getPublicKey()
+	{
+		return new RSAPublicKey(e, n);
+	}
 }
\ No newline at end of file
diff --git a/src/com/trilead/ssh2/signature/RSAPublicKey.java b/src/main/java/com/trilead/ssh2/signature/RSAPublicKey.java
similarity index 93%
rename from src/com/trilead/ssh2/signature/RSAPublicKey.java
rename to src/main/java/com/trilead/ssh2/signature/RSAPublicKey.java
index e7e6611..d7ed54f 100644
--- a/src/com/trilead/ssh2/signature/RSAPublicKey.java
+++ b/src/main/java/com/trilead/ssh2/signature/RSAPublicKey.java
@@ -1,31 +1,31 @@
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * RSAPublicKey.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: RSAPublicKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class RSAPublicKey
-{
-	BigInteger e;
-	BigInteger n;
-
-	public RSAPublicKey(BigInteger e, BigInteger n)
-	{
-		this.e = e;
-		this.n = n;
-	}
-
-	public BigInteger getE()
-	{
-		return e;
-	}
-
-	public BigInteger getN()
-	{
-		return n;
-	}
+package com.trilead.ssh2.signature;
+
+import java.math.BigInteger;
+
+/**
+ * RSAPublicKey.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: RSAPublicKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class RSAPublicKey
+{
+	BigInteger e;
+	BigInteger n;
+
+	public RSAPublicKey(BigInteger e, BigInteger n)
+	{
+		this.e = e;
+		this.n = n;
+	}
+
+	public BigInteger getE()
+	{
+		return e;
+	}
+
+	public BigInteger getN()
+	{
+		return n;
+	}
 }
\ No newline at end of file
diff --git a/src/com/trilead/ssh2/signature/RSASHA1Verify.java b/src/main/java/com/trilead/ssh2/signature/RSASHA1Verify.java
similarity index 91%
rename from src/com/trilead/ssh2/signature/RSASHA1Verify.java
rename to src/main/java/com/trilead/ssh2/signature/RSASHA1Verify.java
index 8a0f07a..d106909 100644
--- a/src/com/trilead/ssh2/signature/RSASHA1Verify.java
+++ b/src/main/java/com/trilead/ssh2/signature/RSASHA1Verify.java
@@ -1,285 +1,286 @@
-
-package com.trilead.ssh2.signature;
-
-import java.io.IOException;
-import java.math.BigInteger;
-
-import com.trilead.ssh2.crypto.SimpleDERReader;
-import com.trilead.ssh2.crypto.digest.SHA1;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-
-
-/**
- * RSASHA1Verify.
- * 
- * @author Christian Plattner, plattner at 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!");
-
-		return new RSAPublicKey(e, n);
-	}
-
-	public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException
-	{
-		TypesWriter tw = new TypesWriter();
-
-		tw.writeString("ssh-rsa");
-		tw.writeMPInt(pk.getE());
-		tw.writeMPInt(pk.getN());
-
-		return tw.getBytes();
-	}
-
-	public static RSASignature 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!");
-
-		return new RSASignature(new BigInteger(1, s));
-	}
-
-	public static byte[] encodeSSHRSASignature(RSASignature sig) 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)."
-		 */
-
-		byte[] s = sig.getS().toByteArray();
-
-		/* 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 RSASignature generateSignature(byte[] message, RSAPrivateKey pk) throws IOException
-	{
-		SHA1 md = new SHA1();
-		md.update(message);
-		byte[] sha_message = new byte[md.getDigestLength()];
-		md.digest(sha_message);
-
-		byte[] der_header = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00,
-				0x04, 0x14 };
-
-		int rsa_block_len = (pk.getN().bitLength() + 7) / 8;
-
-		int num_pad = rsa_block_len - (2 + der_header.length + sha_message.length) - 1;
-
-		if (num_pad < 8)
-			throw new IOException("Cannot sign with RSA, message too long");
-
-		byte[] sig = new byte[der_header.length + sha_message.length + 2 + num_pad];
-
-		sig[0] = 0x01;
-
-		for (int i = 0; i < num_pad; i++)
-		{
-			sig[i + 1] = (byte) 0xff;
-		}
-
-		sig[num_pad + 1] = 0x00;
-
-		System.arraycopy(der_header, 0, sig, 2 + num_pad, der_header.length);
-		System.arraycopy(sha_message, 0, sig, 2 + num_pad + der_header.length, sha_message.length);
-
-		BigInteger m = new BigInteger(1, sig);
-
-		BigInteger s = m.modPow(pk.getD(), pk.getN());
-
-		return new RSASignature(s);
-	}
-
-	public static boolean verifySignature(byte[] message, RSASignature ds, RSAPublicKey dpk) throws IOException
-	{
-		SHA1 md = new SHA1();
-		md.update(message);
-		byte[] sha_message = new byte[md.getDigestLength()];
-		md.digest(sha_message);
-
-		BigInteger n = dpk.getN();
-		BigInteger e = dpk.getE();
-		BigInteger s = ds.getS();
-
-		if (n.compareTo(s) <= 0)
-		{
-			log.log(20, "ssh-rsa signature: n.compareTo(s) <= 0");
-			return false;
-		}
-
-		int rsa_block_len = (n.bitLength() + 7) / 8;
-
-		/* And now the show begins */
-
-		if (rsa_block_len < 1)
-		{
-			log.log(20, "ssh-rsa signature: rsa_block_len < 1");
-			return false;
-		}
-
-		byte[] v = s.modPow(e, n).toByteArray();
-
-		int startpos = 0;
-
-		if ((v.length > 0) && (v[0] == 0x00))
-			startpos++;
-
-		if ((v.length - startpos) != (rsa_block_len - 1))
-		{
-			log.log(20, "ssh-rsa signature: (v.length - startpos) != (rsa_block_len - 1)");
-			return false;
-		}
-
-		if (v[startpos] != 0x01)
-		{
-			log.log(20, "ssh-rsa signature: v[startpos] != 0x01");
-			return false;
-		}
-
-		int pos = startpos + 1;
-
-		while (true)
-		{
-			if (pos >= v.length)
-			{
-				log.log(20, "ssh-rsa signature: pos >= v.length");
-				return false;
-			}
-			if (v[pos] == 0x00)
-				break;
-			if (v[pos] != (byte) 0xff)
-			{
-				log.log(20, "ssh-rsa signature: v[pos] != (byte) 0xff");
-				return false;
-			}
-			pos++;
-		}
-
-		int num_pad = pos - (startpos + 1);
-
-		if (num_pad < 8)
-		{
-			log.log(20, "ssh-rsa signature: num_pad < 8");
-			return false;
-		}
-
-		pos++;
-
-		if (pos >= v.length)
-		{
-			log.log(20, "ssh-rsa signature: pos >= v.length");
-			return false;
-		}
-
-		SimpleDERReader dr = new SimpleDERReader(v, pos, v.length - pos);
-
-		byte[] seq = dr.readSequenceAsByteArray();
-
-		if (dr.available() != 0)
-		{
-			log.log(20, "ssh-rsa signature: dr.available() != 0");
-			return false;
-		}
-
-		dr.resetInput(seq);
-
-		/* Read digestAlgorithm */
-
-		byte digestAlgorithm[] = dr.readSequenceAsByteArray();
-
-		/* Inspired by RFC 3347, however, ignoring the comment regarding old BER based implementations */
-
-		if ((digestAlgorithm.length < 8) || (digestAlgorithm.length > 9))
-		{
-			log.log(20, "ssh-rsa signature: (digestAlgorithm.length < 8) || (digestAlgorithm.length > 9)");
-			return false;
-		}
-
-		byte[] digestAlgorithm_sha1 = new byte[] { 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00 };
-
-		for (int i = 0; i < digestAlgorithm.length; i++)
-		{
-			if (digestAlgorithm[i] != digestAlgorithm_sha1[i])
-			{
-				log.log(20, "ssh-rsa signature: digestAlgorithm[i] != digestAlgorithm_sha1[i]");
-				return false;
-			}
-		}
-
-		byte[] digest = dr.readOctetString();
-
-		if (dr.available() != 0)
-		{
-			log.log(20, "ssh-rsa signature: dr.available() != 0 (II)");
-			return false;
-		}
-			
-		if (digest.length != sha_message.length)
-		{
-			log.log(20, "ssh-rsa signature: digest.length != sha_message.length");
-			return false;
-		}
-
-		for (int i = 0; i < sha_message.length; i++)
-		{
-			if (sha_message[i] != digest[i])
-			{
-				log.log(20, "ssh-rsa signature: sha_message[i] != digest[i]");
-				return false;
-			}
-		}
-
-		return true;
-	}
-}
+
+package com.trilead.ssh2.signature;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import com.trilead.ssh2.IOWarningException;
+import com.trilead.ssh2.crypto.SimpleDERReader;
+import com.trilead.ssh2.crypto.digest.SHA1;
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.packets.TypesWriter;
+
+
+/**
+ * RSASHA1Verify.
+ * 
+ * @author Christian Plattner, plattner at 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 {
+		final TypesReader tr = new TypesReader(key);
+
+		final String key_format = tr.readString();
+		if (!key_format.equals("ssh-rsa")) {
+			throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting ssh-rsa");
+		}
+
+		final BigInteger e = tr.readMPINT();
+		final BigInteger n = tr.readMPINT();
+
+		if (tr.remain() != 0) {
+			throw new IOException("Padding in RSA public key!");
+		}
+
+		return new RSAPublicKey(e, n);
+	}
+
+	public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException
+	{
+		TypesWriter tw = new TypesWriter();
+
+		tw.writeString("ssh-rsa");
+		tw.writeMPInt(pk.getE());
+		tw.writeMPInt(pk.getN());
+
+		return tw.getBytes();
+	}
+
+	public static RSASignature 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!");
+
+		return new RSASignature(new BigInteger(1, s));
+	}
+
+	public static byte[] encodeSSHRSASignature(RSASignature sig) 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)."
+		 */
+
+		byte[] s = sig.getS().toByteArray();
+
+		/* 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 RSASignature generateSignature(byte[] message, RSAPrivateKey pk) throws IOException
+	{
+		SHA1 md = new SHA1();
+		md.update(message);
+		byte[] sha_message = new byte[md.getDigestLength()];
+		md.digest(sha_message);
+
+		byte[] der_header = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00,
+				0x04, 0x14 };
+
+		int rsa_block_len = (pk.getN().bitLength() + 7) / 8;
+
+		int num_pad = rsa_block_len - (2 + der_header.length + sha_message.length) - 1;
+
+		if (num_pad < 8)
+			throw new IOException("Cannot sign with RSA, message too long");
+
+		byte[] sig = new byte[der_header.length + sha_message.length + 2 + num_pad];
+
+		sig[0] = 0x01;
+
+		for (int i = 0; i < num_pad; i++)
+		{
+			sig[i + 1] = (byte) 0xff;
+		}
+
+		sig[num_pad + 1] = 0x00;
+
+		System.arraycopy(der_header, 0, sig, 2 + num_pad, der_header.length);
+		System.arraycopy(sha_message, 0, sig, 2 + num_pad + der_header.length, sha_message.length);
+
+		BigInteger m = new BigInteger(1, sig);
+
+		BigInteger s = m.modPow(pk.getD(), pk.getN());
+
+		return new RSASignature(s);
+	}
+
+	public static boolean verifySignature(byte[] message, RSASignature ds, RSAPublicKey dpk) throws IOException
+	{
+		SHA1 md = new SHA1();
+		md.update(message);
+		byte[] sha_message = new byte[md.getDigestLength()];
+		md.digest(sha_message);
+
+		BigInteger n = dpk.getN();
+		BigInteger e = dpk.getE();
+		BigInteger s = ds.getS();
+
+		if (n.compareTo(s) <= 0)
+		{
+			log.log(20, "ssh-rsa signature: n.compareTo(s) <= 0");
+			return false;
+		}
+
+		int rsa_block_len = (n.bitLength() + 7) / 8;
+
+		/* And now the show begins */
+
+		if (rsa_block_len < 1)
+		{
+			log.log(20, "ssh-rsa signature: rsa_block_len < 1");
+			return false;
+		}
+
+		byte[] v = s.modPow(e, n).toByteArray();
+
+		int startpos = 0;
+
+		if ((v.length > 0) && (v[0] == 0x00))
+			startpos++;
+
+		if ((v.length - startpos) != (rsa_block_len - 1))
+		{
+			log.log(20, "ssh-rsa signature: (v.length - startpos) != (rsa_block_len - 1)");
+			return false;
+		}
+
+		if (v[startpos] != 0x01)
+		{
+			log.log(20, "ssh-rsa signature: v[startpos] != 0x01");
+			return false;
+		}
+
+		int pos = startpos + 1;
+
+		while (true)
+		{
+			if (pos >= v.length)
+			{
+				log.log(20, "ssh-rsa signature: pos >= v.length");
+				return false;
+			}
+			if (v[pos] == 0x00)
+				break;
+			if (v[pos] != (byte) 0xff)
+			{
+				log.log(20, "ssh-rsa signature: v[pos] != (byte) 0xff");
+				return false;
+			}
+			pos++;
+		}
+
+		int num_pad = pos - (startpos + 1);
+
+		if (num_pad < 8)
+		{
+			log.log(20, "ssh-rsa signature: num_pad < 8");
+			return false;
+		}
+
+		pos++;
+
+		if (pos >= v.length)
+		{
+			log.log(20, "ssh-rsa signature: pos >= v.length");
+			return false;
+		}
+
+		SimpleDERReader dr = new SimpleDERReader(v, pos, v.length - pos);
+
+		byte[] seq = dr.readSequenceAsByteArray();
+
+		if (dr.available() != 0)
+		{
+			log.log(20, "ssh-rsa signature: dr.available() != 0");
+			return false;
+		}
+
+		dr.resetInput(seq);
+
+		/* Read digestAlgorithm */
+
+		byte digestAlgorithm[] = dr.readSequenceAsByteArray();
+
+		/* Inspired by RFC 3347, however, ignoring the comment regarding old BER based implementations */
+
+		if ((digestAlgorithm.length < 8) || (digestAlgorithm.length > 9))
+		{
+			log.log(20, "ssh-rsa signature: (digestAlgorithm.length < 8) || (digestAlgorithm.length > 9)");
+			return false;
+		}
+
+		byte[] digestAlgorithm_sha1 = new byte[] { 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00 };
+
+		for (int i = 0; i < digestAlgorithm.length; i++)
+		{
+			if (digestAlgorithm[i] != digestAlgorithm_sha1[i])
+			{
+				log.log(20, "ssh-rsa signature: digestAlgorithm[i] != digestAlgorithm_sha1[i]");
+				return false;
+			}
+		}
+
+		byte[] digest = dr.readOctetString();
+
+		if (dr.available() != 0)
+		{
+			log.log(20, "ssh-rsa signature: dr.available() != 0 (II)");
+			return false;
+		}
+			
+		if (digest.length != sha_message.length)
+		{
+			log.log(20, "ssh-rsa signature: digest.length != sha_message.length");
+			return false;
+		}
+
+		for (int i = 0; i < sha_message.length; i++)
+		{
+			if (sha_message[i] != digest[i])
+			{
+				log.log(20, "ssh-rsa signature: sha_message[i] != digest[i]");
+				return false;
+			}
+		}
+
+		return true;
+	}
+}
diff --git a/src/com/trilead/ssh2/signature/RSASignature.java b/src/main/java/com/trilead/ssh2/signature/RSASignature.java
similarity index 93%
rename from src/com/trilead/ssh2/signature/RSASignature.java
rename to src/main/java/com/trilead/ssh2/signature/RSASignature.java
index e04e7ee..638f13f 100644
--- a/src/com/trilead/ssh2/signature/RSASignature.java
+++ b/src/main/java/com/trilead/ssh2/signature/RSASignature.java
@@ -1,27 +1,27 @@
-
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-
-/**
- * RSASignature.
- * 
- * @author Christian Plattner, plattner at trilead.com
- * @version $Id: RSASignature.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-
-public class RSASignature
-{
-	BigInteger s;
-
-	public BigInteger getS()
-	{
-		return s;
-	}
-
-	public RSASignature(BigInteger s)
-	{
-		this.s = s;
-	}
+
+package com.trilead.ssh2.signature;
+
+import java.math.BigInteger;
+
+
+/**
+ * RSASignature.
+ * 
+ * @author Christian Plattner, plattner at trilead.com
+ * @version $Id: RSASignature.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+
+public class RSASignature
+{
+	BigInteger s;
+
+	public BigInteger getS()
+	{
+		return s;
+	}
+
+	public RSASignature(BigInteger s)
+	{
+		this.s = s;
+	}
 }
\ No newline at end of file
diff --git a/src/com/trilead/ssh2/transport/ClientServerHello.java b/src/main/java/com/trilead/ssh2/transport/ClientServerHello.java
similarity index 95%
rename from src/com/trilead/ssh2/transport/ClientServerHello.java
rename to src/main/java/com/trilead/ssh2/transport/ClientServerHello.java
index 23726f3..d7a5ee5 100644
--- a/src/com/trilead/ssh2/transport/ClientServerHello.java
+++ b/src/main/java/com/trilead/ssh2/transport/ClientServerHello.java
@@ -1,125 +1,125 @@
-
-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 at 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;
-	}
-}
+
+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 at 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;
+	}
+}
diff --git a/src/com/trilead/ssh2/transport/KexManager.java b/src/main/java/com/trilead/ssh2/transport/KexManager.java
similarity index 96%
rename from src/com/trilead/ssh2/transport/KexManager.java
rename to src/main/java/com/trilead/ssh2/transport/KexManager.java
index 686e6cd..899e4cf 100644
--- a/src/com/trilead/ssh2/transport/KexManager.java
+++ b/src/main/java/com/trilead/ssh2/transport/KexManager.java
@@ -1,629 +1,629 @@
-
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.ConnectionInfo;
-import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.ServerHostKeyVerifier;
-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.DhExchange;
-import com.trilead.ssh2.crypto.dh.DhGroupExchange;
-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.DSAPublicKey;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.DSASignature;
-import com.trilead.ssh2.signature.RSAPublicKey;
-import com.trilead.ssh2.signature.RSASHA1Verify;
-import com.trilead.ssh2.signature.RSASignature;
-
-
-/**
- * KexManager.
- * 
- * @author Christian Plattner, plattner at 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);
-
-	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, rnd);
-			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("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;
-	}
-
-	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;
-
-		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);
-
-		}
-		catch (IllegalArgumentException e1)
-		{
-			throw new IOException("Fatal error during MAC startup!");
-		}
-
-		tm.changeSendCipher(cbc, mac);
-		tm.kexFinished();
-	}
-
-	public static final String[] getDefaultServerHostkeyAlgorithmList()
-	{
-		return new String[] { "ssh-rsa", "ssh-dss" };
-	}
-
-	public static final void checkServerHostkeyAlgorithmsList(String[] algos)
-	{
-		for (int i = 0; i < algos.length; i++)
-		{
-			if (("ssh-rsa".equals(algos[i]) == false) && ("ssh-dss".equals(algos[i]) == false))
-				throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
-		}
-	}
-
-	public static final String[] getDefaultKexAlgorithmList()
-	{
-		return new String[] { "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
-				"diffie-hellman-group1-sha1" };
-	}
-
-	public static final void checkKexAlgorithmList(String[] algos)
-	{
-		for (int i = 0; i < algos.length; i++)
-		{
-			if ("diffie-hellman-group-exchange-sha1".equals(algos[i]))
-				continue;
-
-			if ("diffie-hellman-group14-sha1".equals(algos[i]))
-				continue;
-
-			if ("diffie-hellman-group1-sha1".equals(algos[i]))
-				continue;
-
-			throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
-		}
-	}
-
-	private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
-	{
-		if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
-		{
-			RSASignature 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"))
-		{
-			DSASignature 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, rnd);
-				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"))
-			{
-				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());
-				}
-				kxs.state = 1;
-				return;
-			}
-
-			if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
-					|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
-			{
-				kxs.dhx = new DhExchange();
-
-				if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
-					kxs.dhx.init(1, rnd);
-				else
-					kxs.dhx.init(14, rnd);
-
-				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;
-
-			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);
-
-			}
-			catch (IllegalArgumentException e1)
-			{
-				throw new IOException("Fatal error during MAC startup!");
-			}
-
-			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.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"))
-		{
-			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(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"))
-		{
-			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 + ")");
-	}
-}
+
+package com.trilead.ssh2.transport;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import com.trilead.ssh2.ConnectionInfo;
+import com.trilead.ssh2.DHGexParameters;
+import com.trilead.ssh2.ServerHostKeyVerifier;
+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.DhExchange;
+import com.trilead.ssh2.crypto.dh.DhGroupExchange;
+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.DSAPublicKey;
+import com.trilead.ssh2.signature.DSASHA1Verify;
+import com.trilead.ssh2.signature.DSASignature;
+import com.trilead.ssh2.signature.RSAPublicKey;
+import com.trilead.ssh2.signature.RSASHA1Verify;
+import com.trilead.ssh2.signature.RSASignature;
+
+
+/**
+ * KexManager.
+ * 
+ * @author Christian Plattner, plattner at 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);
+
+	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, rnd);
+			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("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;
+	}
+
+	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;
+
+		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);
+
+		}
+		catch (IllegalArgumentException e1)
+		{
+			throw new IOException("Fatal error during MAC startup!");
+		}
+
+		tm.changeSendCipher(cbc, mac);
+		tm.kexFinished();
+	}
+
+	public static final String[] getDefaultServerHostkeyAlgorithmList()
+	{
+		return new String[] { "ssh-rsa", "ssh-dss" };
+	}
+
+	public static final void checkServerHostkeyAlgorithmsList(String[] algos)
+	{
+		for (int i = 0; i < algos.length; i++)
+		{
+			if (("ssh-rsa".equals(algos[i]) == false) && ("ssh-dss".equals(algos[i]) == false))
+				throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
+		}
+	}
+
+	public static final String[] getDefaultKexAlgorithmList()
+	{
+		return new String[] { "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
+				"diffie-hellman-group1-sha1" };
+	}
+
+	public static final void checkKexAlgorithmList(String[] algos)
+	{
+		for (int i = 0; i < algos.length; i++)
+		{
+			if ("diffie-hellman-group-exchange-sha1".equals(algos[i]))
+				continue;
+
+			if ("diffie-hellman-group14-sha1".equals(algos[i]))
+				continue;
+
+			if ("diffie-hellman-group1-sha1".equals(algos[i]))
+				continue;
+
+			throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
+		}
+	}
+
+	private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
+	{
+		if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
+		{
+			RSASignature 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"))
+		{
+			DSASignature 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, rnd);
+				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"))
+			{
+				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());
+				}
+				kxs.state = 1;
+				return;
+			}
+
+			if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
+					|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
+			{
+				kxs.dhx = new DhExchange();
+
+				if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
+					kxs.dhx.init(1, rnd);
+				else
+					kxs.dhx.init(14, rnd);
+
+				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;
+
+			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);
+
+			}
+			catch (IllegalArgumentException e1)
+			{
+				throw new IOException("Fatal error during MAC startup!");
+			}
+
+			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.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"))
+		{
+			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(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"))
+		{
+			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 + ")");
+	}
+}
diff --git a/src/com/trilead/ssh2/transport/KexParameters.java b/src/main/java/com/trilead/ssh2/transport/KexParameters.java
similarity index 97%
rename from src/com/trilead/ssh2/transport/KexParameters.java
rename to src/main/java/com/trilead/ssh2/transport/KexParameters.java
index 6e7e08f..70bcf3e 100644
--- a/src/com/trilead/ssh2/transport/KexParameters.java
+++ b/src/main/java/com/trilead/ssh2/transport/KexParameters.java
@@ -1,24 +1,24 @@
-package com.trilead.ssh2.transport;
-
-/**
- * KexParameters.
- * 
- * @author Christian Plattner, plattner at 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;
-}
+package com.trilead.ssh2.transport;
+
+/**
+ * KexParameters.
+ * 
+ * @author Christian Plattner, plattner at 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;
+}
diff --git a/src/com/trilead/ssh2/transport/KexState.java b/src/main/java/com/trilead/ssh2/transport/KexState.java
similarity index 95%
rename from src/com/trilead/ssh2/transport/KexState.java
rename to src/main/java/com/trilead/ssh2/transport/KexState.java
index dabf450..82889c2 100644
--- a/src/com/trilead/ssh2/transport/KexState.java
+++ b/src/main/java/com/trilead/ssh2/transport/KexState.java
@@ -1,32 +1,32 @@
-package com.trilead.ssh2.transport;
-
-
-import java.math.BigInteger;
-
-import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.crypto.dh.DhExchange;
-import com.trilead.ssh2.crypto.dh.DhGroupExchange;
-import com.trilead.ssh2.packets.PacketKexInit;
-
-/**
- * KexState.
- * 
- * @author Christian Plattner, plattner at 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 DhExchange dhx;
-	public DhGroupExchange dhgx;
-	public DHGexParameters dhgexParameters;
-}
+package com.trilead.ssh2.transport;
+
+
+import java.math.BigInteger;
+
+import com.trilead.ssh2.DHGexParameters;
+import com.trilead.ssh2.crypto.dh.DhExchange;
+import com.trilead.ssh2.crypto.dh.DhGroupExchange;
+import com.trilead.ssh2.packets.PacketKexInit;
+
+/**
+ * KexState.
+ * 
+ * @author Christian Plattner, plattner at 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 DhExchange dhx;
+	public DhGroupExchange dhgx;
+	public DHGexParameters dhgexParameters;
+}
diff --git a/src/com/trilead/ssh2/transport/MessageHandler.java b/src/main/java/com/trilead/ssh2/transport/MessageHandler.java
similarity index 95%
rename from src/com/trilead/ssh2/transport/MessageHandler.java
rename to src/main/java/com/trilead/ssh2/transport/MessageHandler.java
index 1f173e8..039d473 100644
--- a/src/com/trilead/ssh2/transport/MessageHandler.java
+++ b/src/main/java/com/trilead/ssh2/transport/MessageHandler.java
@@ -1,14 +1,14 @@
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-
-/**
- * MessageHandler.
- * 
- * @author Christian Plattner, plattner at 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;
-}
+package com.trilead.ssh2.transport;
+
+import java.io.IOException;
+
+/**
+ * MessageHandler.
+ * 
+ * @author Christian Plattner, plattner at 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;
+}
diff --git a/src/com/trilead/ssh2/transport/NegotiateException.java b/src/main/java/com/trilead/ssh2/transport/NegotiateException.java
similarity index 96%
rename from src/com/trilead/ssh2/transport/NegotiateException.java
rename to src/main/java/com/trilead/ssh2/transport/NegotiateException.java
index 8562a65..ff53097 100644
--- a/src/com/trilead/ssh2/transport/NegotiateException.java
+++ b/src/main/java/com/trilead/ssh2/transport/NegotiateException.java
@@ -1,12 +1,12 @@
-package com.trilead.ssh2.transport;
-
-/**
- * NegotiateException.
- * 
- * @author Christian Plattner, plattner at 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;
-}
+package com.trilead.ssh2.transport;
+
+/**
+ * NegotiateException.
+ * 
+ * @author Christian Plattner, plattner at 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;
+}
diff --git a/src/com/trilead/ssh2/transport/NegotiatedParameters.java b/src/main/java/com/trilead/ssh2/transport/NegotiatedParameters.java
similarity index 96%
rename from src/com/trilead/ssh2/transport/NegotiatedParameters.java
rename to src/main/java/com/trilead/ssh2/transport/NegotiatedParameters.java
index 6f035c0..e9f3a0a 100644
--- a/src/com/trilead/ssh2/transport/NegotiatedParameters.java
+++ b/src/main/java/com/trilead/ssh2/transport/NegotiatedParameters.java
@@ -1,22 +1,22 @@
-package com.trilead.ssh2.transport;
-
-/**
- * NegotiatedParameters.
- * 
- * @author Christian Plattner, plattner at 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;
-}
+package com.trilead.ssh2.transport;
+
+/**
+ * NegotiatedParameters.
+ * 
+ * @author Christian Plattner, plattner at 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;
+}
diff --git a/src/com/trilead/ssh2/transport/TransportConnection.java b/src/main/java/com/trilead/ssh2/transport/TransportConnection.java
similarity index 96%
rename from src/com/trilead/ssh2/transport/TransportConnection.java
rename to src/main/java/com/trilead/ssh2/transport/TransportConnection.java
index a193503..216395b 100644
--- a/src/com/trilead/ssh2/transport/TransportConnection.java
+++ b/src/main/java/com/trilead/ssh2/transport/TransportConnection.java
@@ -1,284 +1,284 @@
-
-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.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 at 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;
-
-	/* 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 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;
-
-		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");
-		}
-
-		return payload_length;
-	}
-}
+
+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.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 at 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;
+
+	/* 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 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;
+
+		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");
+		}
+
+		return payload_length;
+	}
+}
diff --git a/src/com/trilead/ssh2/transport/TransportManager.java b/src/main/java/com/trilead/ssh2/transport/TransportManager.java
similarity index 94%
rename from src/com/trilead/ssh2/transport/TransportManager.java
rename to src/main/java/com/trilead/ssh2/transport/TransportManager.java
index 1922c1f..f204fc8 100644
--- a/src/com/trilead/ssh2/transport/TransportManager.java
+++ b/src/main/java/com/trilead/ssh2/transport/TransportManager.java
@@ -1,782 +1,787 @@
-
-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.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 at 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 asynchronousQueue = new Vector();
-	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 = (byte[]) 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 messageHandlers = new Vector();
-
-	Thread receiveThread;
-
-	Vector connectionMonitors = new Vector();
-	boolean monitorsWereInformed = false;
-	private ClientServerHello versions;
-
-	/**
-	 * 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 ClientServerHello getVersionInfo() {
-		return versions;
-	}
-
-	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());
-		versions = csh;
-
-		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 = (HandlerEntry) 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 = (HandlerEntry) 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);
-	}
-
-	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;
-			}
-
-			MessageHandler mh = null;
-
-			for (int i = 0; i < messageHandlers.size(); i++)
-			{
-				HandlerEntry he = (HandlerEntry) 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);
-		}
-	}
-}
+
+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.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 at 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 asynchronousQueue = new Vector();
+	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 = (byte[]) 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 messageHandlers = new Vector();
+
+	Thread receiveThread;
+
+	Vector connectionMonitors = new Vector();
+	boolean monitorsWereInformed = false;
+	private ClientServerHello versions;
+
+	/**
+	 * 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 ClientServerHello getVersionInfo() {
+		return versions;
+	}
+
+	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, int readTimeout) throws IOException
+	{
+		/* See the comment for createInetAddress() */
+
+		if (proxyData == null)
+		{
+			InetAddress addr = createInetAddress(hostname);
+			sock.connect(new InetSocketAddress(addr, port), connectTimeout);
+			sock.setSoTimeout(readTimeout);
+			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(readTimeout);
+
+			/* 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 {
+        initialize(cwl, verifier, dhgex, connectTimeout, 0, rnd, proxyData);
+    }
+    
+    public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
+			int connectTimeout, int readTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException
+	{
+		/* First, establish the TCP connection to the SSH-2 server */
+
+		establishConnection(proxyData, connectTimeout, readTimeout);
+
+		/* 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());
+		versions = csh;
+
+		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 = (HandlerEntry) 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 = (HandlerEntry) 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);
+	}
+
+	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;
+			}
+
+			MessageHandler mh = null;
+
+			for (int i = 0; i < messageHandlers.size(); i++)
+			{
+				HandlerEntry he = (HandlerEntry) 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);
+		}
+	}
+}
diff --git a/src/com/trilead/ssh2/util/TimeoutService.java b/src/main/java/com/trilead/ssh2/util/TimeoutService.java
similarity index 95%
rename from src/com/trilead/ssh2/util/TimeoutService.java
rename to src/main/java/com/trilead/ssh2/util/TimeoutService.java
index b09ed07..3d52161 100644
--- a/src/com/trilead/ssh2/util/TimeoutService.java
+++ b/src/main/java/com/trilead/ssh2/util/TimeoutService.java
@@ -1,149 +1,149 @@
-
-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 at 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();
-		}
-	}
-
-}
+
+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 at 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();
+		}
+	}
+
+}
diff --git a/src/com/trilead/ssh2/util/Tokenizer.java b/src/main/java/com/trilead/ssh2/util/Tokenizer.java
similarity index 95%
rename from src/com/trilead/ssh2/util/Tokenizer.java
rename to src/main/java/com/trilead/ssh2/util/Tokenizer.java
index ff3fcba..dfd480b 100644
--- a/src/com/trilead/ssh2/util/Tokenizer.java
+++ b/src/main/java/com/trilead/ssh2/util/Tokenizer.java
@@ -1,51 +1,51 @@
-
-package com.trilead.ssh2.util;
-
-/**
- * Tokenizer. Why? Because StringTokenizer is not available in J2ME.
- * 
- * @author Christian Plattner, plattner at 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;
-	}
-}
+
+package com.trilead.ssh2.util;
+
+/**
+ * Tokenizer. Why? Because StringTokenizer is not available in J2ME.
+ * 
+ * @author Christian Plattner, plattner at 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;
+	}
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/trilead-ssh2.git



More information about the pkg-java-commits mailing list