adds experimental support for SSH tunneling to the Java client.  Has not been tested with large desktop sizes yet.  CMakeLists.txt needs some rework.

git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4882 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt
index eb391e3..d00bedb 100644
--- a/java/CMakeLists.txt
+++ b/java/CMakeLists.txt
@@ -5,7 +5,7 @@
 
 find_package(Java)
 
-set(DEFAULT_JAVACFLAGS "-source 1.5 -target 1.5 -Xlint:all,-serial,-cast")
+set(DEFAULT_JAVACFLAGS "-source 1.5 -target 1.5 -Xlint:all,-serial,-cast,-unchecked,-fallthrough,-dep-ann")
 set(JAVACFLAGS ${DEFAULT_JAVACFLAGS} CACHE STRING
   "Java compiler flags (Default: ${DEFAULT_JAVACFLAGS})")
 message(STATUS "Java compiler flags = ${JAVACFLAGS}")
@@ -27,7 +27,7 @@
   F8Menu
   OptionsDialogCallback
   PasswdDialog
-  PixelBufferImage
+  PlatformPixelBuffer
   OptionsDialog
   ServerDialog
   UserPrefs
@@ -41,17 +41,69 @@
     ${CMAKE_CURRENT_BINARY_DIR}/${CLASSPATH}/${class}.class)
 endforeach()
 
+set(JSCH_CLASSNAMES
+  DH
+  DHG1
+  DHG14
+  DHGEX
+  JSch
+  Session
+  UserAuth
+  UserAuthKeyboardInteractive
+  UserAuthPassword
+  UserAuthPublicKey
+  UserAuthNone
+  jce/AES128CBC
+  jce/AES192CTR
+  jce/ARCFOUR128
+  jce/BlowfishCBC
+  jce/HMACMD5
+  jce/KeyPairGenDSA
+  jce/Random
+  jce/SignatureRSA
+  jce/AES128CTR
+  jce/AES256CBC
+  jce/ARCFOUR256
+  jce/DH
+  jce/HMACSHA196
+  jce/KeyPairGenRSA
+  jce/SHA1
+  jce/TripleDESCBC
+  jce/AES192CBC
+  jce/AES256CTR
+  jce/ARCFOUR
+  jce/HMACMD596
+  jce/HMACSHA1
+  jce/MD5
+  jce/SignatureDSA
+  jce/TripleDESCTR
+  jcraft/Compression
+  jcraft/HMAC
+  jcraft/HMACMD596
+  jcraft/HMACMD5
+  jcraft/HMACSHA196
+  jcraft/HMACSHA1)
+set(JSCH_SRCDIR ${CMAKE_CURRENT_SOURCE_DIR}/com/jcraft/jsch)
+foreach(class ${JSCH_CLASSNAMES})
+  set(JAVA_SOURCES ${JAVA_SOURCES} ${JSCH_SRCDIR}/${class}.java)
+  set(JAVA_CLASSES_FULL ${JAVA_CLASSES_FULL}
+    ${CMAKE_CURRENT_BINARY_DIR}/com/jcraft/jsch/${class}.class)
+endforeach()
+
 file(GLOB DEPEND_SOURCES
   ${CMAKE_CURRENT_SOURCE_DIR}/com/tigervnc/rfb/*.java
   ${CMAKE_CURRENT_SOURCE_DIR}/com/tigervnc/rdr/*.java
   ${CMAKE_CURRENT_SOURCE_DIR}/com/tigervnc/network/*.java
+  ${CMAKE_CURRENT_SOURCE_DIR}/com/jcraft/jsch/*.java
+  ${CMAKE_CURRENT_SOURCE_DIR}/com/jcraft/jsch/jcraft/*.java
+  ${CMAKE_CURRENT_SOURCE_DIR}/com/jcraft/jsch/jgss/*.java
   ${CMAKE_CURRENT_SOURCE_DIR}/com/jcraft/jzlib/*.java)
 
 string(REGEX REPLACE " " ";" JAVACFLAGS "${JAVACFLAGS}")
 add_custom_command(OUTPUT ${JAVA_CLASSES_FULL}
   DEPENDS ${JAVA_SOURCES} ${DEPEND_SOURCES}
   COMMAND ${JAVA_COMPILE}
-  ARGS ${JAVACFLAGS} -cp ${CMAKE_CURRENT_SOURCE_DIR}
+  ARGS ${JAVACFLAGS} -sourcepath ${CMAKE_CURRENT_SOURCE_DIR}
     -d ${CMAKE_CURRENT_BINARY_DIR} ${JAVA_SOURCES})
 
 configure_file(${CLASSPATH}/timestamp.in ${CLASSPATH}/timestamp)
@@ -84,6 +136,9 @@
     com/tigervnc/rdr/*.class
     com/tigervnc/network/*.class
     com/jcraft/jzlib/*.class
+    com/jcraft/jsch/jcraft/*.class
+    com/jcraft/jsch/jce/*.class
+    com/jcraft/jsch/*.class
     com/tigervnc/vncviewer/tigervnc.png
     com/tigervnc/vncviewer/tigervnc.ico
   COMMAND ${CMAKE_COMMAND}
diff --git a/java/com/jcraft/jsch/Buffer.java b/java/com/jcraft/jsch/Buffer.java
new file mode 100644
index 0000000..a3135a1
--- /dev/null
+++ b/java/com/jcraft/jsch/Buffer.java
@@ -0,0 +1,254 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class Buffer{
+  final byte[] tmp=new byte[4];
+  byte[] buffer;
+  int index;
+  int s;
+  public Buffer(int size){
+    buffer=new byte[size];
+    index=0;
+    s=0;
+  }
+  public Buffer(byte[] buffer){
+    this.buffer=buffer;
+    index=0;
+    s=0;
+  }
+  public Buffer(){ this(1024*10*2); }
+  public void putByte(byte foo){
+    buffer[index++]=foo;
+  }
+  public void putByte(byte[] foo) {
+    putByte(foo, 0, foo.length);
+  }
+  public void putByte(byte[] foo, int begin, int length) {
+    System.arraycopy(foo, begin, buffer, index, length);
+    index+=length;
+  }
+  public void putString(byte[] foo){
+    putString(foo, 0, foo.length);
+  }
+  public void putString(byte[] foo, int begin, int length) {
+    putInt(length);
+    putByte(foo, begin, length);
+  }
+  public void putInt(int val) {
+    tmp[0]=(byte)(val >>> 24);
+    tmp[1]=(byte)(val >>> 16);
+    tmp[2]=(byte)(val >>> 8);
+    tmp[3]=(byte)(val);
+    System.arraycopy(tmp, 0, buffer, index, 4);
+    index+=4;
+  }
+  public void putLong(long val) {
+    tmp[0]=(byte)(val >>> 56);
+    tmp[1]=(byte)(val >>> 48);
+    tmp[2]=(byte)(val >>> 40);
+    tmp[3]=(byte)(val >>> 32);
+    System.arraycopy(tmp, 0, buffer, index, 4);
+    tmp[0]=(byte)(val >>> 24);
+    tmp[1]=(byte)(val >>> 16);
+    tmp[2]=(byte)(val >>> 8);
+    tmp[3]=(byte)(val);
+    System.arraycopy(tmp, 0, buffer, index+4, 4);
+    index+=8;
+  }
+  void skip(int n) {
+    index+=n;
+  }
+  void putPad(int n) {
+    while(n>0){
+      buffer[index++]=(byte)0;
+      n--;
+    }
+  }
+  public void putMPInt(byte[] foo){
+    int i=foo.length;
+    if((foo[0]&0x80)!=0){
+      i++;
+      putInt(i);
+      putByte((byte)0);
+    }
+    else{
+      putInt(i);
+    }
+    putByte(foo);
+  }
+  public int getLength(){
+    return index-s;
+  }
+  public int getOffSet(){
+    return s;
+  }
+  public void setOffSet(int s){
+    this.s=s;
+  }
+  public long getLong(){
+    long foo = getInt()&0xffffffffL;
+    foo = ((foo<<32)) | (getInt()&0xffffffffL);
+    return foo;
+  }
+  public int getInt(){
+    int foo = getShort();
+    foo = ((foo<<16)&0xffff0000) | (getShort()&0xffff);
+    return foo;
+  }
+  public long getUInt(){
+    long foo = 0L;
+    long bar = 0L;
+    foo = getByte();
+    foo = ((foo<<8)&0xff00)|(getByte()&0xff);
+    bar = getByte();
+    bar = ((bar<<8)&0xff00)|(getByte()&0xff);
+    foo = ((foo<<16)&0xffff0000) | (bar&0xffff);
+    return foo;
+  }
+  int getShort() {
+    int foo = getByte();
+    foo = ((foo<<8)&0xff00)|(getByte()&0xff);
+    return foo;
+  }
+  public int getByte() {
+    return (buffer[s++]&0xff);
+  }
+  public void getByte(byte[] foo) {
+    getByte(foo, 0, foo.length);
+  }
+  void getByte(byte[] foo, int start, int len) {
+    System.arraycopy(buffer, s, foo, start, len); 
+    s+=len;
+  }
+  public int getByte(int len) {
+    int foo=s;
+    s+=len;
+    return foo;
+  }
+  public byte[] getMPInt() {
+    int i=getInt();  // uint32
+    if(i<0 ||  // bigger than 0x7fffffff
+       i>8*1024){
+      // TODO: an exception should be thrown.
+      i = 8*1024; // the session will be broken, but working around OOME.
+    }
+    byte[] foo=new byte[i];
+    getByte(foo, 0, i);
+    return foo;
+  }
+  public byte[] getMPIntBits() {
+    int bits=getInt();
+    int bytes=(bits+7)/8;
+    byte[] foo=new byte[bytes];
+    getByte(foo, 0, bytes);
+    if((foo[0]&0x80)!=0){
+      byte[] bar=new byte[foo.length+1];
+      bar[0]=0; // ??
+      System.arraycopy(foo, 0, bar, 1, foo.length);
+      foo=bar;
+    }
+    return foo;
+  }
+  public byte[] getString() {
+    int i = getInt();  // uint32
+    if(i<0 ||  // bigger than 0x7fffffff
+       i>256*1024){
+      // TODO: an exception should be thrown.
+      i = 256*1024; // the session will be broken, but working around OOME.
+    }
+    byte[] foo=new byte[i];
+    getByte(foo, 0, i);
+    return foo;
+  }
+  byte[] getString(int[]start, int[]len) {
+    int i=getInt();
+    start[0]=getByte(i);
+    len[0]=i;
+    return buffer;
+  }
+  public void reset(){
+    index=0;
+    s=0;
+  }
+  public void shift(){
+    if(s==0)return;
+    System.arraycopy(buffer, s, buffer, 0, index-s);
+    index=index-s;
+    s=0;
+  }
+  void rewind(){
+    s=0;
+  }
+
+  byte getCommand(){
+    return buffer[5];
+  }
+
+  void checkFreeSize(int n){
+    if(buffer.length<index+n){
+      byte[] tmp = new byte[buffer.length*2];
+      System.arraycopy(buffer, 0, tmp, 0, index);
+      buffer = tmp;
+    }
+  }
+
+/*
+  static String[] chars={
+    "0","1","2","3","4","5","6","7","8","9", "a","b","c","d","e","f"
+  };
+  static void dump_buffer(){
+    int foo;
+    for(int i=0; i<tmp_buffer_index; i++){
+        foo=tmp_buffer[i]&0xff;
+	System.err.print(chars[(foo>>>4)&0xf]);
+	System.err.print(chars[foo&0xf]);
+        if(i%16==15){
+          System.err.println("");
+	  continue;
+	}
+        if(i>0 && i%2==1){
+          System.err.print(" ");
+	}
+    }
+    System.err.println("");
+  }
+  static void dump(byte[] b){
+    dump(b, 0, b.length);
+  }
+  static void dump(byte[] b, int s, int l){
+    for(int i=s; i<s+l; i++){
+      System.err.print(Integer.toHexString(b[i]&0xff)+":");
+    }
+    System.err.println("");
+  }
+*/
+
+}
diff --git a/java/com/jcraft/jsch/ChangeLog b/java/com/jcraft/jsch/ChangeLog
new file mode 100644
index 0000000..7642c28
--- /dev/null
+++ b/java/com/jcraft/jsch/ChangeLog
@@ -0,0 +1,794 @@
+ChangeLog of JSch
+====================================================================
+Last modified: Thu Feb  2 09:04:04 UTC 2012
+
+	
+Changes since version 0.1.45:
+- bugfix: in the agent forwarding mode, "ssh-add -l" on the remote
+          will freeze.                                                 FIXED
+- bugfix: requests should not be sent to the closed channel.           FIXED
+- bugfix: ChannelShell#setAgentForwarding(true) will cause
+          resource leaks.                                              FIXED
+- change: for the efficiency, channel opening will be delayed
+          in local port forwarding.
+- change: added examples/Sudo.java to demonstrate sudo on exec channel.
+- change: authentication trials will be failed at 6 failures by the default.
+- change: updating copyright messages; 2011 -> 2012
+- feature: added JSch#setIdentityRepository(IdentityRepository irepo) to
+	   integrate with jsch-agent-proxy.
+
+	
+Changes since version 0.1.44:
+- bugfix: fields referred by multiple threads simultaneously should be
+	  volatile.                                                    FIXED 
+- bugfix: use local window size offered by the remote in sftp put.
+                                                                       FIXED 
+- bugfix: SftpProgressMonitor#init was not invoked in sftp-put
+	  for input-stream.                                            FIXED 
+- bugfix: sftp protocol version 3, 4 and 5 should allow only 
+	  UTF-8 encoding.                                              FIXED 
+- bugfix: Channel Subsystem had failed to set X forwarding flag.
+                                                                       FIXED 
+- bugfix: Channel X11 had leaked some resources.
+                                                                       FIXED 
+- bugfix: packet compression may break sessions
+	  in some case(transferring deflated data).                    FIXED 
+- bugfix: failed to set dev-null for logger
+                                                                       FIXED 
+- bugfix: even in sftp protocol version 3 session, some sftpd sends data
+	  packets defined in sftp protocol 6 ;-( working around it.    FIXED 
+- bugfix: ChannelSftp file globbing logic had missed 
+	  the string "foo\\\*bar" as a pattern.                        FIXED 
+- bugfix: sequential accesses to ChannelSftp by multiple threads may
+	  break its I/O channel.
+	  https://bugs.eclipse.org/bugs/show_bug.cgi?id=359184         FIXED 
+- bugfix: KeyPair.load can not handle private keys cyphered with AES.  FIXED
+- change: to improve sftp-put performance, send multiple packet at one time.
+- change: wait/notify will be used instead of sleep loop 
+          in establishing channel connections.
+- change: increasing local window size for sftp get.
+- change: updating copyright messages; 2010 -> 2011
+- change: src/com -> src/main/java/com
+- feature: key-exchange method "diffie-hellman-group14-sha1"
+	  (RFC4253#section-8.2)
+- feature: KeyPair#getPlulicKeyCommment() is added.
+
+	
+Changes since version 0.1.43:
+- bugfix: hmac-md5-96 and hmac-sha1-96 are broken. FIXED.
+- bugfix: working around OOME in parsing broken data from the remote. FIXED.
+- bugfix: failed to send very long command for exec channels. FIXED.
+- bugfix: in some case, failed to get the response 
+	  for remote port-forwarding request.  FIXED.
+- feature: support for private keys ciphered with aes192-cbc and aes128-cbc.
+
+	
+Changes since version 0.1.42:
+- bugfix: the remote window size must be in unsigned int.  FIXED.
+- bugfix: support for EBCDIC environment.  FIXED.
+- bugfix: data may be written to the closed channel.  FIXED.
+- bugfix: NPE in closing channels.  FIXED.
+- bugfix: the private key file may include garbage data before its header.  FIXED.
+- bugfix: the session down may not be detected during the re-keying process.  FIXED.
+- change: try keyboard-interactive auth with the given password if UserInfo is not given.
+- change: working around the wrong auth method list sent by some SSHD 
+          in the partial auth success.
+- change: working around the CPNI-957037 Plain-text Recovery Attack.
+- change: in searching for [host]:non-default port in known_hosts, 
+          host:22 should be also checked.
+- change: updating copyright messages; 2009 -> 2010
+
+	
+Changes since version 0.1.41:
+- bugfix: making exec request during re-keying process will cause 
+	  the dead lock for the session.                        FIXED.
+          Many thanks for PanLi at Prominic dot NET and www.prominic.net, 
+	  US based hosting company.  Without their testing JSch with
+  	  hundreds of hosts and their bug reports, this problem 
+	  was not fixed.
+- change: updating copyright messages; 2008 -> 2009	
+
+	
+Changes since version 0.1.40:
+- bugfix: canceling the remote port-forwarding with the incorrect 
+	  bind-address.                                         FIXED.
+- bugfix: sftp had missed to close the file in some case.       FIXED.
+- bugfix: ls(sftp) will throw an exception for the empty directory
+	  in connecting to some sftpd server.                   FIXED.
+- change: dropping the session gently in accepting incorrect packets.
+- change: by the default, aes128-ctr will be chosen if it is available
+	  on the local and the remote.
+- feature: adding the support for the private key ciphered in AES256.
+- feature: new ciphers: aes128-ctr,aes192-ctr,aes256-ctr,
+	   3des-ctr,arcfour,arcfour128 ,arcfour256
+
+	
+Changes since version 0.1.39:
+- bugfix: ProxySOCKS4 had not been functional.                  FIXED.
+- bugfix: NPE at closing the session when it is not opened.     FIXED.
+- change: JSch#getConfing has become public.
+
+	
+Changes since version 0.1.38:
+- bugfix: session will be dropped at rekeying.                  FIXED.
+- bugfix: NPE should not be thrown at unexpected session drop.  FIXED.
+- change: Channel#getSession() may throw JSchExecption.
+
+		
+Changes since version 0.1.37:
+- bugfix: NPE should not be thrown at unexpected session drop.  FIXED.
+- bugfix: AIOOBE at Session#connect().                         FIXED.
+- bugfix: Even if 'true' is given for
+	    Channel#setOutputStream(OutputStream out, boolean dontclose)
+	  as the second paramter, 'out' will be closed.        FIXED.
+- change: 'examples/Sftp.java' has been modified to demonstrate
+          ChannelSftp#reaplpath(String path)
+- change: setEnv(Hashtable env) for exec and shell channels have been
+	  marked as @deprecated
+- feature: setEnv(String name, String value) has been added to exec
+	   and shell channels.
+- feature: setEnv(byte[] name, byte[] value) has been added to exec
+	   and shell channels.
+- feature: ChannelSftp#realpath(String path) has been added.
+- feature: ChannelExec#setCommand(byte[] command) has been added.
+- feature: com.jcraft.jsch.ChannelSftp.LsEntry has implemented
+	   java.lang.Comparable
+- feature: Session#getServerAliveInterval(), Session#getServerAliveCountMaX()
+	   have been added.
+
+
+Changes since version 0.1.36:
+- bugfix: some sftpd will send invalid data in sftp protocol 
+          point of view, and we need to work around such data. FIXED.
+- bugfix: the stream forwarding had been broken since 0.1.30.  FIXED.
+- bugfix: failed to detect 'SSH_MSG_CHANNEL_OPEN_FAILURE'.     FIXED.
+- bugfix: ChannelSftp will generate the unfavorable absolute pathname
+	  in some case.                                        FIXED.
+- bugfix: failed to ignore the invalid public-key file.        FIXED.
+- change: ignoring empty data sent by 'SSH_MSG_CHANNEL_DATA' and
+           'SSH_MSG_CHANNEL_EXTENDED_DATA'.
+- change: updating copyright messages; 2007 -> 2008
+- change: build.xml will enable 'javac.debug' option by the default.
+- change: added logging messages to IndentityFile and Session class.
+- change: followings are deprecated methods,
+	   InputStream ChannelSftp#get(String src, 
+	                               int mode)
+	   InputStream ChannelSftp#get(String src, 
+	                               SftpProgressMonitor, 
+	                               int mode)
+- feature: following method is added,
+	   InputStream ChannelSftp#get(String src, 
+	                               SftpProgressMonitor monitor, 
+	                               long skip)
+
+	
+Changes since version 0.1.35:			
+- bugfix: ChannelSftp can not handle the local filenames correctly on Windows. FIXED.
+- bugfix: '/' must be handled as the file separator on JVM for Windows.  FIXED.
+- change: the system property
+	    "javax.security.auth.useSubjectCredsOnly"
+	  will be set to "false" for "gssapi-with-mic"
+	  if that property is not given explicitly.
+- change: added changes about ChannelSftp#{pwd(), home()} to
+	  ChangeLog; 'Changes since version 0.1.34:' section.
+	
+	
+Changes since version 0.1.34:			
+- bugfix: the OutputStream from the channel may make the JVM
+	  lockup in some case.                                 FIXED.
+	  There was a possibility that Channel#connect() may be failed 
+	  to initialize its internal without throwing the JSchException.
+	  On such case, the write operation for OutputStream from 
+	  that channel will cause the system(JVM) to lock up.
+- bugfix: ChannelSftp had problems filename globbing.              FIXED.
+- bugfix: the message included in SSH_FXP_STATUS must be UTF-8.    FIXED.
+- change: ChannelSftp supports the filename globbing for 
+	  the filename in multi-byte characters.
+- change: ChannelSftp will internally handle filenames in UTF-8 encoding. 
+- change: ChannelSftp#pwd() may throw an SftpException.
+- change: ChannelSftp#home() may throw an SftpException.
+- feature: following methods have been added in ChannelSftp
+	   String getServerVersion()
+	   String getClientVersion()
+	   void setFilenameEncoding(String encoding)
+	   String getExtension(String key)
+	
+Changes since version 0.1.33:			
+- bugfix: there had a possibility that the session may be broken 
+	  if ciphers for upward/downward streams are different.    FIXED.
+- bugfix: the authentication method "keyboard-interactive" had 
+	  not been tried without UserInfo.                         FIXED.
+- bugfix: ChannelShell#setTerminalMode(byte[] terminal_mode) had 
+	  not been functional.                                     FIXED.
+- bugfix: the remote port-forwarding to the daemon had been broken 
+	  since 0.1.30.                                            FIXED. 
+- change: the cipher "aes128-cbc" will be used if AES is available.
+- change: the interface 'com.jcraft.jsch.ForwardedTCPIPDaemon' has been changed.
+- change: the data transfer rate will be improved on some environment.
+- feature: ChannelExec can control the size of pty;
+	   ChannelExec#setPtySize(int col, int row, int wp, int hp) is
+           added.
+- feature: the property "CheckCiphers" has been added.
+	   Refer to 'examples/AES.java'.
+- feature: Session#setConfig(String key, String value),
+	   JSch#setConfig(String key, String value) have been added.
+
+	
+Changes since version 0.1.32:			
+- bugfix: freeze in 'diffie-hellman-group-exchange-sha1'.          FIXED.
+          By the default, 'diffie-hellman-group1-sha1' will be used
+	  and if you have not chosen 'diffie-hellman-group-exchange-sha1'
+	  explicitly, you don't have to worry about it.
+- bugfix: there should be timeout mechanism in opening a socket
+	  for remote port forwarding.                              FIXED.
+	  At the failure or timeout, 'SSH_MSG_CHANNEL_OPEN_FAILURE'
+	  will be sent to sshd.
+- bugfix: there should be timeout mechanism in opening a socket
+	  for X11 forwarding.                                      FIXED.
+	  At the failure or timeout, 'SSH_MSG_CHANNEL_OPEN_FAILURE'
+          will be sent to sshd.
+
+	
+Changes since version 0.1.31:			
+- bugfix: remote port forwarding will be hanged at its closing.    FIXED.
+- bugfix: X forwarding channels will be hanged and some resources 
+	  will be leaked whenever they are closed.                 FIXED.
+- bugfix: failed to detect "Connection refused".                   FIXED.
+- bugfix: at the failure for keyboard-interactive auth method, 
+	a session will be terminated.                              FIXED.
+- bugfix: due to the cancel for keyboard-interactive auth method, 
+	a session will be terminated.                              FIXED.
+- change: com.jcraft.jsch.jcraft.Compression#uncompress will respect
+	the argument "start".
+- change: "gssapi-with-mic" will choose the default credential.
+- feature: Session#setPortForwardingL will return the assigned local
+	TCP port number; TCP port will be assigned dynamically if lport==0.
+- feature: support for SSH_MSG_UNIMPLEMENTED.
+- feature: support for PASSWD_CHANGEREQ. 
+
+	
+Changes since version 0.1.30:		
+- bugfix: a problem in padding for ciphered private key.  
+	PKCS#5 padding should be used.                             FIXED.
+- bugfix: crash in parsing invalid public key file.                FIXED.
+- bugfix: a public key may not have a comment.                     FIXED.
+- bugfix: output stream from ChannelSftp#put will hang if it is closed
+	  twice.                                                   FIXED.
+- feature: agent forwarding. To enable this functionality,
+	Channel{Exec,Shell,Sftp}#setAgentForwarding(boolean enable) are added.
+- feature: ChannelShell#setTerminalMode(byte[] terminal_mode) is added.
+- feature: Session#setDaemonThread(boolean true) to run internal threads as
+	   daemon threads.
+- feature: an option "PreferredAuthentications" is added.
+	   The default value is "gssapi-with-mic,publickey,keyboard-interactive,password".
+- change: if alias-name is given, non-standard TCP port number will not be
+	  saved in 'known_hosts' file.
+
+Changes since version 0.1.29:		
+- bugfix: ChannelSftp#cd() will not throw an exception even if
+	  a file is given.                                           FIXED.
+- bugfix: ChannelSftp#put() has a bug which will appear in using
+	  on the slow network.                                       FIXED.
+- bugfix: ChannelSftp#ls() has a bug which will appear in using
+	  on the slow network.                                       FIXED.
+- bugfix: a bug had sneaked in the remote port forwarding.           FIXED.
+- bugfix: some APIs from JCE have the memory leak on Mac OS X,
+	  so we have re-written the code(com.jcraft.jsch.jcraft.HMAC*
+	  classes) without them.  On Mac OS X, such new classes will
+	  be used automatically.                                     FIXED.
+- bugfix: the session will be crashed by the long banner message.    FIXED.
+- change: '/dev/random' will not be referred on Gnu/Linux.
+- change: if non-standard TCP port number is used, that number will 
+	  be saved in known_hosts file as recent OpenSSH's ssh client does.
+- change: Channel#getOutputStream will not use Piped{Output,Input}Stream.
+- change: com.jcraft.jsch.HostKeyRepository interface has been
+	  slightly modified.
+- feature: Session#setPortForwardingR(String bind_address, ...) has been added.
+- feature: the packet compression method 'zlib@openssh.com' has been supported.
+- feature: the hashed known_hosts file has been supported.
+	   Refer to 'examples/KnownHosts.java'.
+- feature: the authentication method 'gssapi-with-mic' has been
+ 	   experimentally supported.
+- feature: com.jcraft.jsch.Logger interface and 
+	   JSch#setLogger(Logger logger) have been added.
+	   Refer to 'examples/Logger.java' for the usage.
+
+
+Changes since version 0.1.28:	
+- bugfix: ChannelSftp#put will stack in some situations              FIXED.
+- bugfix: ChannelSftp invoked 'glob_remote' redundantly.             FIXED.
+- bugfix: ChannelSftp failed to make globbing for some file names.    FIXED.
+- bugfix: ChannelSftp did not carefully read its input-stream.       FIXED.
+- bugfix: ChannelSftp#lstat did not try globbing for given path.      FIXED.
+- bugfix: at closing channel, eof_lcoal and eof_remote did not
+          become true.                                               FIXED.
+- bugfix: IdentityFile did not carefully read file input-streams.    FIXED.
+- bugfix: KeyPair did not carefully read file input-streams.         FIXED.
+- bugfix: ProxySOCKS4 did not carefully read file input-streams.     FIXED.
+- bugfix: ProxySOCKS5 did not carefully read file input-streams.     FIXED.
+- bugfix: ForwardedTCPIPDaemom may fail in some situation.           FIXED.
+- bugfix: X forwarding failed to handle the magic-cookie 
+	  in some case                                               FIXED.
+          Thanks to Walter Pfannenmller.
+- bugfix: setKnownHosts in KnownHosts.java doesn't read the last
+	  line if linefeed is missing                                FIXED.
+          Thanks to Henrik Langos.
+- bugfix: With StrictHostKeyChecking set to yes connect()
+          shouldn't ask.                                             FIXED.
+          Thanks to Henrik Langos.
+- change: Identity#setPassphrase(String passphrase) is replaced with
+          Identity#setPassphrase(byte[] passphrase).
+- change: IdentityFile will clear its internal secrets at finalizing.
+- change: KeyPair will clear its internal secrets at finalizing.
+- change: KeyPair will clear its internal secrets at finalizing.
+- change: MAC#doFinal() is replaced with
+          MAC#doFile(byte[] buf, int offset)
+- change: at TCP socket reading timeout, keep-alive message will be sent
+          to the remote sshd.  To disable this functionality, invoke
+          explicitly Session.setServerAliveCountMax(0)
+- change: PortWatcher stops to use InetAddress.getByAddress().
+- change: in the user authentication, username, password and passphrase
+	  will be encoded in UTF-8.
+- change: JSch#addIdentity will check duplicate keys.
+- change: SftpException#message has been deleted.
+- change: SftpException#getMessage() will return the detailed message.
+- feature: IdentityFile#setPassphrase(byte[] passphrase) is added.
+- feature: IdentityFile#clear() is added to clear its internal secrets.
+- feature: KeyPair#decrypt(byte[] passphrase) is added.
+- feature: JSch#addIdentity(String path, byte[] passphrase) is added.
+- feature: JSch#getIdentityNames() is added.
+- feature: JSch#removeIdentity(String name) is added.
+- feature: JSch#removeAllIdentity() is added.
+- feature: ChannelSftp#readlink(String path) is added.
+- feature: ChannelSftp#getHome() is added.
+- feature: Channel#connect(int connectTimeout) is added.
+- feature: ChannelShell#setPtyType(String ttype) is added.
+- feature: Session#setPassword(byte[] password) is added.
+- feature: Session#setHostKeyAlias(String alias) is added.
+- feature: KeepAlive is implemented and 
+	   Session#setServerAliveInterval(int interval) and
+	   Session#setServerAliveCountMax(int count) are added.
+- feature: Session#sendKeepAliveMsg() is added.
+- feature: JSchException#getCause() may return a reason.
+- feature: SftpException#getCause() may return a reason.	
+- feature: ChannelExec#setErrStream(OutputStream out, boolean dontclose)
+           is added.
+	
+
+Changes since version 0.1.27:	
+- bugfix: ChannelSftp#localAbsolutePath did not work correctly.      FIXED.
+- bugfix: ChannelSftp#chmod did not work for directory.              FIXED.
+- bugfix: ProxyHTTP had a bug in processing HTTP headers.            FIXED.
+- bugfix: messages before server's version string should be ignored. FIXED.
+- feature: Environment Variable Passing.
+
+	
+Changes since version 0.1.26:
+- bugfix: there was a session crash bug. That occurrence is rare, but 
+          depends on the thread scheduling.  FIXED.
+- bugfix: problems in generating remote/local absolute paths on sftp.  FIXED.
+- bugfix: problems in handling cancel operations for sftp.  FIXED.
+- bugfix: ChannelX11s were not terminated for a wrong cookie.  FIXED.
+- bugfix: NoSuchAlgorithmException should be thrown if JCE is not 
+	  accessible.  FIXED.
+- bugfix: ProxyHTTP should check the return code from proxy.  FIXED.
+- bugfix: server's version string should be checked carefully.  FIXED.
+- feature: some more improvements on sftp uploading.
+- feature: 'getUserName' method is added to Session class.
+
+	
+Changes since version 0.1.25:		
+- bugfix: infinite loop/hang on connection at unexpected error during
+          key-exchanging operation.  FIXED
+- bugfix: delays on sftp uploading.  FIXED
+- bugfix: failed to purge a host-key from its repository in some case. FIXED.
+- feature: SOCKS4 proxy
+
+	
+Changes since version 0.1.24:		
+- bugfix: remote port forwarding is not established. FIXED. 
+- bugfix: failed to parse known_hosts file if it has a long public key blob.
+ 	  FIXED.
+- bugfix: sftp put/get operations keep failing.  FIXED.
+- bugfix: ChannelShell.setXForwarding always set xforwarding to be true. FIXED.
+- change: ChannelShell.setPty is added.
+- change: Proxy interface is free from session object.
+- change: added examples/ScpToNoneCipher.java to demonstrate NONE Cipher switching.
+- feature: added NONE Cipher switching support.
+- feature: timeout check will be enabled for proxy connections.
+
+	
+Changes since version 0.1.23:		
+- bugfix: there was resource leak problems in closing local port forwardings.
+  	  FIXED. 
+- bugfix: there was a session crash problems in using aes-cbc cipher.  FIXED. 
+- change: ChannelSession.setPtySize was redefined.
+- feature: added SocketFactory for remote port forwarding.
+	   Session.setPortForwardingR(int rport, String host, int lport, 
+	                              SocketFactory sf)
+- feature: added ServerSocketFactory for local port forwarding.	
+	   Session.setPortForwardingL(String boundaddress,
+	                              int lport, String host, int rport,
+	                              ServerSocketFactory ssf)
+
+Changes since version 0.1.22:		
+- bugfix: there was a freeze problem at fails on key-exchanging. FIXED.
+- bugfix: race-condition forcefully disconnects session in closing channels.
+	  FIXED. 
+
+	
+Changes since version 0.1.21:		
+- bugfix: there is a bug in read() method implementation for the
+          input-stream returned from ChannelSftp.get().  FIXED. 
+- bugfix: at fail on requesting for the remote port forwarding,
+	  an exception should be thrown. FIXED. 
+- bugfix: SSH_MSG_CHANNEL_OPEN request for the X11 forwarding should not
+	  be accepted if clients don not expect it. FIXED.
+- bugfix: there is a problem in transferring large data(mote than 1GB)
+	  to sshd from recent OpenSSH(3.6.1 or later). FIXED.
+	  For security concerns, those sshd will re-key periodically and 
+	  jsch had failed to handle it.
+- bugfix: 'exec', 'shell' and 'sftp' channels will fail if the acceptable
+	  packet size by remote sshd is not so large. FIXED.
+- bugfix: there are problems in 'InputStream ChannelSftp.get(String)' 
+   	  and 'OutputStream put(String)'.  FIXED.
+- feature: added boolean flag 'dontclose' to 
+	   * setInputStream(),
+	   * setOutputStream() and
+	   * setExtOutputStream() 
+           methods of Channel class.
+- feature: added 'com.jcraft.jsch.ChannelSubsystem'
+- feature: allowed to control the compression level in the packet compression.
+ 	   Refer to 'examples/Compression.java'.
+- change: modified 'com/jcraft/jsch/jce/KeyPairGenRSA.java' to be complied
+	  on JDK 1.2.
+- change: 'examples/ScpTo.java' and 'examples/ScpFrom.java' will use
+	  'long' type for the file size instead of 'int'.
+- change: 'Identity.getSignature' method will not expect 'session'.
+- change: while waiting for socket connection establishment, Thread.join
+	  will be used instead of Thread.sleep.
+
+
+Changes since version 0.1.20:		
+- known issue: there are problems in 'InputStream ChannelSftp.get(String)' 
+   	       and 'OutputStream put(String)'.  They will be re-implemented
+  	       in the next release.
+- bugfix: EOF packets should not be sent twice. This bug had crashed 
+	  the session on every channel close. FIXED.
+- bugfix: at the fail on opening connection to remote sshd,
+          a misleading exception "invalid server's version string"
+          had been thrown. FIXED.
+- bugfix: fixed a bug in hadling the size of remote channel window.
+- bugfix: channels should not be closed even if EOF is received. FIXED.
+- bugfix: fixed bugs in file name globbing on sftp.
+- change: to transfer packets efficiently, the size of internal buffer
+          for each channel has been increased.
+- change: ChannelSftp.ls will return a vector of 
+	  com.jcraft.jsch.ChannelSftp.LsEntry.  Sorry for inconveniences.
+- feature: added ForwardedTCPIPDaemon.  Refer to 'examples/Daemon.java', 
+           which demonstrates to provide network services like inetd.
+- feature: ChannelExec.setPty() method to request for assigning pseudo tty.
+- feature: added ciphers "aes128-cbc", "aes192-cbc" and "aes256-cbc".
+	   Refer to 'examples/AES.java'.
+- feature: local port-forwarding settings can be dynamically deleted 
+           by the bound address.
+- feature: added 'Channel.isClosed()'. Channel.getExitStatus() should be
+	   invoked after Channel.isClosed()==true.
+
+	
+Changes since version 0.1.19:		
+- ClassCastException while calling ChannelExec.finalize() method. FIXED.
+  Thanks to wswiatek at ais dot pl.
+
+	
+Changes since version 0.1.18:		
+- fixed problems related to thread-safety.
+  Thanks to Eric Meek at cs dot utk dot edu.
+- At the lost of the network connectivity to the remote SSHD,  clients 
+  connected to the local port were never made aware of the
+  disconnection.  FIXED.
+- fixed confusions in handling EOFs from local input stream and 
+  the input stream for remote process.
+- 'com.jcraft.jsch.jce.AES128CBC' is added, but it is not be functional in
+  this release.  It will work in the next release.
+- Some sshd(Foxit-WAC-Serve) waits for SSH_MSG_NEWKEYS request before
+  sending it. FIXED.	
+- fixed a problem in connecting to Cisco Devices.	
+  Thanks to Jason Jeffords at comcast dot net.
+- changed the method 'add' of 'HostKeyRepository' interface.
+- 'UIKeyborarInteracetive' will ignore empty prompt by sshd.
+- added 'sendIgnore()' method to 'Session' class.
+- added '-p' for scp command in 'examples/ScpTo.java' to preserve
+  modification times, access times, and modes from the original file.
+
+	
+Changes since version 0.1.17:		
+- added 'com.jcraft.jsch.HostKeyRepository' interface.
+  It will allow you to handle host keys in your own repository
+  (for example, RDB) instead of using 'known_hosts' file.
+- added methods to get the finger-print of host keys.
+  refer to 'examples/KnownHosts.java'.	
+- improved 'known_hosts' file handling.
+  - comment lines will be kept.
+  - SSH1 host keys will be kept.
+  - each hostname can have multiple host keys.	
+- fixed a crash bug in processing private keys which have too long key-bits.
+
+	
+Changes since version 0.1.16:		
+- 'com.jcraft.jsch.jce.DHG1' and 'com.jcraft.jsch.jce.DHGEX' are moved to
+  'com.jcraft.jsch' package.	 
+- added APIs to handle hostkeys included in 'known_hosts',
+     JSch.getHostKeys(),
+     JSch.removeHostKey()
+- allowing to set timeout value in opening sockets,
+     Session.connect(int timeout)
+
+	
+Changes since version 0.1.15:		
+- adding support of setting mtime for remote files on sftp channel.
+- setKnownHosts method of JSch class will accept InputStream.
+- implementation of Basic password authentication for HTTP proxy.
+- fixed a bug in checking which ssh protocol version remote sshd supports
+- SSH_MSG_CHANNEL_OPEN_FAILURE will be handled correctly.
+- methods in SftpATTRS class has been public.
+- working around SIGBLOB bug hidden in older sshd.
+
+	
+Changes since version 0.1.14:		
+- fixed a crash bug in accepting keep-alive messages.
+- the parent directory of 'known_hosts' file will be created 
+  if it does not exist.
+- the Subsystem channel support was removed.
+
+	
+Changes since version 0.1.13:		
+- added 'setClientVersion' method to Session class.
+- fixed hung-up problem on SftpChannel in connecting to
+  the sshd, which does not support sftp.
+- fixed OutOfMemory exception problem in decrypting a private key
+  with bad passphrase.
+- fixed hung-up problem in communicating with the sshd,
+  whose window size is small.
+- RuntimeExceptions will be thrown from jsch APIs.
+- SSH_MSG_CHANNEL_SUCCESS and SSH_MSG_CHANNEL_FAILURE requests
+  have been supported.
+  	
+	
+Changes since version 0.1.12:	
+- added the partial authentication support.
+- allowing to change the passphrase of a private key file
+  instead of creating a new private key.
+- added 'examples/ChangePassphrase.java' 	
+- the interface 'UIKeyboardInteractive' has been modified.
+
+	
+Changes since version 0.1.11:	
+- RSA keypair generation.
+- added the method 'getFingerPrint' to KeyPair class,
+  which will return the finger print of the public key.
+- fixed a bug in generating non-ciphered private key.
+
+	
+Changes since version 0.1.10:	
+- fixed a bug in the password authentication, sneaked in
+  0.1.9. By this bug, the password authentication had failed every time.
+
+	
+Changes since version 0.1.9:	
+- username and password can be in UTF-8 encoding.
+- DSA keypair generation.
+- added 'examples/KeyGen.java', which demonstrates
+  the DSA keypair generation.
+
+	
+Changes since version 0.1.8:	
+- fixed crash problems on the local port forwarding.
+- fixed crash problems on the remote port forwarding.
+- added setErrStream() and getErrStream() to ChannelExec.
+- added keyboard-interactive authentication support.
+- modified TripleDESCBC to support IBM's JDK 1.4.1.
+- added 'examples/UserAuthKI.java', which demonstrates keyboard-interactive
+  authentication.
+
+	
+Changes since version 0.1.7:	
+- added APIs for sftp resume downloads and uploads.
+  The author greatly appreciates 
+	elpernotador(webmaster at unlix dot com dot ar),
+  who motivated him to hack this functionality.
+- 'examples/Sftp.java' demonstrates sftp resume functionality.
+  Please refer to "put-resume", "put-append", "get-resume" and
+  "get-append" command.
+- added the support of 'window-change' request.
+- fixed a freeze bug in 'Inputstream get(String src)' method of 'ChannelSftp'
+  class.
+
+	
+Changes since version 0.1.6:	
+- added 'int getExitStatus()' method to 'Channel' class.
+- fixed crash bugs in closing channels for port forwarding.
+- fixed glitches in managing forwarded ports.
+
+	
+Changes since version 0.1.5:	
+- fixed crash bugs in port forwarding.
+- modified to use "ssh-rsa" for key-exchanging by the default.
+- the port forwarding setting can be canceled dynamically.
+- fixed a freeze bug in getting an empty file on sftp channel.
+
+	
+Changes since version 0.1.4:	
+- fixed a bug in managing local window size.
+  The local window should be consumed by CHANNEL_EXTENDED_DATA packet.
+- checking the availability of the ssh session in opening channels.
+  In some case, ssh session had been freezed.	
+- java.io.File.separator will be refereed in generating local pathname
+  on sftp channel.
+- absolute local pathname will be handled correctly on sftp channel.
+
+	
+Changes since version 0.1.3:	
+- fixed a serious bug, which had leaked resources related to
+  ChannelExec.	
+- added the older SFTP protocol(version 0, 1, 2) support.
+- fixed a problem in the authentication step for FSecure's sshd.
+- fixed a bug, which had broken Diffie-Hellman key exchange in some case.
+- implemented the file name globbing for local files on sftp session. 
+- fixed a bug in the file name globbing.
+- added an interface 'SftpProgressMonitor'.
+- modified 'examples/Sftp.java' to demonstrate displaying progress-bar
+  in transferring files. 	 
+	
+	
+Changes since version 0.1.2:	
+- modified 'build.xml' to allow Ant users to compile jsch with debug
+  support (i.e. line-number tables) by overriding the property
+  javac.debug on the command line.
+- added a property 'StrictHostKeyChecking'.
+- added 'UserAuthNone' class to request a list of authentication methods on
+  remote sshd.	 
+- channels will be managed in each sessions.
+- added 'ChannelSubsystem', which allows users to use their own
+  implementations for subsystems.
+- added 'isEOF()' method to 'Channel' class.
+- supported key pair files in DOS file format.
+
+	
+Changes since version 0.1.1:	
+- added the file name globbing support on sftp session.
+- fixed a bug in the public key authentication.
+  When there was not a public key in ~/.ssh/, that problem occurred.
+- improved the 'setTimeout' method.
+- fixed a typo in 'LICENSE.txt'
+
+	
+Changes since version 0.1.0:	
+- added 'rekey' method to 'Session' class for key re-exchanging.
+- added 'rekey' and 'compression' command to 'examples/Sftp.java'.
+- added new 'get' and 'put' methods to 'ChannelSftp'.
+  Those methods will operate I/O streams.
+- methods in 'ChannelSftp' will throw 'SftpException'	
+- 'ChannelSftp.Ssh_exp_name' is added for the output of 'ls'.
+  Thanks to Graeme Vetterlein.
+- added 'setTimeout' and 'getTimeout' methods to 'Session' class.
+- guess will be done in the algorithm negotiation step.
+- FSecure's DSA private key has been supported partially.
+- hostkeys will be saved into 'known_hosts' file.
+- fixed a bug in 'Util.toBase64' method.	
+- 'Identity' will reject unrecognized keys.
+- 'build.xml' will check if jzlib is available or not. 
+  Thanks to Stefan Bodewig.	
+- added javadoc target in 'build.xml'.
+  Thanks to Robert Anderson.
+	
+	
+Changes since version 0.0.13:	
+- fixed a bug in connecting to Fsecure's sshd on Windows.
+- the license is changed to BSD style.	
+
+
+Changes since version 0.0.12:	
+- fixed a bug in verifying DAS signatures.
+- added 'SftpATTR' class, which allow you to get attributes of remote files on 
+  sftp channel, and 'stat', 'lstat' method are added to 'ChannelSftp' class.
+- added 'getInputStream' and 'getOutputStream' methods Channel class, which 
+  return passive I/O streams.	 
+- 'setIdentity' method is deleted from 'Session' class and
+  'addIdentity' method is added to 'JSch' class
+- 'setUserName' method is deleted from 'Session' class and
+  'getSession' method of 'JSch' class is changed.
+- 'isConnected' method is added to 'Session' class.
+- 'UserInfo' interface is changed.
+
+	
+Changes since version 0.0.11:	
+- taking care of remote window size.
+- adding 'disconnect' method to 'Channel' and 'Session' classes.
+- signal sending support.
+- 'mkdir' command for sftp.
+- 'fromBase64' method has been moved to Util class and 'toBase64' method has
+   also been added to that class.
+- 'KnownHosts' class for checking host-key in 'known_host' file.
+- 'examples/KnownHosts.java' has been added.
+- 'setUserName' and 'setPassword' methods have been added to Session class.
+- 'UserInfo' interface has been changed.
+- The implementation of compression has moved to 'com.jcraft.jsch.jcraft'
+  package.
+- fixed a bug in handling 'SSH_MSG_CHANNEL_REQUET' request.
+- fixed a bug in sending multiple requests on a single session.
+	
+	
+Changes since version 0.0.10:	
+- Diffie-Hellman key exchange 'diffie-hellman-group1-sha1' is supported.
+  Refer to 'src/com/jcraft/jsch/jce/DHG1.java'.
+  Thanks to Mitsugu Kitano, whose feedback was very helpful.
+- By the default, 'diffie-hellman-group1-sha1' will be used in the
+  key exchange step.
+- The file attribute on 'SSH File Transfer Protocol' is supported.
+  Now, we can say JSch supports 'SSH File Transfer Protocol'.
+- 'examples/Sftp.java' is updated.
+  'chgrp','chown','chmod' commands are supported.
+
+	
+Changes since version 0.0.9:	
+- SSH File Transfer Protocol is supported partially.
+- 'examples/Sftp.java' is added.
+  This example is a tiny sftp command and supports 'cd','put','get','rm',etc.
+- 'close' method is added to Channel interface.	
+- build.xml for examples is added.
+  Thanks to Ronald Broberg.
+
+	
+Changes since version 0.0.8:	
+- the tunneling through a SOCKS5 proxy is supported.
+- 'examples/ScpFrom.java' is added.
+- 'com.jcraft.jsch.UserInfo' interface is modified.
+
+	
+Changes since version 0.0.7:	
+- Packet comression is supported.
+- 'examples/Compression.java' is added.
+- JZlib is included.
+
+	
+Changes since version 0.0.6:	
+- RSA host key is supported.
+- RSA public key authentication is supported.
+
+
+Changes since version 0.0.5:	
+- DSA public key authentication is supported.
+- examples/UserAuthPubKey.java is added.
+- examples/ScpTo.java is added.
+	
+
+Changes since version 0.0.4:	
+- 3des-cbc is supported.
+- hmac-sha1 is supported.
+- hmac-md5-96 is supported.
+- hmac-sha1-96 is supported.	
+	
+
+Changes since version 0.0.3:	
+- port forwarding, similar to the -L option of SSH.
+- examples/PortForwardingL.java is added.
+- examples/StreamForwarding.java is added.
+- examples/Exec.java is renamed as examples/Shell.java
+- stream forwarding is added.
+- ChannelSftp class is added for implementing filexfer.
+- interfaces for jsch users are changed.
+	
+
+Changes since version 0.0.2:	
+- remote exec is supported.	
+- examples/Exec.java is added.
+- build.xml and some scripts for Ant are added. (lbruand)
+- Const class is added. (lbruand)
+
+	
+Changes since version 0.0.1:
+- the tunneling via HTTP proxy is supported.
+- port forwarding like option -R of ssh command.
+  the given port on the remote host will be forwarded to the given host
+  and port on the local side.	
diff --git a/java/com/jcraft/jsch/Channel.java b/java/com/jcraft/jsch/Channel.java
new file mode 100644
index 0000000..669b575
--- /dev/null
+++ b/java/com/jcraft/jsch/Channel.java
@@ -0,0 +1,677 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+
+public abstract class Channel implements Runnable{
+
+  static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION=      91;
+  static final int SSH_MSG_CHANNEL_OPEN_FAILURE=           92;
+  static final int SSH_MSG_CHANNEL_WINDOW_ADJUST=          93;
+
+  static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED=    1;
+  static final int SSH_OPEN_CONNECT_FAILED=                 2;
+  static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE=           3;
+  static final int SSH_OPEN_RESOURCE_SHORTAGE=              4;
+
+  static int index=0; 
+  private static java.util.Vector pool=new java.util.Vector();
+  static Channel getChannel(String type){
+    if(type.equals("session")){
+      return new ChannelSession();
+    }
+    if(type.equals("shell")){
+      return new ChannelShell();
+    }
+    if(type.equals("exec")){
+      return new ChannelExec();
+    }
+    if(type.equals("x11")){
+      return new ChannelX11();
+    }
+    if(type.equals("auth-agent@openssh.com")){
+      return new ChannelAgentForwarding();
+    }
+    if(type.equals("direct-tcpip")){
+      return new ChannelDirectTCPIP();
+    }
+    if(type.equals("forwarded-tcpip")){
+      return new ChannelForwardedTCPIP();
+    }
+    if(type.equals("sftp")){
+      return new ChannelSftp();
+    }
+    if(type.equals("subsystem")){
+      return new ChannelSubsystem();
+    }
+    return null;
+  }
+  static Channel getChannel(int id, Session session){
+    synchronized(pool){
+      for(int i=0; i<pool.size(); i++){
+        Channel c=(Channel)(pool.elementAt(i));
+        if(c.id==id && c.session==session) return c;
+      }
+    }
+    return null;
+  }
+  static void del(Channel c){
+    synchronized(pool){
+      pool.removeElement(c);
+    }
+  }
+
+  int id;
+  volatile int recipient=-1;
+  protected byte[] type=Util.str2byte("foo");
+  volatile int lwsize_max=0x100000;
+  volatile int lwsize=lwsize_max;     // local initial window size
+  volatile int lmpsize=0x4000;     // local maximum packet size
+
+  volatile long rwsize=0;         // remote initial window size
+  volatile int rmpsize=0;        // remote maximum packet size
+
+  IO io=null;    
+  Thread thread=null;
+
+  volatile boolean eof_local=false;
+  volatile boolean eof_remote=false;
+
+  volatile boolean close=false;
+  volatile boolean connected=false;
+  volatile boolean open_confirmation=false;
+
+  volatile int exitstatus=-1;
+
+  volatile int reply=0; 
+  volatile int connectTimeout=0;
+
+  private Session session;
+
+  int notifyme=0; 
+
+  Channel(){
+    synchronized(pool){
+      id=index++;
+      pool.addElement(this);
+    }
+  }
+  synchronized void setRecipient(int foo){
+    this.recipient=foo;
+    if(notifyme>0)
+      notifyAll();
+  }
+  int getRecipient(){
+    return recipient;
+  }
+
+  void init() throws JSchException {
+  }
+
+  public void connect() throws JSchException{
+    connect(0);
+  }
+
+  public void connect(int connectTimeout) throws JSchException{
+    this.connectTimeout=connectTimeout;
+    try{
+      sendChannelOpen();
+      start();
+    }
+    catch(Exception e){
+      connected=false;
+      disconnect();
+      if(e instanceof JSchException) 
+        throw (JSchException)e;
+      throw new JSchException(e.toString(), e);
+    }
+  }
+
+  public void setXForwarding(boolean foo){
+  }
+
+  public void start() throws JSchException{}
+
+  public boolean isEOF() {return eof_remote;}
+
+  void getData(Buffer buf){
+    setRecipient(buf.getInt());
+    setRemoteWindowSize(buf.getUInt());
+    setRemotePacketSize(buf.getInt());
+  }
+
+  public void setInputStream(InputStream in){
+    io.setInputStream(in, false);
+  }
+  public void setInputStream(InputStream in, boolean dontclose){
+    io.setInputStream(in, dontclose);
+  }
+  public void setOutputStream(OutputStream out){
+    io.setOutputStream(out, false);
+  }
+  public void setOutputStream(OutputStream out, boolean dontclose){
+    io.setOutputStream(out, dontclose);
+  }
+  public void setExtOutputStream(OutputStream out){
+    io.setExtOutputStream(out, false);
+  }
+  public void setExtOutputStream(OutputStream out, boolean dontclose){
+    io.setExtOutputStream(out, dontclose);
+  }
+  public InputStream getInputStream() throws IOException {
+    PipedInputStream in=
+      new MyPipedInputStream(
+                             32*1024  // this value should be customizable.
+                             );
+    io.setOutputStream(new PassiveOutputStream(in), false);
+    return in;
+  }
+  public InputStream getExtInputStream() throws IOException {
+    PipedInputStream in=
+      new MyPipedInputStream(
+                             32*1024  // this value should be customizable.
+                             );
+    io.setExtOutputStream(new PassiveOutputStream(in), false);
+    return in;
+  }
+  public OutputStream getOutputStream() throws IOException {
+    /*
+    PipedOutputStream out=new PipedOutputStream();
+    io.setInputStream(new PassiveInputStream(out
+                                             , 32*1024
+                                             ), false);
+    return out;
+    */
+
+    final Channel channel=this;
+    OutputStream out=new OutputStream(){
+        private int dataLen=0;
+        private Buffer buffer=null;
+        private Packet packet=null;
+        private boolean closed=false;
+        private synchronized void init() throws java.io.IOException{
+          buffer=new Buffer(rmpsize);
+          packet=new Packet(buffer);
+
+          byte[] _buf=buffer.buffer;
+          if(_buf.length-(14+0)-Session.buffer_margin<=0){
+            buffer=null;
+            packet=null;
+            throw new IOException("failed to initialize the channel.");
+          }
+
+        }
+        byte[] b=new byte[1];
+        public void write(int w) throws java.io.IOException{
+          b[0]=(byte)w;
+          write(b, 0, 1);
+        }
+        public void write(byte[] buf, int s, int l) throws java.io.IOException{
+          if(packet==null){
+            init();
+          }
+
+          if(closed){
+            throw new java.io.IOException("Already closed");
+          }
+
+          byte[] _buf=buffer.buffer;
+          int _bufl=_buf.length;
+          while(l>0){
+            int _l=l;
+            if(l>_bufl-(14+dataLen)-Session.buffer_margin){
+              _l=_bufl-(14+dataLen)-Session.buffer_margin;
+            }
+
+            if(_l<=0){
+              flush();
+              continue;
+            }
+
+            System.arraycopy(buf, s, _buf, 14+dataLen, _l);
+            dataLen+=_l;
+            s+=_l;
+            l-=_l;
+          }
+        }
+
+        public void flush() throws java.io.IOException{
+          if(closed){
+            throw new java.io.IOException("Already closed");
+          }
+          if(dataLen==0)
+            return;
+          packet.reset();
+          buffer.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
+          buffer.putInt(recipient);
+          buffer.putInt(dataLen);
+          buffer.skip(dataLen);
+          try{
+            int foo=dataLen;
+            dataLen=0;
+            synchronized(channel){
+              if(!channel.close)
+                getSession().write(packet, channel, foo);
+            }
+          }
+          catch(Exception e){
+            close();
+            throw new java.io.IOException(e.toString());
+          }
+
+        }
+        public void close() throws java.io.IOException{
+          if(packet==null){
+            try{
+              init();
+            }
+            catch(java.io.IOException e){
+              // close should be finished silently.
+              return;
+            }
+          }
+          if(closed){
+            return;
+          }
+          if(dataLen>0){
+            flush();
+          }
+          channel.eof();
+          closed=true;
+        }
+      };
+    return out;
+  }
+
+  class MyPipedInputStream extends PipedInputStream{
+    MyPipedInputStream() throws IOException{ super(); }
+    MyPipedInputStream(int size) throws IOException{
+      super();
+      buffer=new byte[size];
+    }
+    MyPipedInputStream(PipedOutputStream out) throws IOException{ super(out); }
+    MyPipedInputStream(PipedOutputStream out, int size) throws IOException{
+      super(out);
+      buffer=new byte[size];
+    }
+
+    /*
+     * TODO: We should have our own Piped[I/O]Stream implementation.
+     * Before accepting data, JDK's PipedInputStream will check the existence of
+     * reader thread, and if it is not alive, the stream will be closed.
+     * That behavior may cause the problem if multiple threads make access to it.
+     */
+    public synchronized void updateReadSide() throws IOException {
+      if(available() != 0){ // not empty
+        return;
+      }
+      in = 0;
+      out = 0;
+      buffer[in++] = 0;
+      read();
+    }
+  }
+  void setLocalWindowSizeMax(int foo){ this.lwsize_max=foo; }
+  void setLocalWindowSize(int foo){ this.lwsize=foo; }
+  void setLocalPacketSize(int foo){ this.lmpsize=foo; }
+  synchronized void setRemoteWindowSize(long foo){ this.rwsize=foo; }
+  synchronized void addRemoteWindowSize(int foo){ 
+    this.rwsize+=foo; 
+    if(notifyme>0)
+      notifyAll();
+  }
+  void setRemotePacketSize(int foo){ this.rmpsize=foo; }
+
+  public void run(){
+  }
+
+  void write(byte[] foo) throws IOException {
+    write(foo, 0, foo.length);
+  }
+  void write(byte[] foo, int s, int l) throws IOException {
+    try{
+      io.put(foo, s, l);
+    }catch(NullPointerException e){}
+  }
+  void write_ext(byte[] foo, int s, int l) throws IOException {
+    try{
+      io.put_ext(foo, s, l);
+    }catch(NullPointerException e){}
+  }
+
+  void eof_remote(){
+    eof_remote=true;
+    try{
+      io.out_close();
+    }
+    catch(NullPointerException e){}
+  }
+
+  void eof(){
+    if(eof_local)return;
+    eof_local=true;
+
+    try{
+      Buffer buf=new Buffer(100);
+      Packet packet=new Packet(buf);
+      packet.reset();
+      buf.putByte((byte)Session.SSH_MSG_CHANNEL_EOF);
+      buf.putInt(getRecipient());
+      synchronized(this){
+        if(!close)
+          getSession().write(packet);
+      }
+    }
+    catch(Exception e){
+      //System.err.println("Channel.eof");
+      //e.printStackTrace();
+    }
+    /*
+    if(!isConnected()){ disconnect(); }
+    */
+  }
+
+  /*
+  http://www1.ietf.org/internet-drafts/draft-ietf-secsh-connect-24.txt
+
+5.3  Closing a Channel
+  When a party will no longer send more data to a channel, it SHOULD
+   send SSH_MSG_CHANNEL_EOF.
+
+            byte      SSH_MSG_CHANNEL_EOF
+            uint32    recipient_channel
+
+  No explicit response is sent to this message.  However, the
+   application may send EOF to whatever is at the other end of the
+  channel.  Note that the channel remains open after this message, and
+   more data may still be sent in the other direction.  This message
+   does not consume window space and can be sent even if no window space
+   is available.
+
+     When either party wishes to terminate the channel, it sends
+     SSH_MSG_CHANNEL_CLOSE.  Upon receiving this message, a party MUST
+   send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this
+   message for the channel.  The channel is considered closed for a
+     party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and
+   the party may then reuse the channel number.  A party MAY send
+   SSH_MSG_CHANNEL_CLOSE without having sent or received
+   SSH_MSG_CHANNEL_EOF.
+
+            byte      SSH_MSG_CHANNEL_CLOSE
+            uint32    recipient_channel
+
+   This message does not consume window space and can be sent even if no
+   window space is available.
+
+   It is recommended that any data sent before this message is delivered
+     to the actual destination, if possible.
+  */
+
+  void close(){
+    if(close)return;
+    close=true;
+    eof_local=eof_remote=true;
+
+    try{
+      Buffer buf=new Buffer(100);
+      Packet packet=new Packet(buf);
+      packet.reset();
+      buf.putByte((byte)Session.SSH_MSG_CHANNEL_CLOSE);
+      buf.putInt(getRecipient());
+      synchronized(this){
+        getSession().write(packet);
+      }
+    }
+    catch(Exception e){
+      //e.printStackTrace();
+    }
+  }
+  public boolean isClosed(){
+    return close;
+  }
+  static void disconnect(Session session){
+    Channel[] channels=null;
+    int count=0;
+    synchronized(pool){
+      channels=new Channel[pool.size()];
+      for(int i=0; i<pool.size(); i++){
+	try{
+	  Channel c=((Channel)(pool.elementAt(i)));
+	  if(c.session==session){
+	    channels[count++]=c;
+	  }
+	}
+	catch(Exception e){
+	}
+      } 
+    }
+    for(int i=0; i<count; i++){
+      channels[i].disconnect();
+    }
+  }
+
+  public void disconnect(){
+    //System.err.println(this+":disconnect "+io+" "+connected);
+    //Thread.dumpStack();
+
+    try{
+
+      synchronized(this){
+        if(!connected){
+          return;
+        }
+        connected=false;
+      }
+
+      close();
+
+      eof_remote=eof_local=true;
+
+      thread=null;
+
+      try{
+        if(io!=null){
+          io.close();
+        }
+      }
+      catch(Exception e){
+        //e.printStackTrace();
+      }
+      // io=null;
+    }
+    finally{
+      Channel.del(this);
+    }
+  }
+
+  public boolean isConnected(){
+    Session _session=this.session;
+    if(_session!=null){
+      return _session.isConnected() && connected;
+    }
+    return false;
+  }
+
+  public void sendSignal(String signal) throws Exception {
+    RequestSignal request=new RequestSignal();
+    request.setSignal(signal);
+    request.request(getSession(), this);
+  }
+
+//  public String toString(){
+//      return "Channel: type="+new String(type)+",id="+id+",recipient="+recipient+",window_size="+window_size+",packet_size="+packet_size;
+//  }
+
+/*
+  class OutputThread extends Thread{
+    Channel c;
+    OutputThread(Channel c){ this.c=c;}
+    public void run(){c.output_thread();}
+  }
+*/
+
+  class PassiveInputStream extends MyPipedInputStream{
+    PipedOutputStream out;
+    PassiveInputStream(PipedOutputStream out, int size) throws IOException{
+      super(out, size);
+      this.out=out;
+    }
+    PassiveInputStream(PipedOutputStream out) throws IOException{
+      super(out);
+      this.out=out;
+    }
+    public void close() throws IOException{
+      if(out!=null){
+        this.out.close();
+      }
+      out=null;
+    }
+  }
+  class PassiveOutputStream extends PipedOutputStream{
+    PassiveOutputStream(PipedInputStream in) throws IOException{
+      super(in);
+    }
+  }
+
+  void setExitStatus(int status){ exitstatus=status; }
+  public int getExitStatus(){ return exitstatus; }
+
+  void setSession(Session session){
+    this.session=session;
+  }
+
+  public Session getSession() throws JSchException{ 
+    Session _session=session;
+    if(_session==null){
+      throw new JSchException("session is not available");
+    }
+    return _session;
+  }
+  public int getId(){ return id; }
+
+  protected void sendOpenConfirmation() throws Exception{
+    Buffer buf=new Buffer(100);
+    Packet packet=new Packet(buf);
+    packet.reset();
+    buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+    buf.putInt(getRecipient());
+    buf.putInt(id);
+    buf.putInt(lwsize);
+    buf.putInt(lmpsize);
+    getSession().write(packet);
+  }
+
+  protected void sendOpenFailure(int reasoncode){
+    try{
+      Buffer buf=new Buffer(100);
+      Packet packet=new Packet(buf);
+      packet.reset();
+      buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_FAILURE);
+      buf.putInt(getRecipient());
+      buf.putInt(reasoncode);
+      buf.putString(Util.str2byte("open failed"));
+      buf.putString(Util.empty);
+      getSession().write(packet);
+    }
+    catch(Exception e){
+    }
+  }
+
+  protected Packet genChannelOpenPacket(){
+    Buffer buf=new Buffer(100);
+    Packet packet=new Packet(buf);
+    // byte   SSH_MSG_CHANNEL_OPEN(90)
+    // string channel type         //
+    // uint32 sender channel       // 0
+    // uint32 initial window size  // 0x100000(65536)
+    // uint32 maxmum packet size   // 0x4000(16384)
+    packet.reset();
+    buf.putByte((byte)90);
+    buf.putString(this.type);
+    buf.putInt(this.id);
+    buf.putInt(this.lwsize);
+    buf.putInt(this.lmpsize);
+    return packet;
+  }
+
+  protected void sendChannelOpen() throws Exception {
+    Session _session=getSession();
+    if(!_session.isConnected()){
+      throw new JSchException("session is down");
+    }
+
+    Packet packet = genChannelOpenPacket();
+    _session.write(packet);
+
+    int retry=10;
+    long start=System.currentTimeMillis();
+    long timeout=connectTimeout;
+    if(timeout!=0L) retry = 1;
+    synchronized(this){
+      while(this.getRecipient()==-1 &&
+            _session.isConnected() &&
+             retry>0){
+        if(timeout>0L){
+          if((System.currentTimeMillis()-start)>timeout){
+            retry=0;
+            continue;
+          }
+        }
+        try{
+          long t = timeout==0L ? 5000L : timeout;
+          this.notifyme=1;
+          wait(t);
+        }
+        catch(java.lang.InterruptedException e){
+        }
+        finally{
+          this.notifyme=0;
+        }
+        retry--;
+      }
+    }
+    if(!_session.isConnected()){
+      throw new JSchException("session is down");
+    }
+    if(this.getRecipient()==-1){  // timeout
+      throw new JSchException("channel is not opened.");
+    }
+    if(this.open_confirmation==false){  // SSH_MSG_CHANNEL_OPEN_FAILURE
+      throw new JSchException("channel is not opened.");
+    }
+    connected=true;
+  }
+}
diff --git a/java/com/jcraft/jsch/ChannelAgentForwarding.java b/java/com/jcraft/jsch/ChannelAgentForwarding.java
new file mode 100644
index 0000000..9788d90
--- /dev/null
+++ b/java/com/jcraft/jsch/ChannelAgentForwarding.java
@@ -0,0 +1,266 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.net.*;
+import java.util.Vector;
+
+class ChannelAgentForwarding extends Channel{
+
+  static private final int LOCAL_WINDOW_SIZE_MAX=0x20000;
+  static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000;
+
+  private final byte SSH_AGENTC_REQUEST_RSA_IDENTITIES = 1;
+  private final byte SSH_AGENT_RSA_IDENTITIES_ANSWER = 2;
+  private final byte SSH_AGENTC_RSA_CHALLENGE = 3;
+  private final byte SSH_AGENT_RSA_RESPONSE = 4;
+  private final byte SSH_AGENT_FAILURE = 5;
+  private final byte SSH_AGENT_SUCCESS = 6;
+  private final byte SSH_AGENTC_ADD_RSA_IDENTITY	= 7;
+  private final byte SSH_AGENTC_REMOVE_RSA_IDENTITY = 8;
+  private final byte SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES = 9;
+
+  private final byte SSH2_AGENTC_REQUEST_IDENTITIES=11;
+  private final byte SSH2_AGENT_IDENTITIES_ANSWER=12;
+  private final byte SSH2_AGENTC_SIGN_REQUEST=13;
+  private final byte SSH2_AGENT_SIGN_RESPONSE=14;
+  private final byte SSH2_AGENTC_ADD_IDENTITY=17;
+  private final byte SSH2_AGENTC_REMOVE_IDENTITY=18;
+  private final byte SSH2_AGENTC_REMOVE_ALL_IDENTITIES=19;
+  private final byte SSH2_AGENT_FAILURE=30;
+
+  boolean init=true;
+
+  private Buffer rbuf=null;
+  private Buffer wbuf=null;
+  private Packet packet=null;
+  private Buffer mbuf=null;
+
+  ChannelAgentForwarding(){
+    super();
+
+    setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
+    setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
+    setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
+
+    type=Util.str2byte("auth-agent@openssh.com");
+    rbuf=new Buffer();
+    rbuf.reset();
+    //wbuf=new Buffer(rmpsize);
+    //packet=new Packet(wbuf);
+    mbuf=new Buffer();
+    connected=true;
+  }
+
+  public void run(){
+    try{
+      sendOpenConfirmation();
+    }
+    catch(Exception e){
+      close=true;
+      disconnect();
+    }
+  }
+
+  void write(byte[] foo, int s, int l) throws java.io.IOException {
+
+    if(packet==null){
+      wbuf=new Buffer(rmpsize);
+      packet=new Packet(wbuf);
+    }
+
+    rbuf.shift();
+    if(rbuf.buffer.length<rbuf.index+l){
+      byte[] newbuf=new byte[rbuf.s+l];
+      System.arraycopy(rbuf.buffer, 0, newbuf, 0, rbuf.buffer.length);
+      rbuf.buffer=newbuf;
+    }
+
+    rbuf.putByte(foo, s, l);
+
+    int mlen=rbuf.getInt();
+    if(mlen>rbuf.getLength()){
+      rbuf.s-=4;
+      return;
+    }
+
+    int typ=rbuf.getByte();
+
+    Session _session=null;
+    try{
+      _session=getSession();
+    }
+    catch(JSchException e){
+      throw new java.io.IOException(e.toString());
+    }
+
+    IdentityRepository irepo = _session.jsch.getIdentityRepository();
+    UserInfo userinfo=_session.getUserInfo();
+
+    mbuf.reset();
+
+    if(typ==SSH2_AGENTC_REQUEST_IDENTITIES){ 
+      mbuf.putByte(SSH2_AGENT_IDENTITIES_ANSWER);
+      Vector identities = irepo.getIdentities();
+      synchronized(identities){
+        int count=0;
+        for(int i=0; i<identities.size(); i++){
+          Identity identity=(Identity)(identities.elementAt(i));
+          if(identity.getPublicKeyBlob()!=null)
+            count++;
+        }
+        mbuf.putInt(count);
+        for(int i=0; i<identities.size(); i++){
+          Identity identity=(Identity)(identities.elementAt(i));
+          byte[] pubkeyblob=identity.getPublicKeyBlob();
+          if(pubkeyblob==null)
+            continue;
+          mbuf.putString(pubkeyblob);
+          mbuf.putString(Util.empty);
+        }
+      }
+    }
+    else if(typ==SSH_AGENTC_REQUEST_RSA_IDENTITIES) {
+      mbuf.putByte(SSH_AGENT_RSA_IDENTITIES_ANSWER);
+      mbuf.putInt(0);
+    }
+    else if(typ==SSH2_AGENTC_SIGN_REQUEST){
+      byte[] blob=rbuf.getString();
+      byte[] data=rbuf.getString();
+      int flags=rbuf.getInt();
+
+//      if((flags & 1)!=0){ //SSH_AGENT_OLD_SIGNATURE // old OpenSSH 2.0, 2.1
+//        datafellows = SSH_BUG_SIGBLOB;
+//      }
+
+      Vector identities = irepo.getIdentities();
+      Identity identity = null;
+      synchronized(identities){
+        for(int i=0; i<identities.size(); i++){
+          Identity _identity=(Identity)(identities.elementAt(i));
+          if(_identity.getPublicKeyBlob()==null)
+            continue;
+          if(!Util.array_equals(blob, _identity.getPublicKeyBlob())){
+            continue;
+          }
+          if(_identity.isEncrypted()){
+            if(userinfo==null)
+              continue;
+            while(_identity.isEncrypted()){
+              if(!userinfo.promptPassphrase("Passphrase for "+_identity.getName())){
+                break;
+              }
+
+              String _passphrase=userinfo.getPassphrase();
+              if(_passphrase==null){
+                break;
+              }
+
+              byte[] passphrase=Util.str2byte(_passphrase);
+              try{
+                if(_identity.setPassphrase(passphrase)){
+                  break;
+                }
+              }
+              catch(JSchException e){
+                break;
+              }
+            }
+          }
+
+          if(!_identity.isEncrypted()){
+            identity=_identity;
+            break;
+          }
+        }
+      }
+
+      byte[] signature=null;
+
+      if(identity!=null){
+        signature=identity.getSignature(data);
+      }
+
+      if(signature==null){
+        mbuf.putByte(SSH2_AGENT_FAILURE);
+      }
+      else{
+        mbuf.putByte(SSH2_AGENT_SIGN_RESPONSE);
+        mbuf.putString(signature);
+      }
+    }
+    else if(typ==SSH2_AGENTC_REMOVE_IDENTITY){
+      byte[] blob=rbuf.getString();
+      irepo.remove(blob);
+      mbuf.putByte(SSH_AGENT_SUCCESS);
+    }
+    else if(typ==SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES){
+      mbuf.putByte(SSH_AGENT_SUCCESS);
+    }
+    else if(typ==SSH2_AGENTC_REMOVE_ALL_IDENTITIES){
+      irepo.removeAll();
+      mbuf.putByte(SSH_AGENT_SUCCESS);
+    }
+    else if(typ==SSH2_AGENTC_ADD_IDENTITY){
+      int fooo = rbuf.getLength();
+      byte[] tmp = new byte[fooo];
+      rbuf.getByte(tmp);
+      boolean result = irepo.add(tmp);
+      mbuf.putByte(result ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
+    }
+    else {
+      rbuf.skip(rbuf.getLength()-1);
+      mbuf.putByte(SSH_AGENT_FAILURE);
+    }
+
+    byte[] response = new byte[mbuf.getLength()];
+    mbuf.getByte(response);
+    send(response);
+  }
+
+  private void send(byte[] message){
+    packet.reset();
+    wbuf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
+    wbuf.putInt(recipient);
+    wbuf.putInt(4+message.length);
+    wbuf.putString(message);
+
+    try{
+      getSession().write(packet, this, 4+message.length);
+    }
+    catch(Exception e){
+    }
+  }
+
+  void eof_remote(){
+    super.eof_remote();
+    eof();
+  }
+}
diff --git a/java/com/jcraft/jsch/ChannelDirectTCPIP.java b/java/com/jcraft/jsch/ChannelDirectTCPIP.java
new file mode 100644
index 0000000..c8af6a2
--- /dev/null
+++ b/java/com/jcraft/jsch/ChannelDirectTCPIP.java
@@ -0,0 +1,155 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.*;
+
+public class ChannelDirectTCPIP extends Channel{
+
+  static private final int LOCAL_WINDOW_SIZE_MAX=0x20000;
+  static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000;
+  static private final byte[] _type = Util.str2byte("direct-tcpip");
+  String host;
+  int port;
+
+  String originator_IP_address="127.0.0.1";
+  int originator_port=0;
+
+  ChannelDirectTCPIP(){
+    super();
+    type = _type;
+    setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
+    setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
+    setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
+  }
+
+  void init (){
+    io=new IO();
+  }
+
+  public void connect() throws JSchException{
+    try{
+      Session _session=getSession();
+      if(!_session.isConnected()){
+        throw new JSchException("session is down");
+      }
+
+      if(io.in!=null){
+        thread=new Thread(this);
+        thread.setName("DirectTCPIP thread "+_session.getHost());
+        if(_session.daemon_thread){
+          thread.setDaemon(_session.daemon_thread);
+        }
+        thread.start();
+      }
+    }
+    catch(Exception e){
+      io.close();
+      io=null;
+      Channel.del(this);
+      if (e instanceof JSchException) {
+        throw (JSchException) e;
+      }
+    }
+  }
+
+  public void run(){
+
+    try{
+      sendChannelOpen();
+
+      Buffer buf=new Buffer(rmpsize);
+      Packet packet=new Packet(buf);
+      Session _session=getSession();
+      int i=0;
+
+      while(isConnected() &&
+            thread!=null && 
+            io!=null && 
+            io.in!=null){
+        i=io.in.read(buf.buffer, 
+                     14, 
+                     buf.buffer.length-14
+                     -Session.buffer_margin
+                     );
+        if(i<=0){
+          eof();
+          break;
+        }
+        packet.reset();
+        buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
+        buf.putInt(recipient);
+        buf.putInt(i);
+        buf.skip(i);
+        synchronized(this){
+          if(close)
+            break;
+          _session.write(packet, this, i);
+        }
+      }
+    }
+    catch(Exception e){
+    }
+    disconnect();
+  }
+
+  public void setInputStream(InputStream in){
+    io.setInputStream(in);
+  }
+  public void setOutputStream(OutputStream out){
+    io.setOutputStream(out);
+  }
+
+  public void setHost(String host){this.host=host;}
+  public void setPort(int port){this.port=port;}
+  public void setOrgIPAddress(String foo){this.originator_IP_address=foo;}
+  public void setOrgPort(int foo){this.originator_port=foo;}
+
+  protected Packet genChannelOpenPacket(){
+    Buffer buf = new Buffer(150);
+    Packet packet = new Packet(buf);
+    // byte   SSH_MSG_CHANNEL_OPEN(90)
+    // string channel type         //
+    // uint32 sender channel       // 0
+    // uint32 initial window size  // 0x100000(65536)
+    // uint32 maxmum packet size   // 0x4000(16384)
+    packet.reset();
+    buf.putByte((byte)90);
+    buf.putString(this.type);
+    buf.putInt(id);
+    buf.putInt(lwsize);
+    buf.putInt(lmpsize);
+    buf.putString(Util.str2byte(host));
+    buf.putInt(port);
+    buf.putString(Util.str2byte(originator_IP_address));
+    buf.putInt(originator_port);
+    return packet;
+  }
+}
diff --git a/java/com/jcraft/jsch/ChannelExec.java b/java/com/jcraft/jsch/ChannelExec.java
new file mode 100644
index 0000000..45322b6
--- /dev/null
+++ b/java/com/jcraft/jsch/ChannelExec.java
@@ -0,0 +1,83 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.util.*;
+
+public class ChannelExec extends ChannelSession{
+
+  byte[] command=new byte[0];
+
+  public void start() throws JSchException{
+    Session _session=getSession();
+    try{
+      sendRequests();
+      Request request=new RequestExec(command);
+      request.request(_session, this);
+    }
+    catch(Exception e){
+      if(e instanceof JSchException) throw (JSchException)e;
+      if(e instanceof Throwable)
+        throw new JSchException("ChannelExec", (Throwable)e);
+      throw new JSchException("ChannelExec");
+    }
+
+    if(io.in!=null){
+      thread=new Thread(this);
+      thread.setName("Exec thread "+_session.getHost());
+      if(_session.daemon_thread){
+        thread.setDaemon(_session.daemon_thread);
+      }
+      thread.start();
+    }
+  }
+
+  public void setCommand(String command){ 
+    this.command=Util.str2byte(command);
+  }
+  public void setCommand(byte[] command){ 
+    this.command=command;
+  }
+
+  void init() throws JSchException {
+    io.setInputStream(getSession().in);
+    io.setOutputStream(getSession().out);
+  }
+
+  public void setErrStream(java.io.OutputStream out){
+    setExtOutputStream(out);
+  }
+  public void setErrStream(java.io.OutputStream out, boolean dontclose){
+    setExtOutputStream(out, dontclose);
+  }
+  public java.io.InputStream getErrStream() throws java.io.IOException {
+    return getExtInputStream();
+  }
+}
diff --git a/java/com/jcraft/jsch/ChannelForwardedTCPIP.java b/java/com/jcraft/jsch/ChannelForwardedTCPIP.java
new file mode 100644
index 0000000..912f1d8
--- /dev/null
+++ b/java/com/jcraft/jsch/ChannelForwardedTCPIP.java
@@ -0,0 +1,315 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.net.*;
+import java.io.*;
+
+public class ChannelForwardedTCPIP extends Channel{
+
+  static java.util.Vector pool=new java.util.Vector();
+
+  static private final int LOCAL_WINDOW_SIZE_MAX=0x20000;
+//static private final int LOCAL_WINDOW_SIZE_MAX=0x100000;
+  static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000;
+
+  static private final int TIMEOUT=10*1000;
+
+  SocketFactory factory=null;
+  private Socket socket=null;
+  private ForwardedTCPIPDaemon daemon=null;
+  String target;
+  int lport;
+  int rport;
+
+  ChannelForwardedTCPIP(){
+    super();
+    setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
+    setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
+    setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
+    io=new IO();
+    connected=true;
+  }
+
+  public void run(){
+    try{ 
+      if(lport==-1){
+        Class c=Class.forName(target);
+        daemon=(ForwardedTCPIPDaemon)c.newInstance();
+
+        PipedOutputStream out=new PipedOutputStream();
+        io.setInputStream(new PassiveInputStream(out
+                                                 , 32*1024
+                                                 ), false);
+
+        daemon.setChannel(this, getInputStream(), out);
+        Object[] foo=getPort(getSession(), rport);
+        daemon.setArg((Object[])foo[3]);
+
+        new Thread(daemon).start();
+      }
+      else{
+        socket=(factory==null) ? 
+           Util.createSocket(target, lport, TIMEOUT) : 
+          factory.createSocket(target, lport);
+        socket.setTcpNoDelay(true);
+        io.setInputStream(socket.getInputStream());
+        io.setOutputStream(socket.getOutputStream());
+      }
+      sendOpenConfirmation();
+    }
+    catch(Exception e){
+      sendOpenFailure(SSH_OPEN_ADMINISTRATIVELY_PROHIBITED);
+      close=true;
+      disconnect();
+      return; 
+    }
+
+    thread=Thread.currentThread();
+    Buffer buf=new Buffer(rmpsize);
+    Packet packet=new Packet(buf);
+    int i=0;
+    try{
+      Session _session = getSession();
+      while(thread!=null && 
+            io!=null && 
+            io.in!=null){
+        i=io.in.read(buf.buffer, 
+                     14, 
+                     buf.buffer.length-14
+                     -Session.buffer_margin
+                     );
+        if(i<=0){
+          eof();
+          break;
+        }
+        packet.reset();
+        buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
+        buf.putInt(recipient);
+        buf.putInt(i);
+        buf.skip(i);
+        synchronized(this){
+          if(close)
+            break;
+          _session.write(packet, this, i);
+        }
+      }
+    }
+    catch(Exception e){
+      //System.err.println(e);
+    }
+    //thread=null;
+    //eof();
+    disconnect();
+  }
+
+  void getData(Buffer buf){
+    setRecipient(buf.getInt());
+    setRemoteWindowSize(buf.getUInt());
+    setRemotePacketSize(buf.getInt());
+    byte[] addr=buf.getString();
+    int port=buf.getInt();
+    byte[] orgaddr=buf.getString();
+    int orgport=buf.getInt();
+
+    /*
+    System.err.println("addr: "+Util.byte2str(addr));
+    System.err.println("port: "+port);
+    System.err.println("orgaddr: "+Util.byte2str(orgaddr));
+    System.err.println("orgport: "+orgport);
+    */
+
+    Session _session=null;
+    try{
+      _session=getSession();
+    }
+    catch(JSchException e){
+      // session has been already down.
+    }
+
+    synchronized(pool){
+      for(int i=0; i<pool.size(); i++){
+        Object[] foo=(Object[])(pool.elementAt(i));
+        if(foo[0]!=_session) continue;
+        if(((Integer)foo[1]).intValue()!=port) continue;
+        this.rport=port;
+        this.target=(String)foo[2];
+        if(foo[3]==null || (foo[3] instanceof Object[])){ this.lport=-1; }
+        else{ this.lport=((Integer)foo[3]).intValue(); }
+        if(foo.length>=6){
+          this.factory=((SocketFactory)foo[5]);
+        }
+        break;
+      }
+      if(target==null){
+        //System.err.println("??");
+      }
+    }
+  }
+
+  static Object[] getPort(Session session, int rport){
+    synchronized(pool){
+      for(int i=0; i<pool.size(); i++){
+        Object[] bar=(Object[])(pool.elementAt(i));
+        if(bar[0]!=session) continue;
+        if(((Integer)bar[1]).intValue()!=rport) continue;
+        return bar;
+      }
+      return null;
+    }
+  }
+
+  static String[] getPortForwarding(Session session){
+    java.util.Vector foo=new java.util.Vector();
+    synchronized(pool){
+      for(int i=0; i<pool.size(); i++){
+        Object[] bar=(Object[])(pool.elementAt(i));
+        if(bar[0]!=session) continue;
+        if(bar[3]==null){ foo.addElement(bar[1]+":"+bar[2]+":"); }
+        else{ foo.addElement(bar[1]+":"+bar[2]+":"+bar[3]); }
+      }
+    }
+    String[] bar=new String[foo.size()];
+    for(int i=0; i<foo.size(); i++){
+      bar[i]=(String)(foo.elementAt(i));
+    }
+    return bar;
+  }
+
+  static String normalize(String address){
+    if(address==null){ return "localhost"; }
+    else if(address.length()==0 || address.equals("*")){ return ""; }
+    else{ return address; }
+  }
+
+  static void addPort(Session session, String _address_to_bind, int port, String target, int lport, SocketFactory factory) throws JSchException{
+    String address_to_bind=normalize(_address_to_bind);
+    synchronized(pool){
+      if(getPort(session, port)!=null){
+        throw new JSchException("PortForwardingR: remote port "+port+" is already registered.");
+      }
+      Object[] foo=new Object[6];
+      foo[0]=session; foo[1]=new Integer(port);
+      foo[2]=target; foo[3]=new Integer(lport);
+      foo[4]=address_to_bind;
+      foo[5]=factory;
+      pool.addElement(foo);
+    }
+  }
+  static void addPort(Session session, String _address_to_bind, int port, String daemon, Object[] arg) throws JSchException{
+    String address_to_bind=normalize(_address_to_bind);
+    synchronized(pool){
+      if(getPort(session, port)!=null){
+        throw new JSchException("PortForwardingR: remote port "+port+" is already registered.");
+      }
+      Object[] foo=new Object[5];
+      foo[0]=session; foo[1]=new Integer(port);
+      foo[2]=daemon; foo[3]=arg;
+      foo[4]=address_to_bind; 
+      pool.addElement(foo);
+    }
+  }
+  static void delPort(ChannelForwardedTCPIP c){
+    Session _session=null;
+    try{
+      _session=c.getSession();
+    }
+    catch(JSchException e){
+      // session has been already down.
+    }
+    if(_session!=null)
+      delPort(_session, c.rport);
+  }
+  static void delPort(Session session, int rport){
+    delPort(session, null, rport);
+  }
+  static void delPort(Session session, String address_to_bind, int rport){
+    synchronized(pool){
+      Object[] foo=null;
+      for(int i=0; i<pool.size(); i++){
+        Object[] bar=(Object[])(pool.elementAt(i));
+        if(bar[0]!=session) continue;
+        if(((Integer)bar[1]).intValue()!=rport) continue;
+        foo=bar;
+        break;
+      }
+      if(foo==null)return;
+      pool.removeElement(foo);
+      if(address_to_bind==null){
+        address_to_bind=(String)foo[4];
+      }	
+      if(address_to_bind==null){
+        address_to_bind="0.0.0.0";
+      }
+    }
+
+    Buffer buf=new Buffer(100); // ??
+    Packet packet=new Packet(buf);
+
+    try{
+      // byte SSH_MSG_GLOBAL_REQUEST 80
+      // string "cancel-tcpip-forward"
+      // boolean want_reply
+      // string  address_to_bind (e.g. "127.0.0.1")
+      // uint32  port number to bind
+      packet.reset();
+      buf.putByte((byte) 80/*SSH_MSG_GLOBAL_REQUEST*/);
+      buf.putString(Util.str2byte("cancel-tcpip-forward"));
+      buf.putByte((byte)0);
+      buf.putString(Util.str2byte(address_to_bind));
+      buf.putInt(rport);
+      session.write(packet);
+    }
+    catch(Exception e){
+//    throw new JSchException(e.toString());
+    }
+  }
+  static void delPort(Session session){
+    int[] rport=null;
+    int count=0;
+    synchronized(pool){
+      rport=new int[pool.size()];
+      for(int i=0; i<pool.size(); i++){
+        Object[] bar=(Object[])(pool.elementAt(i));
+        if(bar[0]==session) {
+          rport[count++]=((Integer)bar[1]).intValue();
+        }
+      }
+    }
+    for(int i=0; i<count; i++){
+      delPort(session, rport[i]);
+    }
+  }
+
+  public int getRemotePort(){return rport;}
+  void setSocketFactory(SocketFactory factory){
+    this.factory=factory;
+  }
+}
diff --git a/java/com/jcraft/jsch/ChannelSession.java b/java/com/jcraft/jsch/ChannelSession.java
new file mode 100644
index 0000000..ffe217a
--- /dev/null
+++ b/java/com/jcraft/jsch/ChannelSession.java
@@ -0,0 +1,276 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.util.*;
+
+class ChannelSession extends Channel{
+  private static byte[] _session=Util.str2byte("session");
+
+  protected boolean agent_forwarding=false;
+  protected boolean xforwading=false;
+  protected Hashtable env=null;
+
+  protected boolean pty=false;
+
+  protected String ttype="vt100";
+  protected int tcol=80;
+  protected int trow=24;
+  protected int twp=640;
+  protected int thp=480;
+  protected byte[] terminal_mode=null;
+
+  ChannelSession(){
+    super();
+    type=_session;
+    io=new IO();
+  }
+
+  /**
+   * Enable the agent forwarding.
+   *
+   * @param enable
+   */
+  public void setAgentForwarding(boolean enable){ 
+    agent_forwarding=enable;
+  }
+
+  /**
+   * Enable the X11 forwarding.
+   *
+   * @param enable
+   * @see RFC4254 6.3.1. Requesting X11 Forwarding
+   */
+  public void setXForwarding(boolean enable){
+    xforwading=enable; 
+  }
+
+  /**
+   * @deprecated Use {@link #setEnv(String, String)} or {@link #setEnv(byte[], byte[])} instead.
+   * @see #setEnv(String, String)
+   * @see #setEnv(byte[], byte[])
+   */
+  public void setEnv(Hashtable env){ 
+    synchronized(this){
+      this.env=env; 
+    }
+  }
+
+  /**
+   * Set the environment variable. 
+   * If <code>name</code> and <code>value</code> are needed to be passed 
+   * to the remote in your faivorite encoding,use 
+   * {@link #setEnv(byte[], byte[])}.
+   *
+   * @param name A name for environment variable.
+   * @param value A value for environment variable.
+   * @see RFC4254 6.4 Environment Variable Passing
+   */
+  public void setEnv(String name, String value){
+    setEnv(Util.str2byte(name), Util.str2byte(value));
+  }
+
+  /**
+   * Set the environment variable.
+   *
+   * @param name A name of environment variable.
+   * @param value A value of environment variable.
+   * @see #setEnv(String, String)
+   * @see RFC4254 6.4 Environment Variable Passing
+   */
+  public void setEnv(byte[] name, byte[] value){
+    synchronized(this){
+      getEnv().put(name, value);
+    }
+  }
+
+  private Hashtable getEnv(){
+    if(env==null)
+      env=new Hashtable();
+    return env;
+  }
+
+  /**
+   * Allocate a Pseudo-Terminal.
+   *
+   * @param enable
+   * @see RFC4254 6.2. Requesting a Pseudo-Terminal
+   */
+  public void setPty(boolean enable){ 
+    pty=enable; 
+  }
+
+  /**
+   * Set the terminal mode.
+   * 
+   * @param terminal_mode
+   */
+  public void setTerminalMode(byte[] terminal_mode){
+    this.terminal_mode=terminal_mode;
+  }
+
+  /**
+   * Change the window dimension interactively.
+   * 
+   * @param col terminal width, columns
+   * @param row terminal height, rows
+   * @param wp terminal width, pixels
+   * @param hp terminal height, pixels
+   * @see RFC4254 6.7. Window Dimension Change Message
+   */
+  public void setPtySize(int col, int row, int wp, int hp){
+    setPtyType(this.ttype, col, row, wp, hp);
+    if(!pty || !isConnected()){
+      return;
+    }
+    try{
+      RequestWindowChange request=new RequestWindowChange();
+      request.setSize(col, row, wp, hp);
+      request.request(getSession(), this);
+    }
+    catch(Exception e){
+      //System.err.println("ChannelSessio.setPtySize: "+e);
+    }
+  }
+
+  /**
+   * Set the terminal type.
+   * This method is not effective after Channel#connect().
+   *
+   * @param ttype terminal type(for example, "vt100")
+   * @see #setPtyType(String, int, int, int, int)
+   */
+  public void setPtyType(String ttype){
+    setPtyType(ttype, 80, 24, 640, 480);
+  }
+
+  /**
+   * Set the terminal type.
+   * This method is not effective after Channel#connect().
+   *
+   * @param ttype terminal type(for example, "vt100")
+   * @param col terminal width, columns
+   * @param row terminal height, rows
+   * @param wp terminal width, pixels
+   * @param hp terminal height, pixels
+   */
+  public void setPtyType(String ttype, int col, int row, int wp, int hp){
+    this.ttype=ttype;
+    this.tcol=col;
+    this.trow=row;
+    this.twp=wp;
+    this.thp=hp;
+  }
+
+  protected void sendRequests() throws Exception{
+    Session _session=getSession();
+    Request request;
+    if(agent_forwarding){
+      request=new RequestAgentForwarding();
+      request.request(_session, this);
+    }
+
+    if(xforwading){
+      request=new RequestX11();
+      request.request(_session, this);
+    }
+
+    if(pty){
+      request=new RequestPtyReq();
+      ((RequestPtyReq)request).setTType(ttype);
+      ((RequestPtyReq)request).setTSize(tcol, trow, twp, thp);
+      if(terminal_mode!=null){
+        ((RequestPtyReq)request).setTerminalMode(terminal_mode);
+      }
+      request.request(_session, this);
+    }
+
+    if(env!=null){
+      for(Enumeration _env=env.keys(); _env.hasMoreElements();){
+        Object name=_env.nextElement();
+        Object value=env.get(name);
+        request=new RequestEnv();
+        ((RequestEnv)request).setEnv(toByteArray(name), 
+                                     toByteArray(value));
+        request.request(_session, this);
+      }
+    }
+  }
+
+  private byte[] toByteArray(Object o){
+    if(o instanceof String){
+      return Util.str2byte((String)o);
+    }
+    return (byte[])o;
+  }
+
+  public void run(){
+    //System.err.println(this+":run >");
+
+    Buffer buf=new Buffer(rmpsize);
+    Packet packet=new Packet(buf);
+    int i=-1;
+    try{
+      while(isConnected() &&
+	    thread!=null && 
+            io!=null && 
+            io.in!=null){
+        i=io.in.read(buf.buffer, 
+                     14,    
+                     buf.buffer.length-14
+                     -Session.buffer_margin
+		     );
+	if(i==0)continue;
+	if(i==-1){
+	  eof();
+	  break;
+	}
+	if(close)break;
+        //System.out.println("write: "+i);
+        packet.reset();
+        buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
+        buf.putInt(recipient);
+        buf.putInt(i);
+        buf.skip(i);
+	getSession().write(packet, this, i);
+      }
+    }
+    catch(Exception e){
+      //System.err.println("# ChannelExec.run");
+      //e.printStackTrace();
+    }
+    Thread _thread=thread; 
+    if(_thread!=null){
+      synchronized(_thread){ _thread.notifyAll(); }
+    }
+    thread=null;
+    //System.err.println(this+":run <");
+  }
+}
diff --git a/java/com/jcraft/jsch/ChannelSftp.java b/java/com/jcraft/jsch/ChannelSftp.java
new file mode 100644
index 0000000..66aec55
--- /dev/null
+++ b/java/com/jcraft/jsch/ChannelSftp.java
@@ -0,0 +1,2651 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.*;
+
+import java.util.Vector;
+
+public class ChannelSftp extends ChannelSession{
+
+  static private final int LOCAL_MAXIMUM_PACKET_SIZE=32*1024;
+  static private final int LOCAL_WINDOW_SIZE_MAX=(64*LOCAL_MAXIMUM_PACKET_SIZE);
+
+  private static final byte SSH_FXP_INIT=               1;
+  private static final byte SSH_FXP_VERSION=            2;
+  private static final byte SSH_FXP_OPEN=               3;
+  private static final byte SSH_FXP_CLOSE=              4;
+  private static final byte SSH_FXP_READ=               5;
+  private static final byte SSH_FXP_WRITE=              6;
+  private static final byte SSH_FXP_LSTAT=              7;
+  private static final byte SSH_FXP_FSTAT=              8;
+  private static final byte SSH_FXP_SETSTAT=            9;
+  private static final byte SSH_FXP_FSETSTAT=          10;
+  private static final byte SSH_FXP_OPENDIR=           11;
+  private static final byte SSH_FXP_READDIR=           12;
+  private static final byte SSH_FXP_REMOVE=            13;
+  private static final byte SSH_FXP_MKDIR=             14;
+  private static final byte SSH_FXP_RMDIR=             15;
+  private static final byte SSH_FXP_REALPATH=          16;
+  private static final byte SSH_FXP_STAT=              17;
+  private static final byte SSH_FXP_RENAME=            18;
+  private static final byte SSH_FXP_READLINK=          19;
+  private static final byte SSH_FXP_SYMLINK=           20;
+  private static final byte SSH_FXP_STATUS=           101;
+  private static final byte SSH_FXP_HANDLE=           102;
+  private static final byte SSH_FXP_DATA=             103;
+  private static final byte SSH_FXP_NAME=             104;
+  private static final byte SSH_FXP_ATTRS=            105;
+  private static final byte SSH_FXP_EXTENDED=         (byte)200;
+  private static final byte SSH_FXP_EXTENDED_REPLY=   (byte)201;
+
+  // pflags
+  private static final int SSH_FXF_READ=           0x00000001;
+  private static final int SSH_FXF_WRITE=          0x00000002;
+  private static final int SSH_FXF_APPEND=         0x00000004;
+  private static final int SSH_FXF_CREAT=          0x00000008;
+  private static final int SSH_FXF_TRUNC=          0x00000010;
+  private static final int SSH_FXF_EXCL=           0x00000020;
+
+  private static final int SSH_FILEXFER_ATTR_SIZE=         0x00000001;
+  private static final int SSH_FILEXFER_ATTR_UIDGID=       0x00000002;
+  private static final int SSH_FILEXFER_ATTR_PERMISSIONS=  0x00000004;
+  private static final int SSH_FILEXFER_ATTR_ACMODTIME=    0x00000008;
+  private static final int SSH_FILEXFER_ATTR_EXTENDED=     0x80000000;
+
+  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;
+/*
+   SSH_FX_OK
+      Indicates successful completion of the operation.
+   SSH_FX_EOF
+     indicates end-of-file condition; for SSH_FX_READ it means that no
+       more data is available in the file, and for SSH_FX_READDIR it
+      indicates that no more files are contained in the directory.
+   SSH_FX_NO_SUCH_FILE
+      is returned when a reference is made to a file which should exist
+      but doesn't.
+   SSH_FX_PERMISSION_DENIED
+      is returned when the authenticated user does not have sufficient
+      permissions to perform the operation.
+   SSH_FX_FAILURE
+      is a generic catch-all error message; it should be returned if an
+      error occurs for which there is no more specific error code
+      defined.
+   SSH_FX_BAD_MESSAGE
+      may be returned if a badly formatted packet or protocol
+      incompatibility is detected.
+   SSH_FX_NO_CONNECTION
+      is a pseudo-error which indicates that the client has no
+      connection to the server (it can only be generated locally by the
+      client, and MUST NOT be returned by servers).
+   SSH_FX_CONNECTION_LOST
+      is a pseudo-error which indicates that the connection to the
+      server has been lost (it can only be generated locally by the
+      client, and MUST NOT be returned by servers).
+   SSH_FX_OP_UNSUPPORTED
+      indicates that an attempt was made to perform an operation which
+      is not supported for the server (it may be generated locally by
+      the client if e.g.  the version number exchange indicates that a
+      required feature is not supported by the server, or it may be
+      returned by the server if the server does not implement an
+      operation).
+*/
+  private static final int MAX_MSG_LENGTH = 256* 1024;
+
+  public static final int OVERWRITE=0;
+  public static final int RESUME=1;
+  public static final int APPEND=2;
+
+  private boolean interactive=false;
+  private int seq=1;
+  private int[] ackid=new int[1];
+
+  private Buffer buf;
+  private Packet packet;
+
+  // The followings will be used in file uploading.
+  private Buffer obuf;
+  private Packet opacket;
+
+  private int client_version=3;
+  private int server_version=3;
+  private String version=String.valueOf(client_version);
+
+  private java.util.Hashtable extensions=null;
+  private InputStream io_in=null;
+
+/*
+10. Changes from previous protocol versions
+  The SSH File Transfer Protocol has changed over time, before it's
+   standardization.  The following is a description of the incompatible
+   changes between different versions.
+10.1 Changes between versions 3 and 2
+   o  The SSH_FXP_READLINK and SSH_FXP_SYMLINK messages were added.
+   o  The SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY messages were added.
+   o  The SSH_FXP_STATUS message was changed to include fields `error
+      message' and `language tag'.
+10.2 Changes between versions 2 and 1
+   o  The SSH_FXP_RENAME message was added.
+10.3 Changes between versions 1 and 0
+   o  Implementation changes, no actual protocol changes.
+*/
+
+  private static final String file_separator=java.io.File.separator;
+  private static final char file_separatorc=java.io.File.separatorChar;
+  private static boolean fs_is_bs=(byte)java.io.File.separatorChar == '\\';
+
+  private String cwd;
+  private String home;
+  private String lcwd;
+
+  private static final String UTF8="UTF-8";
+  private String fEncoding=UTF8;
+  private boolean fEncoding_is_utf8=true;
+
+  private RequestQueue rq = new RequestQueue(10);
+  public void setBulkRequests(int bulk_requests) throws JSchException {
+    if(bulk_requests>0) 
+      rq = new RequestQueue(bulk_requests);
+    else 
+      throw new JSchException("setBulkRequests: "+ 
+                              bulk_requests+" must be greater than 0.");
+  }
+  public int getBulkRequests(){
+    return rq.size();
+  }
+
+  ChannelSftp(){
+    super();
+    setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
+    setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
+    setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
+  }
+
+  void init(){
+  }
+
+  public void start() throws JSchException{
+    try{
+
+      PipedOutputStream pos=new PipedOutputStream();
+      io.setOutputStream(pos);
+      PipedInputStream pis=new MyPipedInputStream(pos, rmpsize);
+      io.setInputStream(pis);
+
+      io_in=io.in;
+
+      if(io_in==null){
+        throw new JSchException("channel is down");
+      }
+
+      Request request=new RequestSftp();
+      request.request(getSession(), this);
+
+      /*
+      System.err.println("lmpsize: "+lmpsize);
+      System.err.println("lwsize: "+lwsize);
+      System.err.println("rmpsize: "+rmpsize);
+      System.err.println("rwsize: "+rwsize);
+      */
+
+      buf=new Buffer(lmpsize);
+      packet=new Packet(buf);
+
+      obuf=new Buffer(rmpsize);
+      opacket=new Packet(obuf);
+
+      int i=0;
+      int length;
+      int type;
+      byte[] str;
+
+      // send SSH_FXP_INIT
+      sendINIT();
+
+      // receive SSH_FXP_VERSION
+      Header header=new Header();
+      header=header(buf, header);
+      length=header.length;
+      if(length > MAX_MSG_LENGTH){
+        throw new SftpException(SSH_FX_FAILURE, 
+                                "Received message is too long: " + length);
+      }
+      type=header.type;             // 2 -> SSH_FXP_VERSION
+      server_version=header.rid;
+      //System.err.println("SFTP protocol server-version="+server_version);
+      if(length>0){
+        extensions=new java.util.Hashtable();
+        // extension data
+        fill(buf, length);
+        byte[] extension_name=null;
+        byte[] extension_data=null;
+        while(length>0){
+          extension_name=buf.getString();
+          length-=(4+extension_name.length);
+          extension_data=buf.getString();
+          length-=(4+extension_data.length);
+          extensions.put(Util.byte2str(extension_name),
+                         Util.byte2str(extension_data));
+        }
+      }
+
+      lcwd=new File(".").getCanonicalPath();
+    }
+    catch(Exception e){
+      //System.err.println(e);
+      if(e instanceof JSchException) throw (JSchException)e;
+      if(e instanceof Throwable)
+        throw new JSchException(e.toString(), (Throwable)e);
+      throw new JSchException(e.toString());
+    }
+  }
+
+  public void quit(){ disconnect();}
+  public void exit(){ disconnect();}
+  public void lcd(String path) throws SftpException{
+    path=localAbsolutePath(path);
+    if((new File(path)).isDirectory()){
+      try{
+	path=(new File(path)).getCanonicalPath();
+      }
+      catch(Exception e){}
+      lcwd=path;
+      return;
+    }
+    throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such directory");
+  }
+
+  public void cd(String path) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+      path=isUnique(path);
+
+      byte[] str=_realpath(path);
+      SftpATTRS attr=_stat(str);
+
+      if((attr.getFlags()&SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS)==0){
+        throw new SftpException(SSH_FX_FAILURE, 
+                                "Can't change directory: "+path);
+      }
+      if(!attr.isDir()){
+        throw new SftpException(SSH_FX_FAILURE, 
+                                "Can't change directory: "+path);
+      }
+
+      setCwd(Util.byte2str(str, fEncoding));
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  public void put(String src, String dst) throws SftpException{
+    put(src, dst, null, OVERWRITE);
+  }
+  public void put(String src, String dst, int mode) throws SftpException{
+    put(src, dst, null, mode);
+  }
+  public void put(String src, String dst, 
+		  SftpProgressMonitor monitor) throws SftpException{
+    put(src, dst, monitor, OVERWRITE);
+  }
+  public void put(String src, String dst, 
+		  SftpProgressMonitor monitor, int mode) throws SftpException{
+
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      src=localAbsolutePath(src);
+      dst=remoteAbsolutePath(dst);
+
+      Vector v=glob_remote(dst);
+      int vsize=v.size();
+      if(vsize!=1){
+        if(vsize==0){
+          if(isPattern(dst))
+            throw new SftpException(SSH_FX_FAILURE, dst);
+          else
+            dst=Util.unquote(dst);
+        }
+        throw new SftpException(SSH_FX_FAILURE, v.toString());
+      }
+      else{
+        dst=(String)(v.elementAt(0));
+      }
+
+      boolean isRemoteDir=isRemoteDir(dst);
+
+      v=glob_local(src);
+      vsize=v.size();
+
+      StringBuffer dstsb=null;
+      if(isRemoteDir){
+        if(!dst.endsWith("/")){
+	    dst+="/";
+        }
+        dstsb=new StringBuffer(dst);
+      }
+      else if(vsize>1){
+        throw new SftpException(SSH_FX_FAILURE, 
+                                "Copying multiple files, but the destination is missing or a file.");
+      }
+
+      for(int j=0; j<vsize; j++){
+	String _src=(String)(v.elementAt(j));
+	String _dst=null;
+	if(isRemoteDir){
+	  int i=_src.lastIndexOf(file_separatorc);
+          if(fs_is_bs){
+            int ii=_src.lastIndexOf('/');
+            if(ii!=-1 && ii>i)
+              i=ii; 
+          }
+	  if(i==-1) dstsb.append(_src);
+	  else dstsb.append(_src.substring(i + 1));
+          _dst=dstsb.toString();
+          dstsb.delete(dst.length(), _dst.length());
+	}
+        else{
+          _dst=dst;
+        }
+        //System.err.println("_dst "+_dst);
+
+	long size_of_dst=0;
+	if(mode==RESUME){
+	  try{
+	    SftpATTRS attr=_stat(_dst);
+	    size_of_dst=attr.getSize();
+	  }
+	  catch(Exception eee){
+	    //System.err.println(eee);
+	  }
+	  long size_of_src=new File(_src).length();
+	  if(size_of_src<size_of_dst){
+	    throw new SftpException(SSH_FX_FAILURE, 
+                                    "failed to resume for "+_dst);
+	  }
+	  if(size_of_src==size_of_dst){
+	    return;
+	  }
+	}
+
+        if(monitor!=null){
+ 	  monitor.init(SftpProgressMonitor.PUT, _src, _dst,
+		       (new File(_src)).length());
+	  if(mode==RESUME){
+	    monitor.count(size_of_dst);
+	  }
+        }
+	FileInputStream fis=null;
+	try{
+	  fis=new FileInputStream(_src);
+	  _put(fis, _dst, monitor, mode);
+	}
+	finally{
+	  if(fis!=null) {
+	    fis.close();
+	  }
+	}
+      }
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, e.toString());
+    }
+  }
+  public void put(InputStream src, String dst) throws SftpException{
+    put(src, dst, null, OVERWRITE);
+  }
+  public void put(InputStream src, String dst, int mode) throws SftpException{
+    put(src, dst, null, mode);
+  }
+  public void put(InputStream src, String dst, 
+		  SftpProgressMonitor monitor) throws SftpException{
+    put(src, dst, monitor, OVERWRITE);
+  }
+  public void put(InputStream src, String dst, 
+		  SftpProgressMonitor monitor, int mode) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      dst=remoteAbsolutePath(dst);
+
+      Vector v=glob_remote(dst);
+      int vsize=v.size();
+      if(vsize!=1){
+        if(vsize==0){
+          if(isPattern(dst))
+            throw new SftpException(SSH_FX_FAILURE, dst);
+          else
+            dst=Util.unquote(dst);
+        }
+        throw new SftpException(SSH_FX_FAILURE, v.toString());
+      }
+      else{
+        dst=(String)(v.elementAt(0));
+      }
+
+      if(isRemoteDir(dst)){
+        throw new SftpException(SSH_FX_FAILURE, dst+" is a directory");
+      }
+
+      if(monitor!=null){
+        monitor.init(SftpProgressMonitor.PUT, 
+                     "-", dst,
+                     SftpProgressMonitor.UNKNOWN_SIZE);
+      }
+
+      _put(src, dst, monitor, mode);
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, e.toString());
+    }
+  }
+
+  public void _put(InputStream src, String dst, 
+                   SftpProgressMonitor monitor, int mode) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      byte[] dstb=Util.str2byte(dst, fEncoding);
+      long skip=0;
+      if(mode==RESUME || mode==APPEND){
+	try{
+	  SftpATTRS attr=_stat(dstb);
+	  skip=attr.getSize();
+	}
+	catch(Exception eee){
+	  //System.err.println(eee);
+	}
+      }
+      if(mode==RESUME && skip>0){
+	long skipped=src.skip(skip);
+	if(skipped<skip){
+	  throw new SftpException(SSH_FX_FAILURE, "failed to resume for "+dst);
+	}
+      }
+
+      if(mode==OVERWRITE){ sendOPENW(dstb); }
+      else{ sendOPENA(dstb); }
+
+      Header header=new Header();
+      header=header(buf, header);
+      int length=header.length;
+      int type=header.type;
+
+      fill(buf, length);
+
+      if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
+	throw new SftpException(SSH_FX_FAILURE, "invalid type="+type);
+      }
+      if(type==SSH_FXP_STATUS){
+        int i=buf.getInt();
+        throwStatusError(buf, i);
+      }
+      byte[] handle=buf.getString();         // handle
+      byte[] data=null;
+
+      boolean dontcopy=true;
+
+      if(!dontcopy){  // This case will not work anymore.
+        data=new byte[obuf.buffer.length
+                      -(5+13+21+handle.length+Session.buffer_margin
+                        )
+        ];
+      }
+
+      long offset=0;
+      if(mode==RESUME || mode==APPEND){
+	offset+=skip;
+      }
+
+      int startid=seq;
+      int ackcount=0;
+      int _s=0;
+      int _datalen=0;
+
+      if(!dontcopy){  // This case will not work anymore.
+        _datalen=data.length;
+      }
+      else{
+        data=obuf.buffer;
+        _s=5+13+21+handle.length;
+        _datalen=obuf.buffer.length-_s-Session.buffer_margin;
+      }
+
+      int bulk_requests = rq.size();
+
+      while(true){
+        int nread=0;
+        int count=0;
+        int s=_s;
+        int datalen=_datalen;
+
+        do{
+          nread=src.read(data, s, datalen);
+          if(nread>0){
+            s+=nread;
+            datalen-=nread;
+            count+=nread;
+          }
+        }
+        while(datalen>0 && nread>0); 
+        if(count<=0)break;
+
+        int foo=count;
+        while(foo>0){
+          if((seq-1)==startid ||
+             ((seq-startid)-ackcount)>=bulk_requests){
+            while(((seq-startid)-ackcount)>=bulk_requests){
+              if(this.rwsize>=foo) break;
+              if(checkStatus(ackid, header)){
+                int _ackid = ackid[0];
+                if(startid>_ackid || _ackid>seq-1){
+                  if(_ackid==seq){
+                    System.err.println("ack error: startid="+startid+" seq="+seq+" _ackid="+_ackid);
+                  } 
+                  else{
+                    throw new SftpException(SSH_FX_FAILURE, "ack error: startid="+startid+" seq="+seq+" _ackid="+_ackid);
+                  }
+                }
+                ackcount++;
+              }
+              else{
+                break;
+              }
+            }
+          }
+          foo-=sendWRITE(handle, offset, data, 0, foo);
+        }
+        offset+=count;
+	if(monitor!=null && !monitor.count(count)){
+          break;
+	}
+      }
+      int _ackcount=seq-startid;
+      while(_ackcount>ackcount){
+        if(!checkStatus(null, header)){
+          break;
+        }
+        ackcount++;
+      }
+      if(monitor!=null)monitor.end();
+      _sendCLOSE(handle, header);
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, e.toString());
+    }
+  }
+
+  public OutputStream put(String dst) throws SftpException{
+    return put(dst, (SftpProgressMonitor)null, OVERWRITE);
+  }
+  public OutputStream put(String dst, final int mode) throws SftpException{
+    return put(dst, (SftpProgressMonitor)null, mode);
+  }
+  public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode) throws SftpException{
+    return put(dst, monitor, mode, 0);
+  }
+  public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode, long offset) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      dst=remoteAbsolutePath(dst);
+      dst=isUnique(dst);
+
+      if(isRemoteDir(dst)){
+	throw new SftpException(SSH_FX_FAILURE, dst+" is a directory");
+      }
+
+      byte[] dstb=Util.str2byte(dst, fEncoding);
+
+      long skip=0;
+      if(mode==RESUME || mode==APPEND){
+	try{
+	  SftpATTRS attr=_stat(dstb);
+	  skip=attr.getSize();
+	}
+	catch(Exception eee){
+	  //System.err.println(eee);
+	}
+      }
+
+      if(mode==OVERWRITE){ sendOPENW(dstb); }
+      else{ sendOPENA(dstb); }
+
+      Header header=new Header();
+      header=header(buf, header);
+      int length=header.length;
+      int type=header.type;
+
+      fill(buf, length);
+
+      if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
+	throw new SftpException(SSH_FX_FAILURE, "");
+      }
+      if(type==SSH_FXP_STATUS){
+        int i=buf.getInt();
+        throwStatusError(buf, i);
+      }
+      final byte[] handle=buf.getString();         // handle
+
+      if(mode==RESUME || mode==APPEND){
+	offset+=skip;
+      }
+
+      final long[] _offset=new long[1];
+      _offset[0]=offset;
+      OutputStream out = new OutputStream(){
+        private boolean init=true;
+        private boolean isClosed=false;
+        private int[] ackid=new int[1];
+        private int startid=0;
+        private int _ackid=0;
+        private int ackcount=0;
+        private int writecount=0;
+        private Header header=new Header();          
+
+        public void write(byte[] d) throws java.io.IOException{
+          write(d, 0, d.length);
+        }
+
+        public void write(byte[] d, int s, int len) throws java.io.IOException{
+          if(init){
+            startid=seq;
+            _ackid=seq;
+            init=false;
+          }
+
+          if(isClosed){
+            throw new IOException("stream already closed");
+          }
+
+          try{
+            int _len=len;
+            while(_len>0){
+              int sent=sendWRITE(handle, _offset[0], d, s, _len);
+              writecount++;
+              _offset[0]+=sent;
+              s+=sent;
+              _len-=sent;
+              if((seq-1)==startid ||
+                 io_in.available()>=1024){
+                while(io_in.available()>0){
+                  if(checkStatus(ackid, header)){
+                    _ackid=ackid[0];
+                    if(startid>_ackid || _ackid>seq-1){
+                      throw new SftpException(SSH_FX_FAILURE, "");
+                    }
+                    ackcount++;
+                  }
+                  else{
+                    break;
+                  }
+                }
+              }
+            }
+    	    if(monitor!=null && !monitor.count(len)){
+              close();
+              throw new IOException("canceled");
+	    }
+          }
+          catch(IOException e){ throw e; }
+          catch(Exception e){ throw new IOException(e.toString());  }
+        }
+
+        byte[] _data=new byte[1];
+        public void write(int foo) throws java.io.IOException{
+          _data[0]=(byte)foo;
+          write(_data, 0, 1);
+        }
+
+        public void flush() throws java.io.IOException{
+
+          if(isClosed){
+            throw new IOException("stream already closed");
+          }
+
+          if(!init){
+            try{
+              while(writecount>ackcount){
+                if(!checkStatus(null, header)){
+                  break;
+                }
+                ackcount++;
+              }
+            }
+            catch(SftpException e){
+              throw new IOException(e.toString());
+            }
+          }
+        }
+
+        public void close() throws java.io.IOException{
+          if(isClosed){
+            return;
+          }
+          flush();
+          if(monitor!=null)monitor.end();
+          try{ _sendCLOSE(handle, header); }
+          catch(IOException e){ throw e; }
+          catch(Exception e){
+            throw new IOException(e.toString());
+          }
+          isClosed=true;
+        }
+      };
+      return out;
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  public void get(String src, String dst) throws SftpException{
+    get(src, dst, null, OVERWRITE);
+  }
+  public void get(String src, String dst,
+		  SftpProgressMonitor monitor) throws SftpException{
+    get(src, dst, monitor, OVERWRITE);
+  }
+  public void get(String src, String dst,
+		  SftpProgressMonitor monitor, int mode) throws SftpException{
+    // System.out.println("get: "+src+" "+dst);
+
+    boolean _dstExist = false;
+    String _dst=null;
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      src=remoteAbsolutePath(src);
+      dst=localAbsolutePath(dst);
+
+      Vector v=glob_remote(src);
+      int vsize=v.size();
+      if(vsize==0){
+        throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such file");
+      }
+
+      File dstFile=new File(dst);
+      boolean isDstDir=dstFile.isDirectory();
+      StringBuffer dstsb=null;
+      if(isDstDir){
+        if(!dst.endsWith(file_separator)){
+          dst+=file_separator;
+        }
+        dstsb=new StringBuffer(dst);
+      }
+      else if(vsize>1){
+        throw new SftpException(SSH_FX_FAILURE, 
+                                "Copying multiple files, but destination is missing or a file.");
+      }
+
+      for(int j=0; j<vsize; j++){
+	String _src=(String)(v.elementAt(j));
+	SftpATTRS attr=_stat(_src);
+        if(attr.isDir()){
+          throw new SftpException(SSH_FX_FAILURE, 
+                                  "not supported to get directory "+_src);
+        } 
+
+	_dst=null;
+	if(isDstDir){
+	  int i=_src.lastIndexOf('/');
+	  if(i==-1) dstsb.append(_src);
+	  else dstsb.append(_src.substring(i + 1));
+          _dst=dstsb.toString();
+          dstsb.delete(dst.length(), _dst.length());
+	}
+        else{
+          _dst=dst;
+        }
+
+        File _dstFile=new File(_dst);
+	if(mode==RESUME){
+	  long size_of_src=attr.getSize();
+	  long size_of_dst=_dstFile.length();
+	  if(size_of_dst>size_of_src){
+	    throw new SftpException(SSH_FX_FAILURE, 
+                                    "failed to resume for "+_dst);
+	  }
+	  if(size_of_dst==size_of_src){
+	    return;
+	  }
+	}
+
+	if(monitor!=null){
+	  monitor.init(SftpProgressMonitor.GET, _src, _dst, attr.getSize());
+	  if(mode==RESUME){
+	    monitor.count(_dstFile.length());
+	  }
+	}
+
+        FileOutputStream fos=null;
+        _dstExist = _dstFile.exists();
+        try{
+          if(mode==OVERWRITE){
+            fos=new FileOutputStream(_dst);
+          }
+          else{
+            fos=new FileOutputStream(_dst, true); // append
+          }
+          // System.err.println("_get: "+_src+", "+_dst);
+          _get(_src, fos, monitor, mode, new File(_dst).length());
+        }
+        finally{
+          if(fos!=null){
+            fos.close();
+          }
+        }
+      }
+    }
+    catch(Exception e){
+      if(!_dstExist && _dst!=null){
+        File _dstFile = new File(_dst);
+        if(_dstFile.exists() && _dstFile.length()==0){
+          _dstFile.delete();
+        }
+      }
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+  public void get(String src, OutputStream dst) throws SftpException{
+    get(src, dst, null, OVERWRITE, 0);
+  }
+  public void get(String src, OutputStream dst,
+		  SftpProgressMonitor monitor) throws SftpException{
+    get(src, dst, monitor, OVERWRITE, 0);
+  }
+  public void get(String src, OutputStream dst,
+		   SftpProgressMonitor monitor, int mode, long skip) throws SftpException{
+//System.err.println("get: "+src+", "+dst);
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      src=remoteAbsolutePath(src);
+      src=isUnique(src);
+
+      if(monitor!=null){
+	SftpATTRS attr=_stat(src);
+        monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize());
+        if(mode==RESUME){
+          monitor.count(skip);
+        }
+      }
+      _get(src, dst, monitor, mode, skip);
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  private void _get(String src, OutputStream dst,
+                    SftpProgressMonitor monitor, int mode, long skip) throws SftpException{
+    //System.err.println("_get: "+src+", "+dst);
+
+    byte[] srcb=Util.str2byte(src, fEncoding);
+    try{
+      sendOPENR(srcb);
+
+      Header header=new Header();
+      header=header(buf, header);
+      int length=header.length;
+      int type=header.type;
+
+      fill(buf, length);
+
+      if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
+	throw new SftpException(SSH_FX_FAILURE, "");
+      }
+
+      if(type==SSH_FXP_STATUS){
+        int i=buf.getInt();
+        throwStatusError(buf, i);
+      }
+
+      byte[] handle=buf.getString();         // filename
+
+      long offset=0;
+      if(mode==RESUME){
+	offset+=skip;
+      }
+
+      int request_max=1;
+      rq.init();
+      long request_offset=offset;
+
+      int request_len = buf.buffer.length-13;
+      if(server_version==0){ request_len=1024; }
+
+      loop:
+      while(true){
+
+        while(rq.count() < request_max){
+          sendREAD(handle, request_offset, request_len, rq);
+          request_offset += request_len;
+        }
+
+        header=header(buf, header);
+        length=header.length;
+        type=header.type;
+
+        RequestQueue.Request rr = rq.get(header.rid);
+
+        if(type==SSH_FXP_STATUS){
+          fill(buf, length);
+          int i=buf.getInt();    
+          if(i==SSH_FX_EOF){
+            break loop;
+          }
+          throwStatusError(buf, i);
+        }
+
+        if(type!=SSH_FXP_DATA){ 
+	  break loop;
+        }
+
+        buf.rewind();
+        fill(buf.buffer, 0, 4); length-=4;
+        int length_of_data = buf.getInt();   // length of data 
+
+        /**
+         Since sftp protocol version 6, "end-of-file" has been defined,
+
+           byte   SSH_FXP_DATA
+           uint32 request-id
+           string data
+           bool   end-of-file [optional]
+
+         but some sftpd server will send such a field in the sftp protocol 3 ;-(
+         */
+        int optional_data = length - length_of_data;
+
+        int foo = length_of_data;
+        while(foo>0){
+          int bar=foo;
+          if(bar>buf.buffer.length){
+            bar=buf.buffer.length;
+          }
+          int data_len = io_in.read(buf.buffer, 0, bar);
+          if(data_len<0){
+            break loop;
+	  }
+          
+          dst.write(buf.buffer, 0, data_len);
+
+          offset+=data_len;
+          foo-=data_len;
+
+          if(monitor!=null){
+            if(!monitor.count(data_len)){
+              skip(foo); 
+              if(optional_data>0){
+                skip(optional_data);
+              }
+              break loop;
+            }
+          }
+
+        }
+	//System.err.println("length: "+length);  // length should be 0
+
+        if(optional_data>0){
+          skip(optional_data);
+        }
+
+        if(length_of_data<rr.length){  //
+          rq.cancel(header, buf);
+          sendREAD(handle, rr.offset+length_of_data, (int)(rr.length-length_of_data), rq);
+          request_offset=rr.offset+rr.length;
+        }
+
+        if(request_max < rq.size()){
+          request_max++;
+        }
+      }
+      dst.flush();
+
+      if(monitor!=null)monitor.end();
+
+      rq.cancel(header, buf);
+
+      _sendCLOSE(handle, header);
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+
+  private class RequestQueue {
+    class Request {
+      int id;
+      long offset;
+      long length;
+    }
+
+    Request[] rrq=null;
+    int head, count;
+    RequestQueue(int size){
+      rrq = new Request[size];
+      for(int i=0; i<rrq.length; i++){
+        rrq[i]=new Request();
+      }
+      init();
+    }
+
+    void init(){
+      head=count=0;
+    }
+
+    void add(int id, long offset, int length){
+      if(count == 0) head = 0;
+      int tail = head + count;
+      if(tail>=rrq.length) tail -= rrq.length;
+      rrq[tail].id=id;
+      rrq[tail].offset=offset;
+      rrq[tail].length=length;
+      count++;
+    }
+
+    Request get(int id){
+      count -= 1;
+      int i=head;
+      head++; 
+      if(head==rrq.length) head=0;
+      if(rrq[i].id!=id){
+        System.err.println("The request is not in order.");
+      }
+      rrq[i].id=0;
+      return rrq[i];
+    }
+
+    int count() {
+      return count;
+    }
+
+    int size() {
+      return rrq.length;
+    } 
+
+    void cancel(Header header, Buffer buf) throws IOException {
+      int _count = count;
+      for(int i=0; i<_count; i++){
+        header=header(buf, header);
+        int length=header.length;
+        get(header.rid);
+        skip(length); 
+      }
+    }
+  }
+
+  public InputStream get(String src) throws SftpException{
+    return get(src, null, 0L);
+  }
+  public InputStream get(String src, SftpProgressMonitor monitor) throws SftpException{
+    return get(src, monitor, 0L);
+  }
+
+  /**
+   * @deprecated  This method will be deleted in the future.
+   */
+  public InputStream get(String src, int mode) throws SftpException{
+    return get(src, null, 0L);
+  }
+  /**
+   * @deprecated  This method will be deleted in the future.
+   */
+  public InputStream get(String src, final SftpProgressMonitor monitor, final int mode) throws SftpException{
+    return get(src, monitor, 0L);
+  }
+  public InputStream get(String src, final SftpProgressMonitor monitor, final long skip) throws SftpException{
+
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      src=remoteAbsolutePath(src);
+      src=isUnique(src);
+
+      byte[] srcb=Util.str2byte(src, fEncoding);
+
+      SftpATTRS attr=_stat(srcb);
+      if(monitor!=null){
+        monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize());
+      }
+
+      sendOPENR(srcb);
+
+      Header header=new Header();
+      header=header(buf, header);
+      int length=header.length;
+      int type=header.type;
+
+      fill(buf, length);
+
+      if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
+	throw new SftpException(SSH_FX_FAILURE, "");
+      }
+      if(type==SSH_FXP_STATUS){
+        int i=buf.getInt();
+        throwStatusError(buf, i);
+      }
+
+      final byte[] handle=buf.getString();         // handle
+
+      java.io.InputStream in=new java.io.InputStream(){
+           long offset=skip;
+           boolean closed=false;
+           int rest_length=0;
+           byte[] _data=new byte[1];
+           byte[] rest_byte=new byte[1024];
+           Header header=new Header();
+
+           public int read() throws java.io.IOException{
+             if(closed)return -1;
+             int i=read(_data, 0, 1);
+             if (i==-1) { return -1; }
+             else {
+               return _data[0]&0xff;
+             }
+           }
+           public int read(byte[] d) throws java.io.IOException{
+             if(closed)return -1;
+             return read(d, 0, d.length);
+           }
+           public int read(byte[] d, int s, int len) throws java.io.IOException{
+             if(closed)return -1;
+             if(d==null){throw new NullPointerException();}
+             if(s<0 || len <0 || s+len>d.length){
+               throw new IndexOutOfBoundsException();
+             } 
+             if(len==0){ return 0; }
+
+             if(rest_length>0){
+               int foo=rest_length;
+               if(foo>len) foo=len;
+               System.arraycopy(rest_byte, 0, d, s, foo);
+               if(foo!=rest_length){
+                 System.arraycopy(rest_byte, foo, 
+                                  rest_byte, 0, rest_length-foo);
+               }
+
+               if(monitor!=null){
+                 if(!monitor.count(foo)){
+                   close();
+                   return -1;
+                 }
+               }
+
+               rest_length-=foo;
+               return foo;
+             }
+
+             if(buf.buffer.length-13<len){
+               len=buf.buffer.length-13;
+             }
+             if(server_version==0 && len>1024){
+               len=1024; 
+             }
+
+             try{sendREAD(handle, offset, len);}
+             catch(Exception e){ throw new IOException("error"); }
+
+             header=header(buf, header);
+             rest_length=header.length;
+             int type=header.type;
+             int id=header.rid;
+
+             if(type!=SSH_FXP_STATUS && type!=SSH_FXP_DATA){ 
+               throw new IOException("error");
+             }
+             if(type==SSH_FXP_STATUS){
+               fill(buf, rest_length);
+               int i=buf.getInt();    
+               rest_length=0;
+               if(i==SSH_FX_EOF){
+                 close();
+                 return -1;
+               }
+               //throwStatusError(buf, i);
+               throw new IOException("error");
+             }
+             buf.rewind();
+             fill(buf.buffer, 0, 4);
+             int length_of_data = buf.getInt(); rest_length-=4;
+
+             /**
+              Since sftp protocol version 6, "end-of-file" has been defined,
+     
+                byte   SSH_FXP_DATA
+                uint32 request-id
+                string data
+                bool   end-of-file [optional]
+     
+              but some sftpd server will send such a field in the sftp protocol 3 ;-(
+              */
+             int optional_data = rest_length - length_of_data;
+
+             offset += length_of_data;
+             int foo = length_of_data;
+             if(foo>0){
+               int bar=foo;
+               if(bar>len){
+                 bar=len;
+               }
+               int i=io_in.read(d, s, bar);
+               if(i<0){
+                 return -1;
+               }
+               foo-=i;
+               rest_length=foo;
+
+               if(foo>0){
+                 if(rest_byte.length<foo){
+                   rest_byte=new byte[foo];
+                 }
+                 int _s=0;
+                 int _len=foo;
+                 int j;
+                 while(_len>0){
+                   j=io_in.read(rest_byte, _s, _len);
+                   if(j<=0)break;
+                   _s+=j;
+                   _len-=j;
+                 }
+               }
+
+               if(optional_data>0){
+                 io_in.skip(optional_data);
+               }
+
+               if(monitor!=null){
+                 if(!monitor.count(i)){
+                   close();
+                   return -1;
+                 }
+               }
+
+               return i;
+             }
+             return 0; // ??
+           }
+           public void close() throws IOException{
+             if(closed)return;
+             closed=true;
+             if(monitor!=null)monitor.end();
+             try{_sendCLOSE(handle, header);}
+             catch(Exception e){throw new IOException("error");}
+           }
+         };
+       return in;
+     }
+     catch(Exception e){
+       if(e instanceof SftpException) throw (SftpException)e;
+       if(e instanceof Throwable)
+         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+       throw new SftpException(SSH_FX_FAILURE, "");
+     }
+   }
+
+   public java.util.Vector ls(String path) throws SftpException{
+     //System.out.println("ls: "+path);
+     try{
+       ((MyPipedInputStream)io_in).updateReadSide();
+
+       path=remoteAbsolutePath(path);
+       byte[] pattern=null;
+       java.util.Vector v=new java.util.Vector();
+
+       int foo=path.lastIndexOf('/');
+       String dir=path.substring(0, ((foo==0)?1:foo));
+       String _pattern=path.substring(foo+1);
+       dir=Util.unquote(dir);
+
+       // If pattern has included '*' or '?', we need to convert
+       // to UTF-8 string before globbing.
+       byte[][] _pattern_utf8=new byte[1][];
+       boolean pattern_has_wildcard=isPattern(_pattern, _pattern_utf8);
+
+       if(pattern_has_wildcard){
+         pattern=_pattern_utf8[0];
+       }
+       else{
+         String upath=Util.unquote(path);
+         //SftpATTRS attr=_lstat(upath);
+         SftpATTRS attr=_stat(upath);
+         if(attr.isDir()){
+           pattern=null;
+           dir=upath;
+         }
+         else{
+           /*
+             // If we can generage longname by ourself,
+             // we don't have to use openDIR.
+           String filename=Util.unquote(_pattern);
+           String longname=...
+           v.addElement(new LsEntry(filename, longname, attr));
+           return v;
+           */
+
+           if(fEncoding_is_utf8){
+             pattern=_pattern_utf8[0];
+             pattern=Util.unquote(pattern);
+           }
+           else{
+             _pattern=Util.unquote(_pattern);
+             pattern=Util.str2byte(_pattern, fEncoding);
+           }
+
+         }
+       }
+
+       sendOPENDIR(Util.str2byte(dir, fEncoding));
+
+       Header header=new Header();
+       header=header(buf, header);
+       int length=header.length;
+       int type=header.type;
+
+       fill(buf, length);
+
+       if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
+         throw new SftpException(SSH_FX_FAILURE, "");
+       }
+       if(type==SSH_FXP_STATUS){
+         int i=buf.getInt();
+         throwStatusError(buf, i);
+       }
+
+       byte[] handle=buf.getString();         // handle
+
+       while(true){
+         sendREADDIR(handle);
+
+         header=header(buf, header);
+         length=header.length;
+         type=header.type;
+         if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){
+           throw new SftpException(SSH_FX_FAILURE, "");
+         }
+         if(type==SSH_FXP_STATUS){ 
+           fill(buf, length);
+           int i=buf.getInt();
+           if(i==SSH_FX_EOF)
+             break;
+           throwStatusError(buf, i);
+         }
+
+         buf.rewind();
+         fill(buf.buffer, 0, 4); length-=4;
+         int count=buf.getInt();
+
+         byte[] str;
+         int flags;
+
+         buf.reset();
+         while(count>0){
+           if(length>0){
+             buf.shift();
+             int j=(buf.buffer.length>(buf.index+length)) ? 
+               length : 
+               (buf.buffer.length-buf.index);
+             int i=fill(buf.buffer, buf.index, j);
+             buf.index+=i;
+             length-=i;
+           }
+           byte[] filename=buf.getString();
+           byte[] longname=null;
+           if(server_version<=3){
+             longname=buf.getString();
+           }
+           SftpATTRS attrs=SftpATTRS.getATTR(buf);
+
+           boolean find=false;
+           String f=null;
+           if(pattern==null){
+             find=true;
+           }
+           else if(!pattern_has_wildcard){
+             find=Util.array_equals(pattern, filename);
+           }
+           else{
+             byte[] _filename=filename;
+             if(!fEncoding_is_utf8){
+               f=Util.byte2str(_filename, fEncoding);
+               _filename=Util.str2byte(f, UTF8);
+             }
+             find=Util.glob(pattern, _filename);
+           }
+
+           if(find){
+             if(f==null){
+               f=Util.byte2str(filename, fEncoding);
+             }
+             String l=null;
+             if(longname==null){
+               // TODO: we need to generate long name from attrs
+               //       for the sftp protocol 4(and later).
+               l=attrs.toString()+" "+f;
+             }
+             else{
+               l=Util.byte2str(longname, fEncoding);
+             }
+             v.addElement(new LsEntry(f, l, attrs));
+           }
+
+           count--; 
+         }
+       }
+       _sendCLOSE(handle, header);
+
+       /*
+       if(v.size()==1 && pattern_has_wildcard){
+         LsEntry le=(LsEntry)v.elementAt(0);
+         if(le.getAttrs().isDir()){
+           String f=le.getFilename();
+           if(isPattern(f)){
+             f=Util.quote(f);
+           }
+           if(!dir.endsWith("/")){
+             dir+="/";
+           }
+           v=null;
+           return ls(dir+f);
+         }
+       }
+       */
+
+       return v;
+     }
+     catch(Exception e){
+       if(e instanceof SftpException) throw (SftpException)e;
+       if(e instanceof Throwable)
+         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+       throw new SftpException(SSH_FX_FAILURE, "");
+     }
+   }
+   public String readlink(String path) throws SftpException{
+     try{
+       if(server_version<3){
+         throw new SftpException(SSH_FX_OP_UNSUPPORTED, 
+                                 "The remote sshd is too old to support symlink operation.");
+       }
+
+       ((MyPipedInputStream)io_in).updateReadSide();
+
+       path=remoteAbsolutePath(path);
+
+       path=isUnique(path);
+
+       sendREADLINK(Util.str2byte(path, fEncoding));
+
+       Header header=new Header();
+       header=header(buf, header);
+       int length=header.length;
+       int type=header.type;
+
+       fill(buf, length);
+
+       if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){
+         throw new SftpException(SSH_FX_FAILURE, "");
+       }
+       if(type==SSH_FXP_NAME){
+         int count=buf.getInt();       // count
+         byte[] filename=null;
+         for(int i=0; i<count; i++){
+           filename=buf.getString();
+           if(server_version<=3){
+             byte[] longname=buf.getString();
+           }
+           SftpATTRS.getATTR(buf);
+         }
+         return Util.byte2str(filename, fEncoding);
+       }
+
+       int i=buf.getInt();
+       throwStatusError(buf, i);
+     }
+     catch(Exception e){
+       if(e instanceof SftpException) throw (SftpException)e;
+       if(e instanceof Throwable)
+         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+       throw new SftpException(SSH_FX_FAILURE, "");
+     }
+     return null;
+   }
+   public void symlink(String oldpath, String newpath) throws SftpException{
+     if(server_version<3){
+       throw new SftpException(SSH_FX_OP_UNSUPPORTED, 
+                               "The remote sshd is too old to support symlink operation.");
+     }
+
+     try{
+       ((MyPipedInputStream)io_in).updateReadSide();
+
+       oldpath=remoteAbsolutePath(oldpath);
+       newpath=remoteAbsolutePath(newpath);
+
+       oldpath=isUnique(oldpath);
+
+       if(isPattern(newpath)){
+         throw new SftpException(SSH_FX_FAILURE, newpath);
+       }
+       newpath=Util.unquote(newpath);
+
+       sendSYMLINK(Util.str2byte(oldpath, fEncoding),
+                   Util.str2byte(newpath, fEncoding));
+
+       Header header=new Header();
+       header=header(buf, header);
+       int length=header.length;
+       int type=header.type;
+
+       fill(buf, length);
+
+       if(type!=SSH_FXP_STATUS){
+         throw new SftpException(SSH_FX_FAILURE, "");
+       }
+
+       int i=buf.getInt();
+       if(i==SSH_FX_OK) return;
+       throwStatusError(buf, i);
+     }
+     catch(Exception e){
+       if(e instanceof SftpException) throw (SftpException)e;
+       if(e instanceof Throwable)
+         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+       throw new SftpException(SSH_FX_FAILURE, "");
+     }
+   }
+
+   public void rename(String oldpath, String newpath) throws SftpException{
+     if(server_version<2){
+       throw new SftpException(SSH_FX_OP_UNSUPPORTED, 
+                               "The remote sshd is too old to support rename operation.");
+     }
+
+     try{
+       ((MyPipedInputStream)io_in).updateReadSide();
+
+       oldpath=remoteAbsolutePath(oldpath);
+       newpath=remoteAbsolutePath(newpath);
+
+       oldpath=isUnique(oldpath);
+
+       Vector v=glob_remote(newpath);
+       int vsize=v.size();
+       if(vsize>=2){
+         throw new SftpException(SSH_FX_FAILURE, v.toString());
+       }
+       if(vsize==1){
+         newpath=(String)(v.elementAt(0));
+       }
+       else{  // vsize==0
+         if(isPattern(newpath))
+           throw new SftpException(SSH_FX_FAILURE, newpath);
+         newpath=Util.unquote(newpath);
+       }
+
+       sendRENAME(Util.str2byte(oldpath, fEncoding),
+                  Util.str2byte(newpath, fEncoding));
+
+       Header header=new Header();
+       header=header(buf, header);
+       int length=header.length;
+       int type=header.type;
+
+       fill(buf, length);
+
+       if(type!=SSH_FXP_STATUS){
+         throw new SftpException(SSH_FX_FAILURE, "");
+       }
+
+       int i=buf.getInt();
+       if(i==SSH_FX_OK) return;
+       throwStatusError(buf, i);
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+  public void rm(String path) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+
+      Vector v=glob_remote(path);
+      int vsize=v.size();
+
+      Header header=new Header();
+
+      for(int j=0; j<vsize; j++){
+	path=(String)(v.elementAt(j));
+        sendREMOVE(Util.str2byte(path, fEncoding));
+
+        header=header(buf, header);
+        int length=header.length;
+        int type=header.type;
+
+        fill(buf, length);
+
+        if(type!=SSH_FXP_STATUS){
+	  throw new SftpException(SSH_FX_FAILURE, "");
+        }
+        int i=buf.getInt();
+	if(i!=SSH_FX_OK){
+	  throwStatusError(buf, i);
+	}
+      }
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  private boolean isRemoteDir(String path){
+    try{
+      sendSTAT(Util.str2byte(path, fEncoding));
+
+      Header header=new Header();
+      header=header(buf, header);
+      int length=header.length;
+      int type=header.type;
+
+      fill(buf, length);
+
+      if(type!=SSH_FXP_ATTRS){
+        return false; 
+      }
+      SftpATTRS attr=SftpATTRS.getATTR(buf);
+      return attr.isDir();
+    }
+    catch(Exception e){}
+    return false;
+  }
+
+  public void chgrp(int gid, String path) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+
+      Vector v=glob_remote(path);
+      int vsize=v.size();
+      for(int j=0; j<vsize; j++){
+	path=(String)(v.elementAt(j));
+
+        SftpATTRS attr=_stat(path);
+
+	attr.setFLAGS(0);
+	attr.setUIDGID(attr.uid, gid); 
+	_setStat(path, attr);
+      }
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  public void chown(int uid, String path) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+
+      Vector v=glob_remote(path);
+      int vsize=v.size();
+      for(int j=0; j<vsize; j++){
+	path=(String)(v.elementAt(j));
+
+        SftpATTRS attr=_stat(path);
+
+	attr.setFLAGS(0);
+	attr.setUIDGID(uid, attr.gid); 
+	_setStat(path, attr);
+      }
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  public void chmod(int permissions, String path) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+
+      Vector v=glob_remote(path);
+      int vsize=v.size();
+      for(int j=0; j<vsize; j++){
+	path=(String)(v.elementAt(j));
+
+	SftpATTRS attr=_stat(path);
+
+	attr.setFLAGS(0);
+	attr.setPERMISSIONS(permissions); 
+	_setStat(path, attr);
+      }
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  public void setMtime(String path, int mtime) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+
+      Vector v=glob_remote(path);
+      int vsize=v.size();
+      for(int j=0; j<vsize; j++){
+	path=(String)(v.elementAt(j));
+
+        SftpATTRS attr=_stat(path);
+
+	attr.setFLAGS(0);
+	attr.setACMODTIME(attr.getATime(), mtime);
+	_setStat(path, attr);
+      }
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  public void rmdir(String path) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+
+      Vector v=glob_remote(path);
+      int vsize=v.size();
+
+      Header header=new Header();
+
+      for(int j=0; j<vsize; j++){
+	path=(String)(v.elementAt(j));
+	sendRMDIR(Util.str2byte(path, fEncoding));
+
+        header=header(buf, header);
+        int length=header.length;
+        int type=header.type;
+
+        fill(buf, length);
+
+	if(type!=SSH_FXP_STATUS){
+	  throw new SftpException(SSH_FX_FAILURE, "");
+	}
+
+	int i=buf.getInt();
+	if(i!=SSH_FX_OK){
+	  throwStatusError(buf, i);
+	}
+      }
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  public void mkdir(String path) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+
+      sendMKDIR(Util.str2byte(path, fEncoding), null);
+
+      Header header=new Header();      
+      header=header(buf, header);
+      int length=header.length;
+      int type=header.type;
+
+      fill(buf, length);
+
+      if(type!=SSH_FXP_STATUS){
+	throw new SftpException(SSH_FX_FAILURE, "");
+      }
+
+      int i=buf.getInt();
+      if(i==SSH_FX_OK) return;
+      throwStatusError(buf, i);
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  public SftpATTRS stat(String path) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+      path=isUnique(path);
+
+      return _stat(path);
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+    //return null;
+  }
+
+  private SftpATTRS _stat(byte[] path) throws SftpException{
+    try{
+
+      sendSTAT(path);
+
+      Header header=new Header();
+      header=header(buf, header);
+      int length=header.length;
+      int type=header.type;
+
+      fill(buf, length);
+
+      if(type!=SSH_FXP_ATTRS){
+	if(type==SSH_FXP_STATUS){
+	  int i=buf.getInt();
+	  throwStatusError(buf, i);
+	}
+	throw new SftpException(SSH_FX_FAILURE, "");
+      }
+      SftpATTRS attr=SftpATTRS.getATTR(buf);
+      return attr;
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+    //return null;
+  }
+
+  private SftpATTRS _stat(String path) throws SftpException{
+    return _stat(Util.str2byte(path, fEncoding));
+  }
+
+  public SftpATTRS lstat(String path) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+      path=isUnique(path);
+
+      return _lstat(path);
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  private SftpATTRS _lstat(String path) throws SftpException{
+    try{
+      sendLSTAT(Util.str2byte(path, fEncoding));
+
+      Header header=new Header();
+      header=header(buf, header);
+      int length=header.length;
+      int type=header.type;
+
+      fill(buf, length);
+
+      if(type!=SSH_FXP_ATTRS){
+	if(type==SSH_FXP_STATUS){
+	  int i=buf.getInt();
+	  throwStatusError(buf, i);
+	}
+	throw new SftpException(SSH_FX_FAILURE, "");
+      }
+      SftpATTRS attr=SftpATTRS.getATTR(buf);
+      return attr;
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  private byte[] _realpath(String path) throws SftpException, IOException, Exception{
+    sendREALPATH(Util.str2byte(path, fEncoding));
+
+    Header header=new Header();
+    header=header(buf, header);
+    int length=header.length;
+    int type=header.type;
+
+    fill(buf, length);
+
+    if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+    int i;
+    if(type==SSH_FXP_STATUS){
+      i=buf.getInt();
+      throwStatusError(buf, i);
+    }
+    i=buf.getInt();   // count
+
+    byte[] str=null;
+    while(i-->0){
+      str=buf.getString();  // absolute path;
+      if(server_version<=3){
+        byte[] lname=buf.getString();  // long filename
+      }
+      SftpATTRS attr=SftpATTRS.getATTR(buf);  // dummy attribute
+    }
+    return str;
+  }
+
+  public void setStat(String path, SftpATTRS attr) throws SftpException{
+    try{
+      ((MyPipedInputStream)io_in).updateReadSide();
+
+      path=remoteAbsolutePath(path);
+
+      Vector v=glob_remote(path);
+      int vsize=v.size();
+      for(int j=0; j<vsize; j++){
+	path=(String)(v.elementAt(j));
+	_setStat(path, attr);
+      }
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+  private void _setStat(String path, SftpATTRS attr) throws SftpException{
+    try{
+      sendSETSTAT(Util.str2byte(path, fEncoding), attr);
+
+      Header header=new Header();
+      header=header(buf, header);
+      int length=header.length;
+      int type=header.type;
+
+      fill(buf, length);
+
+      if(type!=SSH_FXP_STATUS){
+	throw new SftpException(SSH_FX_FAILURE, "");
+      }
+      int i=buf.getInt();
+      if(i!=SSH_FX_OK){
+	throwStatusError(buf, i);
+      }
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  public String pwd() throws SftpException{ return getCwd(); }
+  public String lpwd(){ return lcwd; }
+  public String version(){ return version; }
+  public String getHome() throws SftpException {
+    if(home==null){
+      try{
+        ((MyPipedInputStream)io_in).updateReadSide();
+
+        byte[] _home=_realpath("");
+        home=Util.byte2str(_home, fEncoding);
+      }
+      catch(Exception e){
+        if(e instanceof SftpException) throw (SftpException)e;
+        if(e instanceof Throwable)
+          throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+        throw new SftpException(SSH_FX_FAILURE, "");
+      }
+    }
+    return home; 
+  }
+
+  private String getCwd() throws SftpException{
+    if(cwd==null)
+      cwd=getHome();
+    return cwd;
+  }
+
+  private void setCwd(String cwd){
+    this.cwd=cwd;
+  }
+
+  private void read(byte[] buf, int s, int l) throws IOException, SftpException{
+    int i=0;
+    while(l>0){
+      i=io_in.read(buf, s, l);
+      if(i<=0){
+        throw new SftpException(SSH_FX_FAILURE, "");
+      }
+      s+=i;
+      l-=i;
+    }
+  }
+
+  private boolean checkStatus(int[] ackid, Header header) throws IOException, SftpException{
+    header=header(buf, header);
+    int length=header.length;
+    int type=header.type;
+    if(ackid!=null)
+      ackid[0]=header.rid;
+
+    fill(buf, length);
+
+    if(type!=SSH_FXP_STATUS){ 
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+    int i=buf.getInt();
+    if(i!=SSH_FX_OK){
+      throwStatusError(buf, i);
+    }
+    return true;
+  }
+  private boolean _sendCLOSE(byte[] handle, Header header) throws Exception{
+    sendCLOSE(handle);
+    return checkStatus(null, header);
+  }
+
+  private void sendINIT() throws Exception{
+    packet.reset();
+    putHEAD(SSH_FXP_INIT, 5);
+    buf.putInt(3);                // version 3
+    getSession().write(packet, this, 5+4);
+  }
+
+  private void sendREALPATH(byte[] path) throws Exception{
+    sendPacketPath(SSH_FXP_REALPATH, path);
+  }
+  private void sendSTAT(byte[] path) throws Exception{
+    sendPacketPath(SSH_FXP_STAT, path);
+  }
+  private void sendLSTAT(byte[] path) throws Exception{
+    sendPacketPath(SSH_FXP_LSTAT, path);
+  }
+  private void sendFSTAT(byte[] handle) throws Exception{
+    sendPacketPath(SSH_FXP_FSTAT, handle);
+  }
+  private void sendSETSTAT(byte[] path, SftpATTRS attr) throws Exception{
+    packet.reset();
+    putHEAD(SSH_FXP_SETSTAT, 9+path.length+attr.length());
+    buf.putInt(seq++);
+    buf.putString(path);             // path
+    attr.dump(buf);
+    getSession().write(packet, this, 9+path.length+attr.length()+4);
+  }
+  private void sendREMOVE(byte[] path) throws Exception{
+    sendPacketPath(SSH_FXP_REMOVE, path);
+  }
+  private void sendMKDIR(byte[] path, SftpATTRS attr) throws Exception{
+    packet.reset();
+    putHEAD(SSH_FXP_MKDIR, 9+path.length+(attr!=null?attr.length():4));
+    buf.putInt(seq++);
+    buf.putString(path);             // path
+    if(attr!=null) attr.dump(buf);
+    else buf.putInt(0);
+    getSession().write(packet, this, 9+path.length+(attr!=null?attr.length():4)+4);
+  }
+  private void sendRMDIR(byte[] path) throws Exception{
+    sendPacketPath(SSH_FXP_RMDIR, path);
+  }
+  private void sendSYMLINK(byte[] p1, byte[] p2) throws Exception{
+    sendPacketPath(SSH_FXP_SYMLINK, p1, p2);
+  }
+  private void sendREADLINK(byte[] path) throws Exception{
+    sendPacketPath(SSH_FXP_READLINK, path);
+  }
+  private void sendOPENDIR(byte[] path) throws Exception{
+    sendPacketPath(SSH_FXP_OPENDIR, path);
+  }
+  private void sendREADDIR(byte[] path) throws Exception{
+    sendPacketPath(SSH_FXP_READDIR, path);
+  }
+  private void sendRENAME(byte[] p1, byte[] p2) throws Exception{
+    sendPacketPath(SSH_FXP_RENAME, p1, p2);
+  }
+  private void sendCLOSE(byte[] path) throws Exception{
+    sendPacketPath(SSH_FXP_CLOSE, path);
+  }
+  private void sendOPENR(byte[] path) throws Exception{
+    sendOPEN(path, SSH_FXF_READ);
+  }
+  private void sendOPENW(byte[] path) throws Exception{
+    sendOPEN(path, SSH_FXF_WRITE|SSH_FXF_CREAT|SSH_FXF_TRUNC);
+  }
+  private void sendOPENA(byte[] path) throws Exception{
+    sendOPEN(path, SSH_FXF_WRITE|/*SSH_FXF_APPEND|*/SSH_FXF_CREAT);
+  }
+  private void sendOPEN(byte[] path, int mode) throws Exception{
+    packet.reset();
+    putHEAD(SSH_FXP_OPEN, 17+path.length);
+    buf.putInt(seq++);
+    buf.putString(path);
+    buf.putInt(mode);
+    buf.putInt(0);           // attrs
+    getSession().write(packet, this, 17+path.length+4);
+  }
+  private void sendPacketPath(byte fxp, byte[] path) throws Exception{
+    packet.reset();
+    putHEAD(fxp, 9+path.length);
+    buf.putInt(seq++);
+    buf.putString(path);             // path
+    getSession().write(packet, this, 9+path.length+4);
+  }
+  private void sendPacketPath(byte fxp, byte[] p1, byte[] p2) throws Exception{
+    packet.reset();
+    putHEAD(fxp, 13+p1.length+p2.length);
+    buf.putInt(seq++);
+    buf.putString(p1);
+    buf.putString(p2);
+    getSession().write(packet, this, 13+p1.length+p2.length+4);
+  }
+
+  private int sendWRITE(byte[] handle, long offset, 
+                        byte[] data, int start, int length) throws Exception{
+    int _length=length;
+    opacket.reset();
+    if(obuf.buffer.length<obuf.index+13+21+handle.length+length+Session.buffer_margin){
+      _length=obuf.buffer.length-(obuf.index+13+21+handle.length+Session.buffer_margin);
+      // System.err.println("_length="+_length+" length="+length);
+    }
+
+    putHEAD(obuf, SSH_FXP_WRITE, 21+handle.length+_length);       // 14
+    obuf.putInt(seq++);                                      //  4
+    obuf.putString(handle);                                  //  4+handle.length
+    obuf.putLong(offset);                                    //  8
+    if(obuf.buffer!=data){
+      obuf.putString(data, start, _length);                    //  4+_length
+    }
+    else{
+      obuf.putInt(_length);
+      obuf.skip(_length);
+    }
+    getSession().write(opacket, this, 21+handle.length+_length+4);
+    return _length;
+  }
+  private void sendREAD(byte[] handle, long offset, int length) throws Exception{
+    sendREAD(handle, offset, length, null);
+  }
+  private void sendREAD(byte[] handle, long offset, int length,
+                        RequestQueue rrq) throws Exception{
+    packet.reset();
+    putHEAD(SSH_FXP_READ, 21+handle.length);
+    buf.putInt(seq++);
+    buf.putString(handle);
+    buf.putLong(offset);
+    buf.putInt(length);
+    getSession().write(packet, this, 21+handle.length+4);
+    if(rrq!=null){
+      rrq.add(seq-1, offset, length);
+    }
+  }
+
+  private void putHEAD(Buffer buf, byte type, int length) throws Exception{
+    buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
+    buf.putInt(recipient);
+    buf.putInt(length+4);
+    buf.putInt(length);
+    buf.putByte(type);
+  }
+
+  private void putHEAD(byte type, int length) throws Exception{
+    putHEAD(buf, type, length);
+  }
+
+  private Vector glob_remote(String _path) throws Exception{
+    Vector v=new Vector();
+    int i=0;
+
+    int foo=_path.lastIndexOf('/');
+    if(foo<0){  // it is not absolute path.
+      v.addElement(Util.unquote(_path)); 
+      return v;
+    }
+
+    String dir=_path.substring(0, ((foo==0)?1:foo));
+    String _pattern=_path.substring(foo+1);
+
+    dir=Util.unquote(dir);
+
+    byte[] pattern=null;
+    byte[][] _pattern_utf8=new byte[1][];
+    boolean pattern_has_wildcard=isPattern(_pattern, _pattern_utf8);
+
+    if(!pattern_has_wildcard){
+      if(!dir.equals("/"))
+        dir+="/";
+      v.addElement(dir+Util.unquote(_pattern));
+      return v;
+    }
+
+    pattern=_pattern_utf8[0];
+
+    sendOPENDIR(Util.str2byte(dir, fEncoding));
+
+    Header header=new Header();
+    header=header(buf, header);
+    int length=header.length;
+    int type=header.type;
+
+    fill(buf, length);
+
+    if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+    if(type==SSH_FXP_STATUS){
+      i=buf.getInt();
+      throwStatusError(buf, i);
+    }
+
+    byte[] handle=buf.getString();         // filename
+    String pdir=null;                      // parent directory
+
+    while(true){
+      sendREADDIR(handle);
+      header=header(buf, header);
+      length=header.length;
+      type=header.type;
+
+      if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){
+	throw new SftpException(SSH_FX_FAILURE, "");
+      }
+      if(type==SSH_FXP_STATUS){ 
+        fill(buf, length);
+	break;
+      }
+
+      buf.rewind();
+      fill(buf.buffer, 0, 4); length-=4;
+      int count=buf.getInt();
+
+      byte[] str;
+      int flags;
+
+      buf.reset();
+      while(count>0){
+	if(length>0){
+	  buf.shift();
+          int j=(buf.buffer.length>(buf.index+length)) ? length : (buf.buffer.length-buf.index);
+	  i=io_in.read(buf.buffer, buf.index, j);
+	  if(i<=0)break;
+	  buf.index+=i;
+	  length-=i;
+	}
+
+	byte[] filename=buf.getString();
+	//System.err.println("filename: "+new String(filename));
+        if(server_version<=3){
+          str=buf.getString();  // longname
+        }
+	SftpATTRS attrs=SftpATTRS.getATTR(buf);
+
+        byte[] _filename=filename;
+        String f=null;
+        boolean found=false;
+
+        if(!fEncoding_is_utf8){
+          f=Util.byte2str(filename, fEncoding);
+          _filename=Util.str2byte(f, UTF8);
+        }
+        found=Util.glob(pattern, _filename);
+
+	if(found){
+          if(f==null){
+            f=Util.byte2str(filename, fEncoding);
+          }
+          if(pdir==null){
+            pdir=dir;
+            if(!pdir.endsWith("/")){
+              pdir+="/";
+            }
+          }
+	  v.addElement(pdir+f);
+	}
+	count--; 
+      }
+    }
+    if(_sendCLOSE(handle, header)) 
+      return v;
+    return null;
+  }
+
+  private boolean isPattern(byte[] path){
+    int length=path.length;
+    int i=0;
+    while(i<length){
+      if(path[i]=='*' || path[i]=='?')
+        return true;
+      if(path[i]=='\\' && (i+1)<length)
+        i++;
+      i++;
+    }
+    return false;
+  }
+
+  private Vector glob_local(String _path) throws Exception{
+//System.err.println("glob_local: "+_path);
+    Vector v=new Vector();
+    byte[] path=Util.str2byte(_path, UTF8);
+    int i=path.length-1;
+    while(i>=0){
+      if(path[i]!='*' && path[i]!='?'){
+        i--;
+        continue;
+      }
+      if(!fs_is_bs &&
+         i>0 && path[i-1]=='\\'){
+        i--;
+        if(i>0 && path[i-1]=='\\'){
+          i--;
+          i--;
+          continue;
+        }
+      }
+      break;
+    }
+
+    if(i<0){ v.addElement(fs_is_bs ? _path : Util.unquote(_path)); return v;}
+
+    while(i>=0){
+      if(path[i]==file_separatorc ||
+         (fs_is_bs && path[i]=='/')){ // On Windows, '/' is also the separator.
+        break;
+      }
+      i--;
+    }
+
+    if(i<0){ v.addElement(fs_is_bs ? _path : Util.unquote(_path)); return v;}
+
+    byte[] dir;
+    if(i==0){dir=new byte[]{(byte)file_separatorc};}
+    else{ 
+      dir=new byte[i];
+      System.arraycopy(path, 0, dir, 0, i);
+    }
+
+    byte[] pattern=new byte[path.length-i-1];
+    System.arraycopy(path, i+1, pattern, 0, pattern.length);
+
+//System.err.println("dir: "+new String(dir)+" pattern: "+new String(pattern));
+    try{
+      String[] children=(new File(Util.byte2str(dir, UTF8))).list();
+      String pdir=Util.byte2str(dir)+file_separator;
+      for(int j=0; j<children.length; j++){
+//System.err.println("children: "+children[j]);
+	if(Util.glob(pattern, Util.str2byte(children[j], UTF8))){
+	  v.addElement(pdir+children[j]);
+	}
+      }
+    }
+    catch(Exception e){
+    }
+    return v;
+  }
+
+  private void throwStatusError(Buffer buf, int i) throws SftpException{
+    if(server_version>=3 &&   // WindRiver's sftp will send invalid 
+       buf.getLength()>=4){   // SSH_FXP_STATUS packet.
+      byte[] str=buf.getString();
+      //byte[] tag=buf.getString();
+      throw new SftpException(i, Util.byte2str(str, UTF8));
+    }
+    else{
+      throw new SftpException(i, "Failure");
+    }
+  }
+
+  private static boolean isLocalAbsolutePath(String path){
+    return (new File(path)).isAbsolute();
+  }
+
+  public void disconnect(){
+    super.disconnect();
+  }
+
+  private boolean isPattern(String path, byte[][] utf8){
+    byte[] _path=Util.str2byte(path, UTF8);
+    if(utf8!=null)
+      utf8[0]=_path;
+    return isPattern(_path);
+  }
+
+  private boolean isPattern(String path){
+    return isPattern(path, null);
+  }
+
+  private void fill(Buffer buf, int len)  throws IOException{
+    buf.reset();
+    fill(buf.buffer, 0, len);
+    buf.skip(len);
+  }
+
+  private int fill(byte[] buf, int s, int len) throws IOException{
+    int i=0;
+    int foo=s;
+    while(len>0){
+      i=io_in.read(buf, s, len);
+      if(i<=0){
+        throw new IOException("inputstream is closed");
+        //return (s-foo)==0 ? i : s-foo;
+      }
+      s+=i;
+      len-=i;
+    }
+    return s-foo;
+  }
+  private void skip(long foo) throws IOException{
+    while(foo>0){
+      long bar=io_in.skip(foo);
+      if(bar<=0) 
+        break;
+      foo-=bar;
+    }
+  }
+
+  class Header{
+    int length;
+    int type;
+    int rid;
+  }
+  private Header header(Buffer buf, Header header) throws IOException{
+    buf.rewind();
+    int i=fill(buf.buffer, 0, 9);
+    header.length=buf.getInt()-5;
+    header.type=buf.getByte()&0xff;
+    header.rid=buf.getInt();  
+    return header;
+  }
+
+  private String remoteAbsolutePath(String path) throws SftpException{
+    if(path.charAt(0)=='/') return path;
+    String cwd=getCwd();
+//    if(cwd.equals(getHome())) return path;
+    if(cwd.endsWith("/")) return cwd+path;
+    return cwd+"/"+path;
+  }
+
+  private String localAbsolutePath(String path){
+    if(isLocalAbsolutePath(path)) return path;
+    if(lcwd.endsWith(file_separator)) return lcwd+path;
+    return lcwd+file_separator+path;
+  }
+
+  /**
+   * This method will check if the given string can be expanded to the
+   * unique string.  If it can be expanded to mutiple files, SftpException
+   * will be thrown.
+   * @return the returned string is unquoted.
+   */
+  private String isUnique(String path) throws SftpException, Exception{
+    Vector v=glob_remote(path);
+    if(v.size()!=1){
+      throw new SftpException(SSH_FX_FAILURE, path+" is not unique: "+v.toString());
+    }
+    return (String)(v.elementAt(0));
+  }
+
+  public int getServerVersion() throws SftpException{
+    if(!isConnected()){
+      throw new SftpException(SSH_FX_FAILURE, "The channel is not connected.");
+    }
+    return server_version;
+  }
+
+  public void setFilenameEncoding(String encoding) throws SftpException{
+    int sversion=getServerVersion();
+    if(3 <= sversion && sversion <= 5 &&
+       !encoding.equals(UTF8)){
+      throw new SftpException(SSH_FX_FAILURE,
+                              "The encoding can not be changed for this sftp server.");
+    }
+    if(encoding.equals(UTF8)){
+      encoding=UTF8;
+    }
+    fEncoding=encoding;
+    fEncoding_is_utf8=fEncoding.equals(UTF8);
+  }
+
+  public String getExtension(String key){
+    if(extensions==null)
+      return null;
+    return (String)extensions.get(key);
+  }
+
+  public String realpath(String path) throws SftpException{
+    try{
+      byte[] _path=_realpath(remoteAbsolutePath(path));
+      return Util.byte2str(_path, fEncoding);
+    }
+    catch(Exception e){
+      if(e instanceof SftpException) throw (SftpException)e;
+      if(e instanceof Throwable)
+        throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  public class LsEntry implements Comparable{
+    private  String filename;
+    private  String longname;
+    private  SftpATTRS attrs;
+    LsEntry(String filename, String longname, SftpATTRS attrs){
+      setFilename(filename);
+      setLongname(longname);
+      setAttrs(attrs);
+    }
+    public String getFilename(){return filename;};
+    void setFilename(String filename){this.filename = filename;};
+    public String getLongname(){return longname;};
+    void setLongname(String longname){this.longname = longname;};
+    public SftpATTRS getAttrs(){return attrs;};
+    void setAttrs(SftpATTRS attrs) {this.attrs = attrs;};
+    public String toString(){ return longname; }
+    public int compareTo(Object o) throws ClassCastException{
+      if(o instanceof LsEntry){
+        return filename.compareTo(((LsEntry)o).getFilename());
+      }
+      throw new ClassCastException("a decendent of LsEntry must be given.");
+    }
+  }
+}
diff --git a/java/com/jcraft/jsch/ChannelShell.java b/java/com/jcraft/jsch/ChannelShell.java
new file mode 100644
index 0000000..eeb24a3
--- /dev/null
+++ b/java/com/jcraft/jsch/ChannelShell.java
@@ -0,0 +1,70 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.util.*;
+
+public class ChannelShell extends ChannelSession{
+
+  ChannelShell(){
+    super();
+    pty=true;
+  }
+
+  public void start() throws JSchException{
+    Session _session=getSession();
+    try{
+      sendRequests();
+
+      Request request=new RequestShell();
+      request.request(_session, this);
+    }
+    catch(Exception e){
+      if(e instanceof JSchException) throw (JSchException)e;
+      if(e instanceof Throwable)
+        throw new JSchException("ChannelShell", (Throwable)e);
+      throw new JSchException("ChannelShell");
+    }
+
+    if(io.in!=null){
+      thread=new Thread(this);
+      thread.setName("Shell for "+_session.host);
+      if(_session.daemon_thread){
+        thread.setDaemon(_session.daemon_thread);
+      }
+      thread.start();
+    }
+  }
+
+  void init() throws JSchException {
+    io.setInputStream(getSession().in);
+    io.setOutputStream(getSession().out);
+  }
+}
diff --git a/java/com/jcraft/jsch/ChannelSubsystem.java b/java/com/jcraft/jsch/ChannelSubsystem.java
new file mode 100644
index 0000000..3f33bf3
--- /dev/null
+++ b/java/com/jcraft/jsch/ChannelSubsystem.java
@@ -0,0 +1,83 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class ChannelSubsystem extends ChannelSession{
+  boolean xforwading=false;
+  boolean pty=false;
+  boolean want_reply=true;
+  String subsystem="";
+  public void setXForwarding(boolean foo){ xforwading=foo; }
+  public void setPty(boolean foo){ pty=foo; }
+  public void setWantReply(boolean foo){ want_reply=foo; }
+  public void setSubsystem(String foo){ subsystem=foo; }
+  public void start() throws JSchException{
+    Session _session=getSession();
+    try{
+      Request request;
+      if(xforwading){
+        request=new RequestX11();
+        request.request(_session, this);
+      }
+      if(pty){
+	request=new RequestPtyReq();
+	request.request(_session, this);
+      }
+      request=new RequestSubsystem();
+      ((RequestSubsystem)request).request(_session, this, subsystem, want_reply);
+    }
+    catch(Exception e){
+      if(e instanceof JSchException){ throw (JSchException)e; }
+      if(e instanceof Throwable)
+        throw new JSchException("ChannelSubsystem", (Throwable)e);
+      throw new JSchException("ChannelSubsystem");
+    }
+    if(io.in!=null){
+      thread=new Thread(this);
+      thread.setName("Subsystem for "+_session.host);
+      if(_session.daemon_thread){
+        thread.setDaemon(_session.daemon_thread);
+      }
+      thread.start();
+    }
+  }
+
+  void init() throws JSchException {
+    io.setInputStream(getSession().in);
+    io.setOutputStream(getSession().out);
+  }
+
+  public void setErrStream(java.io.OutputStream out){
+    setExtOutputStream(out);
+  }
+  public java.io.InputStream getErrStream() throws java.io.IOException {
+    return getExtInputStream();
+  }
+}
diff --git a/java/com/jcraft/jsch/ChannelX11.java b/java/com/jcraft/jsch/ChannelX11.java
new file mode 100644
index 0000000..393728c
--- /dev/null
+++ b/java/com/jcraft/jsch/ChannelX11.java
@@ -0,0 +1,273 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.net.*;
+
+class ChannelX11 extends Channel{
+
+  static private final int LOCAL_WINDOW_SIZE_MAX=0x20000;
+  static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000;
+
+  static private final int TIMEOUT=10*1000;
+
+  private static String host="127.0.0.1";
+  private static int port=6000;
+
+  private boolean init=true;
+
+  static byte[] cookie=null;
+  private static byte[] cookie_hex=null;
+
+  private static java.util.Hashtable faked_cookie_pool=new java.util.Hashtable();
+  private static java.util.Hashtable faked_cookie_hex_pool=new java.util.Hashtable();
+
+  private static byte[] table={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,
+                               0x61,0x62,0x63,0x64,0x65,0x66};
+
+  private Socket socket = null;
+
+  static int revtable(byte foo){
+    for(int i=0; i<table.length; i++){
+      if(table[i]==foo)return i;
+    }
+    return 0;
+  }
+  static void setCookie(String foo){
+    cookie_hex=Util.str2byte(foo); 
+    cookie=new byte[16];
+    for(int i=0; i<16; i++){
+	cookie[i]=(byte)(((revtable(cookie_hex[i*2])<<4)&0xf0) |
+			 ((revtable(cookie_hex[i*2+1]))&0xf));
+    }
+  }
+  static void setHost(String foo){ host=foo; }
+  static void setPort(int foo){ port=foo; }
+  static byte[] getFakedCookie(Session session){
+    synchronized(faked_cookie_hex_pool){
+      byte[] foo=(byte[])faked_cookie_hex_pool.get(session);
+      if(foo==null){
+	Random random=Session.random;
+	foo=new byte[16];
+	synchronized(random){
+	  random.fill(foo, 0, 16);
+	}
+/*
+System.err.print("faked_cookie: ");
+for(int i=0; i<foo.length; i++){
+    System.err.print(Integer.toHexString(foo[i]&0xff)+":");
+}
+System.err.println("");
+*/
+	faked_cookie_pool.put(session, foo);
+	byte[] bar=new byte[32];
+	for(int i=0; i<16; i++){
+	  bar[2*i]=table[(foo[i]>>>4)&0xf];
+	  bar[2*i+1]=table[(foo[i])&0xf];
+	}
+	faked_cookie_hex_pool.put(session, bar);
+	foo=bar;
+      }
+      return foo;
+    }
+  }
+
+  static void removeFakedCookie(Session session){
+    synchronized(faked_cookie_hex_pool){
+      faked_cookie_hex_pool.remove(session);
+      faked_cookie_pool.remove(session);
+    }
+  }
+
+  ChannelX11(){
+    super();
+
+    setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
+    setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
+    setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
+
+    type=Util.str2byte("x11");
+
+    connected=true;
+    /*
+    try{ 
+      socket=Util.createSocket(host, port, TIMEOUT);
+      socket.setTcpNoDelay(true);
+      io=new IO();
+      io.setInputStream(socket.getInputStream());
+      io.setOutputStream(socket.getOutputStream());
+    }
+    catch(Exception e){
+      //System.err.println(e);
+    }
+    */
+  }
+
+  public void run(){
+
+    try{ 
+      socket=Util.createSocket(host, port, TIMEOUT);
+      socket.setTcpNoDelay(true);
+      io=new IO();
+      io.setInputStream(socket.getInputStream());
+      io.setOutputStream(socket.getOutputStream());
+      sendOpenConfirmation();
+    }
+    catch(Exception e){
+      sendOpenFailure(SSH_OPEN_ADMINISTRATIVELY_PROHIBITED);
+      close=true;
+      disconnect();
+      return;
+    }
+
+    thread=Thread.currentThread();
+    Buffer buf=new Buffer(rmpsize);
+    Packet packet=new Packet(buf);
+    int i=0;
+    try{
+      while(thread!=null &&
+            io!=null &&
+            io.in!=null){
+        i=io.in.read(buf.buffer, 
+		     14, 
+		     buf.buffer.length-14-Session.buffer_margin);
+	if(i<=0){
+	  eof();
+          break;
+	}
+	if(close)break;
+        packet.reset();
+        buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
+        buf.putInt(recipient);
+        buf.putInt(i);
+        buf.skip(i);
+	getSession().write(packet, this, i);
+      }
+    }
+    catch(Exception e){
+      //System.err.println(e);
+    }
+    disconnect();
+  }
+
+  private byte[] cache=new byte[0];
+  private byte[] addCache(byte[] foo, int s, int l){
+    byte[] bar=new byte[cache.length+l];
+    System.arraycopy(foo, s, bar, cache.length, l);
+    if(cache.length>0)
+      System.arraycopy(cache, 0, bar, 0, cache.length);
+    cache=bar;
+    return cache;
+  }
+
+  void write(byte[] foo, int s, int l) throws java.io.IOException {
+    //if(eof_local)return;
+
+    if(init){
+
+      Session _session=null;
+      try{
+        _session=getSession();
+      }
+      catch(JSchException e){
+        throw new java.io.IOException(e.toString());
+      }
+
+      foo=addCache(foo, s, l);
+      s=0; 
+      l=foo.length;
+
+      if(l<9)
+        return;
+
+      int plen=(foo[s+6]&0xff)*256+(foo[s+7]&0xff);
+      int dlen=(foo[s+8]&0xff)*256+(foo[s+9]&0xff);
+
+      if((foo[s]&0xff)==0x42){
+      }
+      else if((foo[s]&0xff)==0x6c){
+         plen=((plen>>>8)&0xff)|((plen<<8)&0xff00);
+         dlen=((dlen>>>8)&0xff)|((dlen<<8)&0xff00);
+      }
+      else{
+	  // ??
+      }
+
+      if(l<12+plen+((-plen)&3)+dlen)
+        return;
+
+      byte[] bar=new byte[dlen];
+      System.arraycopy(foo, s+12+plen+((-plen)&3), bar, 0, dlen);
+      byte[] faked_cookie=null;
+
+      synchronized(faked_cookie_pool){
+	faked_cookie=(byte[])faked_cookie_pool.get(_session);
+      }
+
+      /*
+System.err.print("faked_cookie: ");
+for(int i=0; i<faked_cookie.length; i++){
+    System.err.print(Integer.toHexString(faked_cookie[i]&0xff)+":");
+}
+System.err.println("");
+System.err.print("bar: ");
+for(int i=0; i<bar.length; i++){
+    System.err.print(Integer.toHexString(bar[i]&0xff)+":");
+}
+System.err.println("");
+      */
+
+      if(equals(bar, faked_cookie)){
+        if(cookie!=null)
+          System.arraycopy(cookie, 0, foo, s+12+plen+((-plen)&3), dlen);
+      }
+      else{
+	  //System.err.println("wrong cookie");
+          thread=null;
+          eof();
+          io.close();
+          disconnect();
+      }
+      init=false;
+      io.put(foo, s, l);
+      cache=null;
+      return;
+    }
+    io.put(foo, s, l);
+  }
+
+  private static boolean equals(byte[] foo, byte[] bar){
+    if(foo.length!=bar.length)return false;
+    for(int i=0; i<foo.length; i++){
+      if(foo[i]!=bar[i])return false;
+    }
+    return true;
+  }
+}
diff --git a/java/com/jcraft/jsch/Cipher.java b/java/com/jcraft/jsch/Cipher.java
new file mode 100644
index 0000000..cc7084e
--- /dev/null
+++ b/java/com/jcraft/jsch/Cipher.java
@@ -0,0 +1,40 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface Cipher{
+  static int ENCRYPT_MODE=0;
+  static int DECRYPT_MODE=1;
+  int getIVSize(); 
+  int getBlockSize(); 
+  void init(int mode, byte[] key, byte[] iv) throws Exception; 
+  void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception;
+  boolean isCBC();
+}
diff --git a/java/com/jcraft/jsch/CipherNone.java b/java/com/jcraft/jsch/CipherNone.java
new file mode 100644
index 0000000..61f836a
--- /dev/null
+++ b/java/com/jcraft/jsch/CipherNone.java
@@ -0,0 +1,42 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class CipherNone implements Cipher{
+  private static final int ivsize=8;
+  private static final int bsize=16;
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+  }
+  public boolean isCBC(){return false; }
+}
diff --git a/java/com/jcraft/jsch/Compression.java b/java/com/jcraft/jsch/Compression.java
new file mode 100644
index 0000000..7ae7922
--- /dev/null
+++ b/java/com/jcraft/jsch/Compression.java
@@ -0,0 +1,38 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface Compression{
+  static public final int INFLATER=0;
+  static public final int DEFLATER=1;
+  void init(int type, int level);
+  byte[] compress(byte[] buf, int start, int[] len);
+  byte[] uncompress(byte[] buf, int start, int[] len);
+}
diff --git a/java/com/jcraft/jsch/DH.java b/java/com/jcraft/jsch/DH.java
new file mode 100644
index 0000000..963f13c
--- /dev/null
+++ b/java/com/jcraft/jsch/DH.java
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface DH{
+  void init() throws Exception;
+  void setP(byte[] p);
+  void setG(byte[] g);
+  byte[] getE() throws Exception;
+  void setF(byte[] f);
+  byte[] getK() throws Exception;
+}
diff --git a/java/com/jcraft/jsch/DHG1.java b/java/com/jcraft/jsch/DHG1.java
new file mode 100644
index 0000000..9b9a860
--- /dev/null
+++ b/java/com/jcraft/jsch/DHG1.java
@@ -0,0 +1,310 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class DHG1 extends KeyExchange{
+
+  static final byte[] g={ 2 };
+  static final byte[] p={
+(byte)0x00,
+(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF, 
+(byte)0xC9,(byte)0x0F,(byte)0xDA,(byte)0xA2,(byte)0x21,(byte)0x68,(byte)0xC2,(byte)0x34,
+(byte)0xC4,(byte)0xC6,(byte)0x62,(byte)0x8B,(byte)0x80,(byte)0xDC,(byte)0x1C,(byte)0xD1,
+(byte)0x29,(byte)0x02,(byte)0x4E,(byte)0x08,(byte)0x8A,(byte)0x67,(byte)0xCC,(byte)0x74,
+(byte)0x02,(byte)0x0B,(byte)0xBE,(byte)0xA6,(byte)0x3B,(byte)0x13,(byte)0x9B,(byte)0x22,
+(byte)0x51,(byte)0x4A,(byte)0x08,(byte)0x79,(byte)0x8E,(byte)0x34,(byte)0x04,(byte)0xDD,
+(byte)0xEF,(byte)0x95,(byte)0x19,(byte)0xB3,(byte)0xCD,(byte)0x3A,(byte)0x43,(byte)0x1B,
+(byte)0x30,(byte)0x2B,(byte)0x0A,(byte)0x6D,(byte)0xF2,(byte)0x5F,(byte)0x14,(byte)0x37,
+(byte)0x4F,(byte)0xE1,(byte)0x35,(byte)0x6D,(byte)0x6D,(byte)0x51,(byte)0xC2,(byte)0x45,
+(byte)0xE4,(byte)0x85,(byte)0xB5,(byte)0x76,(byte)0x62,(byte)0x5E,(byte)0x7E,(byte)0xC6,
+(byte)0xF4,(byte)0x4C,(byte)0x42,(byte)0xE9,(byte)0xA6,(byte)0x37,(byte)0xED,(byte)0x6B,
+(byte)0x0B,(byte)0xFF,(byte)0x5C,(byte)0xB6,(byte)0xF4,(byte)0x06,(byte)0xB7,(byte)0xED,
+(byte)0xEE,(byte)0x38,(byte)0x6B,(byte)0xFB,(byte)0x5A,(byte)0x89,(byte)0x9F,(byte)0xA5,
+(byte)0xAE,(byte)0x9F,(byte)0x24,(byte)0x11,(byte)0x7C,(byte)0x4B,(byte)0x1F,(byte)0xE6,
+(byte)0x49,(byte)0x28,(byte)0x66,(byte)0x51,(byte)0xEC,(byte)0xE6,(byte)0x53,(byte)0x81,
+(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF
+};
+
+  private static final int SSH_MSG_KEXDH_INIT=                     30;
+  private static final int SSH_MSG_KEXDH_REPLY=                    31;
+
+  static final int RSA=0;
+  static final int DSS=1;
+  private int type=0;
+
+  private int state;
+
+  DH dh;
+//  HASH sha;
+
+//  byte[] K;
+//  byte[] H;
+
+  byte[] V_S;
+  byte[] V_C;
+  byte[] I_S;
+  byte[] I_C;
+
+//  byte[] K_S;
+
+  byte[] e;
+
+  private Buffer buf;
+  private Packet packet;
+
+  public void init(Session session,
+		   byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{
+    this.session=session;
+    this.V_S=V_S;      
+    this.V_C=V_C;      
+    this.I_S=I_S;      
+    this.I_C=I_C;      
+
+//    sha=new SHA1();
+//    sha.init();
+    try{
+      Class c=Class.forName(session.getConfig("sha-1"));
+      sha=(HASH)(c.newInstance());
+      sha.init();
+    }
+    catch(Exception e){
+      System.err.println(e);
+    }
+
+    buf=new Buffer();
+    packet=new Packet(buf);
+
+    try{
+      Class c=Class.forName(session.getConfig("dh"));
+      dh=(DH)(c.newInstance());
+      dh.init();
+    }
+    catch(Exception e){
+      //System.err.println(e);
+      throw e;
+    }
+
+    dh.setP(p);
+    dh.setG(g);
+
+    // The client responds with:
+    // byte  SSH_MSG_KEXDH_INIT(30)
+    // mpint e <- g^x mod p
+    //         x is a random number (1 < x < (p-1)/2)
+
+    e=dh.getE();
+
+    packet.reset();
+    buf.putByte((byte)SSH_MSG_KEXDH_INIT);
+    buf.putMPInt(e);
+    session.write(packet);
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "SSH_MSG_KEXDH_INIT sent");
+      JSch.getLogger().log(Logger.INFO, 
+                           "expecting SSH_MSG_KEXDH_REPLY");
+    }
+
+    state=SSH_MSG_KEXDH_REPLY;
+  }
+
+  public boolean next(Buffer _buf) throws Exception{
+    int i,j;
+
+    switch(state){
+    case SSH_MSG_KEXDH_REPLY:
+      // The server responds with:
+      // byte      SSH_MSG_KEXDH_REPLY(31)
+      // string    server public host key and certificates (K_S)
+      // mpint     f
+      // string    signature of H
+      j=_buf.getInt();
+      j=_buf.getByte();
+      j=_buf.getByte();
+      if(j!=31){
+	System.err.println("type: must be 31 "+j);
+	return false;
+      }
+
+      K_S=_buf.getString();
+      // K_S is server_key_blob, which includes ....
+      // string ssh-dss
+      // impint p of dsa
+      // impint q of dsa
+      // impint g of dsa
+      // impint pub_key of dsa
+      //System.err.print("K_S: "); //dump(K_S, 0, K_S.length);
+      byte[] f=_buf.getMPInt();
+      byte[] sig_of_H=_buf.getString();
+      /*
+for(int ii=0; ii<sig_of_H.length;ii++){
+  System.err.print(Integer.toHexString(sig_of_H[ii]&0xff));
+  System.err.print(": ");
+}
+System.err.println("");
+      */
+
+      dh.setF(f);
+      K=dh.getK();
+
+      //The hash H is computed as the HASH hash of the concatenation of the
+      //following:
+      // string    V_C, the client's version string (CR and NL excluded)
+      // string    V_S, the server's version string (CR and NL excluded)
+      // string    I_C, the payload of the client's SSH_MSG_KEXINIT
+      // string    I_S, the payload of the server's SSH_MSG_KEXINIT
+      // string    K_S, the host key
+      // mpint     e, exchange value sent by the client
+      // mpint     f, exchange value sent by the server
+      // mpint     K, the shared secret
+      // This value is called the exchange hash, and it is used to authenti-
+      // cate the key exchange.
+      buf.reset();
+      buf.putString(V_C); buf.putString(V_S);
+      buf.putString(I_C); buf.putString(I_S);
+      buf.putString(K_S);
+      buf.putMPInt(e); buf.putMPInt(f);
+      buf.putMPInt(K);
+      byte[] foo=new byte[buf.getLength()];
+      buf.getByte(foo);
+      sha.update(foo, 0, foo.length);
+      H=sha.digest();
+      //System.err.print("H -> "); //dump(H, 0, H.length);
+
+      i=0;
+      j=0;
+      j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+      String alg=Util.byte2str(K_S, i, j);
+      i+=j;
+
+      boolean result=false;
+
+      if(alg.equals("ssh-rsa")){
+	byte[] tmp;
+	byte[] ee;
+	byte[] n;
+
+	type=RSA;
+
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	ee=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	n=tmp;
+	
+//	SignatureRSA sig=new SignatureRSA();
+//	sig.init();
+
+	SignatureRSA sig=null;
+	try{
+	  Class c=Class.forName(session.getConfig("signature.rsa"));
+	  sig=(SignatureRSA)(c.newInstance());
+	  sig.init();
+	}
+	catch(Exception e){
+	  System.err.println(e);
+	}
+
+	sig.setPubKey(ee, n);   
+	sig.update(H);
+	result=sig.verify(sig_of_H);
+
+        if(JSch.getLogger().isEnabled(Logger.INFO)){
+          JSch.getLogger().log(Logger.INFO, 
+                               "ssh_rsa_verify: signature "+result);
+        }
+
+      }
+      else if(alg.equals("ssh-dss")){
+	byte[] q=null;
+	byte[] tmp;
+	byte[] p;
+	byte[] g;
+      
+	type=DSS;
+
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	p=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	q=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	g=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	f=tmp;
+//	SignatureDSA sig=new SignatureDSA();
+//	sig.init();
+	SignatureDSA sig=null;
+	try{
+	  Class c=Class.forName(session.getConfig("signature.dss"));
+	  sig=(SignatureDSA)(c.newInstance());
+	  sig.init();
+	}
+	catch(Exception e){
+	  System.err.println(e);
+	}
+	sig.setPubKey(f, p, q, g);   
+	sig.update(H);
+	result=sig.verify(sig_of_H);
+
+        if(JSch.getLogger().isEnabled(Logger.INFO)){
+          JSch.getLogger().log(Logger.INFO, 
+                               "ssh_dss_verify: signature "+result);
+        }
+
+      }
+      else{
+	System.err.println("unknown alg");
+      }	    
+      state=STATE_END;
+      return result;
+    }
+    return false;
+  }
+
+  public String getKeyType(){
+    if(type==DSS) return "DSA";
+    return "RSA";
+  }
+
+  public int getState(){return state; }
+}
diff --git a/java/com/jcraft/jsch/DHG14.java b/java/com/jcraft/jsch/DHG14.java
new file mode 100644
index 0000000..8ef5fb7
--- /dev/null
+++ b/java/com/jcraft/jsch/DHG14.java
@@ -0,0 +1,310 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class DHG14 extends KeyExchange{
+
+  static final byte[] g={ 2 };
+  static final byte[] p={
+(byte)0x00,
+(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
+(byte)0xC9,(byte)0x0F,(byte)0xDA,(byte)0xA2,(byte)0x21,(byte)0x68,(byte)0xC2,(byte)0x34,
+(byte)0xC4,(byte)0xC6,(byte)0x62,(byte)0x8B,(byte)0x80,(byte)0xDC,(byte)0x1C,(byte)0xD1,
+(byte)0x29,(byte)0x02,(byte)0x4E,(byte)0x08,(byte)0x8A,(byte)0x67,(byte)0xCC,(byte)0x74,
+(byte)0x02,(byte)0x0B,(byte)0xBE,(byte)0xA6,(byte)0x3B,(byte)0x13,(byte)0x9B,(byte)0x22,
+(byte)0x51,(byte)0x4A,(byte)0x08,(byte)0x79,(byte)0x8E,(byte)0x34,(byte)0x04,(byte)0xDD,
+(byte)0xEF,(byte)0x95,(byte)0x19,(byte)0xB3,(byte)0xCD,(byte)0x3A,(byte)0x43,(byte)0x1B,
+(byte)0x30,(byte)0x2B,(byte)0x0A,(byte)0x6D,(byte)0xF2,(byte)0x5F,(byte)0x14,(byte)0x37,
+(byte)0x4F,(byte)0xE1,(byte)0x35,(byte)0x6D,(byte)0x6D,(byte)0x51,(byte)0xC2,(byte)0x45,
+(byte)0xE4,(byte)0x85,(byte)0xB5,(byte)0x76,(byte)0x62,(byte)0x5E,(byte)0x7E,(byte)0xC6,
+(byte)0xF4,(byte)0x4C,(byte)0x42,(byte)0xE9,(byte)0xA6,(byte)0x37,(byte)0xED,(byte)0x6B,
+(byte)0x0B,(byte)0xFF,(byte)0x5C,(byte)0xB6,(byte)0xF4,(byte)0x06,(byte)0xB7,(byte)0xED,
+(byte)0xEE,(byte)0x38,(byte)0x6B,(byte)0xFB,(byte)0x5A,(byte)0x89,(byte)0x9F,(byte)0xA5,
+(byte)0xAE,(byte)0x9F,(byte)0x24,(byte)0x11,(byte)0x7C,(byte)0x4B,(byte)0x1F,(byte)0xE6,
+(byte)0x49,(byte)0x28,(byte)0x66,(byte)0x51,(byte)0xEC,(byte)0xE4,(byte)0x5B,(byte)0x3D,
+(byte)0xC2,(byte)0x00,(byte)0x7C,(byte)0xB8,(byte)0xA1,(byte)0x63,(byte)0xBF,(byte)0x05,
+(byte)0x98,(byte)0xDA,(byte)0x48,(byte)0x36,(byte)0x1C,(byte)0x55,(byte)0xD3,(byte)0x9A,
+(byte)0x69,(byte)0x16,(byte)0x3F,(byte)0xA8,(byte)0xFD,(byte)0x24,(byte)0xCF,(byte)0x5F,
+(byte)0x83,(byte)0x65,(byte)0x5D,(byte)0x23,(byte)0xDC,(byte)0xA3,(byte)0xAD,(byte)0x96,
+(byte)0x1C,(byte)0x62,(byte)0xF3,(byte)0x56,(byte)0x20,(byte)0x85,(byte)0x52,(byte)0xBB,
+(byte)0x9E,(byte)0xD5,(byte)0x29,(byte)0x07,(byte)0x70,(byte)0x96,(byte)0x96,(byte)0x6D,
+(byte)0x67,(byte)0x0C,(byte)0x35,(byte)0x4E,(byte)0x4A,(byte)0xBC,(byte)0x98,(byte)0x04,
+(byte)0xF1,(byte)0x74,(byte)0x6C,(byte)0x08,(byte)0xCA,(byte)0x18,(byte)0x21,(byte)0x7C,
+(byte)0x32,(byte)0x90,(byte)0x5E,(byte)0x46,(byte)0x2E,(byte)0x36,(byte)0xCE,(byte)0x3B,
+(byte)0xE3,(byte)0x9E,(byte)0x77,(byte)0x2C,(byte)0x18,(byte)0x0E,(byte)0x86,(byte)0x03,
+(byte)0x9B,(byte)0x27,(byte)0x83,(byte)0xA2,(byte)0xEC,(byte)0x07,(byte)0xA2,(byte)0x8F,
+(byte)0xB5,(byte)0xC5,(byte)0x5D,(byte)0xF0,(byte)0x6F,(byte)0x4C,(byte)0x52,(byte)0xC9,
+(byte)0xDE,(byte)0x2B,(byte)0xCB,(byte)0xF6,(byte)0x95,(byte)0x58,(byte)0x17,(byte)0x18,
+(byte)0x39,(byte)0x95,(byte)0x49,(byte)0x7C,(byte)0xEA,(byte)0x95,(byte)0x6A,(byte)0xE5,
+(byte)0x15,(byte)0xD2,(byte)0x26,(byte)0x18,(byte)0x98,(byte)0xFA,(byte)0x05,(byte)0x10,
+(byte)0x15,(byte)0x72,(byte)0x8E,(byte)0x5A,(byte)0x8A,(byte)0xAC,(byte)0xAA,(byte)0x68,
+(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF
+};
+
+  private static final int SSH_MSG_KEXDH_INIT=                     30;
+  private static final int SSH_MSG_KEXDH_REPLY=                    31;
+
+  static final int RSA=0;
+  static final int DSS=1;
+  private int type=0;
+
+  private int state;
+
+  DH dh;
+
+  byte[] V_S;
+  byte[] V_C;
+  byte[] I_S;
+  byte[] I_C;
+
+  byte[] e;
+
+  private Buffer buf;
+  private Packet packet;
+
+  public void init(Session session,
+		   byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{
+    this.session=session;
+    this.V_S=V_S;      
+    this.V_C=V_C;      
+    this.I_S=I_S;      
+    this.I_C=I_C;      
+
+    try{
+      Class c=Class.forName(session.getConfig("sha-1"));
+      sha=(HASH)(c.newInstance());
+      sha.init();
+    }
+    catch(Exception e){
+      System.err.println(e);
+    }
+
+    buf=new Buffer();
+    packet=new Packet(buf);
+
+    try{
+      Class c=Class.forName(session.getConfig("dh"));
+      dh=(DH)(c.newInstance());
+      dh.init();
+    }
+    catch(Exception e){
+      //System.err.println(e);
+      throw e;
+    }
+
+    dh.setP(p);
+    dh.setG(g);
+    // The client responds with:
+    // byte  SSH_MSG_KEXDH_INIT(30)
+    // mpint e <- g^x mod p
+    //         x is a random number (1 < x < (p-1)/2)
+
+    e=dh.getE();
+    packet.reset();
+    buf.putByte((byte)SSH_MSG_KEXDH_INIT);
+    buf.putMPInt(e);
+
+    if(V_S==null){  // This is a really ugly hack for Session.checkKexes ;-(
+      return;
+    }
+
+    session.write(packet);
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "SSH_MSG_KEXDH_INIT sent");
+      JSch.getLogger().log(Logger.INFO, 
+                           "expecting SSH_MSG_KEXDH_REPLY");
+    }
+
+    state=SSH_MSG_KEXDH_REPLY;
+  }
+
+  public boolean next(Buffer _buf) throws Exception{
+    int i,j;
+
+    switch(state){
+    case SSH_MSG_KEXDH_REPLY:
+      // The server responds with:
+      // byte      SSH_MSG_KEXDH_REPLY(31)
+      // string    server public host key and certificates (K_S)
+      // mpint     f
+      // string    signature of H
+      j=_buf.getInt();
+      j=_buf.getByte();
+      j=_buf.getByte();
+      if(j!=31){
+	System.err.println("type: must be 31 "+j);
+	return false;
+      }
+
+      K_S=_buf.getString();
+      // K_S is server_key_blob, which includes ....
+      // string ssh-dss
+      // impint p of dsa
+      // impint q of dsa
+      // impint g of dsa
+      // impint pub_key of dsa
+      //System.err.print("K_S: "); //dump(K_S, 0, K_S.length);
+      byte[] f=_buf.getMPInt();
+      byte[] sig_of_H=_buf.getString();
+
+      dh.setF(f);
+      K=dh.getK();
+
+      //The hash H is computed as the HASH hash of the concatenation of the
+      //following:
+      // string    V_C, the client's version string (CR and NL excluded)
+      // string    V_S, the server's version string (CR and NL excluded)
+      // string    I_C, the payload of the client's SSH_MSG_KEXINIT
+      // string    I_S, the payload of the server's SSH_MSG_KEXINIT
+      // string    K_S, the host key
+      // mpint     e, exchange value sent by the client
+      // mpint     f, exchange value sent by the server
+      // mpint     K, the shared secret
+      // This value is called the exchange hash, and it is used to authenti-
+      // cate the key exchange.
+      buf.reset();
+      buf.putString(V_C); buf.putString(V_S);
+      buf.putString(I_C); buf.putString(I_S);
+      buf.putString(K_S);
+      buf.putMPInt(e); buf.putMPInt(f);
+      buf.putMPInt(K);
+      byte[] foo=new byte[buf.getLength()];
+      buf.getByte(foo);
+      sha.update(foo, 0, foo.length);
+      H=sha.digest();
+      //System.err.print("H -> "); //dump(H, 0, H.length);
+
+      i=0;
+      j=0;
+      j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+      String alg=Util.byte2str(K_S, i, j);
+      i+=j;
+
+      boolean result=false;
+
+      if(alg.equals("ssh-rsa")){
+	byte[] tmp;
+	byte[] ee;
+	byte[] n;
+
+	type=RSA;
+
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	ee=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	n=tmp;
+	
+	SignatureRSA sig=null;
+	try{
+	  Class c=Class.forName(session.getConfig("signature.rsa"));
+	  sig=(SignatureRSA)(c.newInstance());
+	  sig.init();
+	}
+	catch(Exception e){
+	  System.err.println(e);
+	}
+
+	sig.setPubKey(ee, n);   
+	sig.update(H);
+	result=sig.verify(sig_of_H);
+
+        if(JSch.getLogger().isEnabled(Logger.INFO)){
+          JSch.getLogger().log(Logger.INFO, 
+                               "ssh_rsa_verify: signature "+result);
+        }
+
+      }
+      else if(alg.equals("ssh-dss")){
+	byte[] q=null;
+	byte[] tmp;
+	byte[] p;
+	byte[] g;
+      
+	type=DSS;
+
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	p=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	q=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	g=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	f=tmp;
+
+	SignatureDSA sig=null;
+	try{
+	  Class c=Class.forName(session.getConfig("signature.dss"));
+	  sig=(SignatureDSA)(c.newInstance());
+	  sig.init();
+	}
+	catch(Exception e){
+	  System.err.println(e);
+	}
+	sig.setPubKey(f, p, q, g);   
+	sig.update(H);
+	result=sig.verify(sig_of_H);
+
+        if(JSch.getLogger().isEnabled(Logger.INFO)){
+          JSch.getLogger().log(Logger.INFO, 
+                               "ssh_dss_verify: signature "+result);
+        }
+
+      }
+      else{
+	System.err.println("unknown alg");
+      }	    
+      state=STATE_END;
+      return result;
+    }
+    return false;
+  }
+
+  public String getKeyType(){
+    if(type==DSS) return "DSA";
+    return "RSA";
+  }
+
+  public int getState(){return state; }
+}
diff --git a/java/com/jcraft/jsch/DHGEX.java b/java/com/jcraft/jsch/DHGEX.java
new file mode 100644
index 0000000..23fa9eb
--- /dev/null
+++ b/java/com/jcraft/jsch/DHGEX.java
@@ -0,0 +1,340 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class DHGEX extends KeyExchange{
+
+  private static final int SSH_MSG_KEX_DH_GEX_GROUP=               31;
+  private static final int SSH_MSG_KEX_DH_GEX_INIT=                32;
+  private static final int SSH_MSG_KEX_DH_GEX_REPLY=               33;
+  private static final int SSH_MSG_KEX_DH_GEX_REQUEST=             34;
+
+  static int min=1024;
+
+//  static int min=512;
+  static int preferred=1024;
+  static int max=1024;
+
+//  static int preferred=1024;
+//  static int max=2000;
+
+  static final int RSA=0;
+  static final int DSS=1;
+  private int type=0;
+
+  private int state;
+
+//  com.jcraft.jsch.DH dh;
+  DH dh;
+
+  byte[] V_S;
+  byte[] V_C;
+  byte[] I_S;
+  byte[] I_C;
+
+  private Buffer buf;
+  private Packet packet;
+
+  private byte[] p;
+  private byte[] g;
+  private byte[] e;
+  //private byte[] f;
+
+  public void init(Session session,
+		   byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{
+    this.session=session;
+    this.V_S=V_S;      
+    this.V_C=V_C;      
+    this.I_S=I_S;      
+    this.I_C=I_C;      
+
+    try{
+      Class c=Class.forName(session.getConfig("sha-1"));
+      sha=(HASH)(c.newInstance());
+      sha.init();
+    }
+    catch(Exception e){
+      System.err.println(e);
+    }
+
+    buf=new Buffer();
+    packet=new Packet(buf);
+
+    try{
+      Class c=Class.forName(session.getConfig("dh"));
+      dh=(com.jcraft.jsch.DH)(c.newInstance());
+      dh.init();
+    }
+    catch(Exception e){
+//      System.err.println(e);
+      throw e;
+    }
+
+    packet.reset();
+    buf.putByte((byte)SSH_MSG_KEX_DH_GEX_REQUEST);
+    buf.putInt(min);
+    buf.putInt(preferred);
+    buf.putInt(max);
+    session.write(packet); 
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "SSH_MSG_KEX_DH_GEX_REQUEST("+min+"<"+preferred+"<"+max+") sent");
+      JSch.getLogger().log(Logger.INFO, 
+                           "expecting SSH_MSG_KEX_DH_GEX_GROUP");
+    }
+
+    state=SSH_MSG_KEX_DH_GEX_GROUP;
+  }
+
+  public boolean next(Buffer _buf) throws Exception{
+    int i,j;
+    switch(state){
+    case SSH_MSG_KEX_DH_GEX_GROUP:
+      // byte  SSH_MSG_KEX_DH_GEX_GROUP(31)
+      // mpint p, safe prime
+      // mpint g, generator for subgroup in GF (p)
+      _buf.getInt();
+      _buf.getByte();
+      j=_buf.getByte();
+      if(j!=SSH_MSG_KEX_DH_GEX_GROUP){
+	System.err.println("type: must be SSH_MSG_KEX_DH_GEX_GROUP "+j);
+	return false;
+      }
+
+      p=_buf.getMPInt();
+      g=_buf.getMPInt();
+      /*
+for(int iii=0; iii<p.length; iii++){
+System.err.println("0x"+Integer.toHexString(p[iii]&0xff)+",");
+}
+System.err.println("");
+for(int iii=0; iii<g.length; iii++){
+System.err.println("0x"+Integer.toHexString(g[iii]&0xff)+",");
+}
+      */
+      dh.setP(p);
+      dh.setG(g);
+
+      // The client responds with:
+      // byte  SSH_MSG_KEX_DH_GEX_INIT(32)
+      // mpint e <- g^x mod p
+      //         x is a random number (1 < x < (p-1)/2)
+
+      e=dh.getE();
+
+      packet.reset();
+      buf.putByte((byte)SSH_MSG_KEX_DH_GEX_INIT);
+      buf.putMPInt(e);
+      session.write(packet);
+
+      if(JSch.getLogger().isEnabled(Logger.INFO)){
+        JSch.getLogger().log(Logger.INFO, 
+                             "SSH_MSG_KEX_DH_GEX_INIT sent");
+        JSch.getLogger().log(Logger.INFO, 
+                             "expecting SSH_MSG_KEX_DH_GEX_REPLY");
+      }
+
+      state=SSH_MSG_KEX_DH_GEX_REPLY;
+      return true;
+      //break;
+
+    case SSH_MSG_KEX_DH_GEX_REPLY:
+      // The server responds with:
+      // byte      SSH_MSG_KEX_DH_GEX_REPLY(33)
+      // string    server public host key and certificates (K_S)
+      // mpint     f
+      // string    signature of H
+      j=_buf.getInt();
+      j=_buf.getByte();
+      j=_buf.getByte();
+      if(j!=SSH_MSG_KEX_DH_GEX_REPLY){
+	System.err.println("type: must be SSH_MSG_KEX_DH_GEX_REPLY "+j);
+	return false;
+      }
+
+      K_S=_buf.getString();
+      // K_S is server_key_blob, which includes ....
+      // string ssh-dss
+      // impint p of dsa
+      // impint q of dsa
+      // impint g of dsa
+      // impint pub_key of dsa
+      //System.err.print("K_S: "); dump(K_S, 0, K_S.length);
+
+      byte[] f=_buf.getMPInt();
+      byte[] sig_of_H=_buf.getString();
+
+      dh.setF(f);
+      K=dh.getK();
+
+      //The hash H is computed as the HASH hash of the concatenation of the
+      //following:
+      // string    V_C, the client's version string (CR and NL excluded)
+      // string    V_S, the server's version string (CR and NL excluded)
+      // string    I_C, the payload of the client's SSH_MSG_KEXINIT
+      // string    I_S, the payload of the server's SSH_MSG_KEXINIT
+      // string    K_S, the host key
+      // uint32    min, minimal size in bits of an acceptable group
+      // uint32   n, preferred size in bits of the group the server should send
+      // uint32    max, maximal size in bits of an acceptable group
+      // mpint     p, safe prime
+      // mpint     g, generator for subgroup
+      // mpint     e, exchange value sent by the client
+      // mpint     f, exchange value sent by the server
+      // mpint     K, the shared secret
+      // This value is called the exchange hash, and it is used to authenti-
+      // cate the key exchange.
+
+      buf.reset();
+      buf.putString(V_C); buf.putString(V_S);
+      buf.putString(I_C); buf.putString(I_S);
+      buf.putString(K_S);
+      buf.putInt(min); buf.putInt(preferred); buf.putInt(max);
+      buf.putMPInt(p); buf.putMPInt(g); buf.putMPInt(e); buf.putMPInt(f);
+      buf.putMPInt(K);
+
+      byte[] foo=new byte[buf.getLength()];
+      buf.getByte(foo);
+      sha.update(foo, 0, foo.length);
+
+      H=sha.digest();
+
+      // System.err.print("H -> "); dump(H, 0, H.length);
+
+      i=0;
+      j=0;
+      j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+      String alg=Util.byte2str(K_S, i, j);
+      i+=j;
+
+      boolean result=false;
+      if(alg.equals("ssh-rsa")){
+	byte[] tmp;
+	byte[] ee;
+	byte[] n;
+	
+	type=RSA;
+
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	ee=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	n=tmp;
+
+//	SignatureRSA sig=new SignatureRSA();
+//	sig.init();
+
+	SignatureRSA sig=null;
+	try{
+	  Class c=Class.forName(session.getConfig("signature.rsa"));
+	  sig=(SignatureRSA)(c.newInstance());
+	  sig.init();
+	}
+	catch(Exception e){
+	  System.err.println(e);
+	}
+
+	sig.setPubKey(ee, n);   
+	sig.update(H);
+	result=sig.verify(sig_of_H);
+
+        if(JSch.getLogger().isEnabled(Logger.INFO)){
+          JSch.getLogger().log(Logger.INFO, 
+                               "ssh_rsa_verify: signature "+result);
+        }
+
+      }
+      else if(alg.equals("ssh-dss")){
+	byte[] q=null;
+	byte[] tmp;
+
+	type=DSS;
+
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	p=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	q=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	g=tmp;
+	j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
+	  ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
+	tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
+	f=tmp;
+	
+//	SignatureDSA sig=new SignatureDSA();
+//	sig.init();
+
+	SignatureDSA sig=null;
+	try{
+	  Class c=Class.forName(session.getConfig("signature.dss"));
+	  sig=(SignatureDSA)(c.newInstance());
+	  sig.init();
+	}
+	catch(Exception e){
+	  System.err.println(e);
+	}
+
+	sig.setPubKey(f, p, q, g);   
+	sig.update(H);
+	result=sig.verify(sig_of_H);
+
+        if(JSch.getLogger().isEnabled(Logger.INFO)){
+          JSch.getLogger().log(Logger.INFO, 
+                               "ssh_dss_verify: signature "+result);
+        }
+
+      }
+      else{
+	System.err.println("unknown alg");
+      }	    
+      state=STATE_END;
+      return result;
+    }
+    return false;
+  }
+
+  public String getKeyType(){
+    if(type==DSS) return "DSA";
+    return "RSA";
+  }
+
+  public int getState(){return state; }
+}
diff --git a/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java b/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java
new file mode 100644
index 0000000..4176aef
--- /dev/null
+++ b/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java
@@ -0,0 +1,36 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+import java.io.*;
+
+public interface ForwardedTCPIPDaemon extends Runnable{
+  void setChannel(ChannelForwardedTCPIP channel, InputStream in, OutputStream out);
+  void setArg(Object[] arg);
+}
diff --git a/java/com/jcraft/jsch/GSSContext.java b/java/com/jcraft/jsch/GSSContext.java
new file mode 100644
index 0000000..23c58ee
--- /dev/null
+++ b/java/com/jcraft/jsch/GSSContext.java
@@ -0,0 +1,38 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2004-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface GSSContext{
+  public void create(String user, String host) throws JSchException;
+  public boolean isEstablished();
+  public byte[] init(byte[] token, int s, int l) throws JSchException;
+  public byte[] getMIC(byte[] message, int s, int l);
+  public void dispose();
+}
diff --git a/java/com/jcraft/jsch/HASH.java b/java/com/jcraft/jsch/HASH.java
new file mode 100644
index 0000000..6bfff88
--- /dev/null
+++ b/java/com/jcraft/jsch/HASH.java
@@ -0,0 +1,37 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface HASH{
+  void init() throws Exception;
+  int getBlockSize();
+  void update(byte[] foo, int start, int len) throws Exception;
+  byte[] digest() throws Exception;
+}
diff --git a/java/com/jcraft/jsch/HostKey.java b/java/com/jcraft/jsch/HostKey.java
new file mode 100644
index 0000000..6401ad2
--- /dev/null
+++ b/java/com/jcraft/jsch/HostKey.java
@@ -0,0 +1,104 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class HostKey{
+  private static final byte[] sshdss=Util.str2byte("ssh-dss");
+  private static final byte[] sshrsa=Util.str2byte("ssh-rsa");
+
+  protected static final int GUESS=0;
+  public static final int SSHDSS=1;
+  public static final int SSHRSA=2;
+  static final int UNKNOWN=3;
+
+  protected String host;
+  protected int type;
+  protected byte[] key;
+
+  public HostKey(String host, byte[] key) throws JSchException {
+    this(host, GUESS, key);
+  }
+
+  public HostKey(String host, int type, byte[] key) throws JSchException {
+    this.host=host; 
+    if(type==GUESS){
+      if(key[8]=='d'){ this.type=SSHDSS; }
+      else if(key[8]=='r'){ this.type=SSHRSA; }
+      else { throw new JSchException("invalid key type");}
+    }
+    else{
+      this.type=type; 
+    }
+    this.key=key;
+  }
+
+  public String getHost(){ return host; }
+  public String getType(){
+    if(type==SSHDSS){ return Util.byte2str(sshdss); }
+    if(type==SSHRSA){ return Util.byte2str(sshrsa);}
+    return "UNKNOWN";
+  }
+  public String getKey(){
+    return Util.byte2str(Util.toBase64(key, 0, key.length));
+  }
+  public String getFingerPrint(JSch jsch){
+    HASH hash=null;
+    try{
+      Class c=Class.forName(jsch.getConfig("md5"));
+      hash=(HASH)(c.newInstance());
+    }
+    catch(Exception e){ System.err.println("getFingerPrint: "+e); }
+    return Util.getFingerPrint(hash, key);
+  }
+
+  boolean isMatched(String _host){
+    return isIncluded(_host);
+  }
+
+  private boolean isIncluded(String _host){
+    int i=0;
+    String hosts=this.host; 
+    int hostslen=hosts.length();
+    int hostlen=_host.length();
+    int j;
+    while(i<hostslen){
+      j=hosts.indexOf(',', i);
+      if(j==-1){
+       if(hostlen!=hostslen-i) return false;
+       return hosts.regionMatches(true, i, _host, 0, hostlen);
+      }
+      if(hostlen==(j-i)){
+	if(hosts.regionMatches(true, i, _host, 0, hostlen)) return true;
+      }
+      i=j+1;
+    }
+    return false;
+  }
+}
diff --git a/java/com/jcraft/jsch/HostKeyRepository.java b/java/com/jcraft/jsch/HostKeyRepository.java
new file mode 100644
index 0000000..adfe633
--- /dev/null
+++ b/java/com/jcraft/jsch/HostKeyRepository.java
@@ -0,0 +1,44 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2004-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface HostKeyRepository{
+  final int OK=0;
+  final int NOT_INCLUDED=1;
+  final int CHANGED=2;
+
+  int check(String host, byte[] key);
+  void add(HostKey hostkey, UserInfo ui);
+  void remove(String host, String type);
+  void remove(String host, String type, byte[] key);
+  String getKnownHostsRepositoryID();
+  HostKey[] getHostKey();
+  HostKey[] getHostKey(String host, String type);
+}
diff --git a/java/com/jcraft/jsch/IO.java b/java/com/jcraft/jsch/IO.java
new file mode 100644
index 0000000..65535ba
--- /dev/null
+++ b/java/com/jcraft/jsch/IO.java
@@ -0,0 +1,132 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.*;
+
+public class IO{
+  InputStream in;
+  OutputStream out;
+  OutputStream out_ext;
+
+  private boolean in_dontclose=false;
+  private boolean out_dontclose=false;
+  private boolean out_ext_dontclose=false;
+
+  void setOutputStream(OutputStream out){ this.out=out; }
+  void setOutputStream(OutputStream out, boolean dontclose){
+    this.out_dontclose=dontclose;
+    setOutputStream(out);
+  }
+  void setExtOutputStream(OutputStream out){ this.out_ext=out; }
+  void setExtOutputStream(OutputStream out, boolean dontclose){
+    this.out_ext_dontclose=dontclose;
+    setExtOutputStream(out);
+  }
+  void setInputStream(InputStream in){ this.in=in; }
+  void setInputStream(InputStream in, boolean dontclose){
+    this.in_dontclose=dontclose;
+    setInputStream(in);
+  }
+
+  public void put(Packet p) throws IOException, java.net.SocketException {
+    out.write(p.buffer.buffer, 0, p.buffer.index);
+    out.flush();
+  }
+  void put(byte[] array, int begin, int length) throws IOException {
+    out.write(array, begin, length);
+    out.flush();
+  }
+  void put_ext(byte[] array, int begin, int length) throws IOException {
+    out_ext.write(array, begin, length);
+    out_ext.flush();
+  }
+
+  int getByte() throws IOException {
+    return in.read();
+  }
+
+  void getByte(byte[] array) throws IOException {
+    getByte(array, 0, array.length);
+  }
+
+  void getByte(byte[] array, int begin, int length) throws IOException {
+    do{
+      int completed = in.read(array, begin, length);
+      if(completed<0){
+	throw new IOException("End of IO Stream Read");
+      }
+      begin+=completed;
+      length-=completed;
+    }
+    while (length>0);
+  }
+
+  void out_close(){
+    try{
+      if(out!=null && !out_dontclose) out.close();
+      out=null;
+    }
+    catch(Exception ee){}
+  }
+
+  public void close(){
+    try{
+      if(in!=null && !in_dontclose) in.close();
+      in=null;
+    }
+    catch(Exception ee){}
+
+    out_close();
+
+    try{
+      if(out_ext!=null && !out_ext_dontclose) out_ext.close();
+      out_ext=null;
+    }
+    catch(Exception ee){}
+  }
+
+  /*
+  public void finalize() throws Throwable{
+    try{
+      if(in!=null) in.close();
+    }
+    catch(Exception ee){}
+    try{
+      if(out!=null) out.close();
+    }
+    catch(Exception ee){}
+    try{
+      if(out_ext!=null) out_ext.close();
+    }
+    catch(Exception ee){}
+  }
+  */
+}
diff --git a/java/com/jcraft/jsch/Identity.java b/java/com/jcraft/jsch/Identity.java
new file mode 100644
index 0000000..2f8cb80
--- /dev/null
+++ b/java/com/jcraft/jsch/Identity.java
@@ -0,0 +1,41 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface Identity{
+  public boolean setPassphrase(byte[] passphrase) throws JSchException;
+  public byte[] getPublicKeyBlob();
+  public byte[] getSignature(byte[] data);
+  public boolean decrypt();
+  public String getAlgName();
+  public String getName();
+  public boolean isEncrypted();
+  public void clear();
+}
diff --git a/java/com/jcraft/jsch/IdentityFile.java b/java/com/jcraft/jsch/IdentityFile.java
new file mode 100644
index 0000000..0427dab
--- /dev/null
+++ b/java/com/jcraft/jsch/IdentityFile.java
@@ -0,0 +1,955 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.*;
+
+class IdentityFile implements Identity{
+  String identity;
+  byte[] key;
+  byte[] iv;
+  private JSch jsch;
+  private HASH hash;
+  private byte[] encoded_data;
+
+  private Cipher cipher;
+
+  // DSA
+  private byte[] P_array;    
+  private byte[] Q_array;    
+  private byte[] G_array;    
+  private byte[] pub_array;    
+  private byte[] prv_array;    
+ 
+  // RSA
+  private  byte[] n_array;   // modulus
+  private  byte[] e_array;   // public exponent
+  private  byte[] d_array;   // private exponent
+ 
+//  private String algname="ssh-dss";
+  private String algname="ssh-rsa";
+
+  private static final int ERROR=0;
+  private static final int RSA=1;
+  private static final int DSS=2;
+  private static final int UNKNOWN=3;
+
+  private static final int OPENSSH=0;
+  private static final int FSECURE=1;
+  private static final int PUTTY=2;
+
+  private int type=ERROR;
+  private int keytype=OPENSSH;
+
+  private byte[] publickeyblob=null;
+
+  private boolean encrypted=true;
+
+  static IdentityFile newInstance(String prvfile, String pubfile, JSch jsch) throws JSchException{
+    byte[] prvkey=null;
+    byte[] pubkey=null;
+
+    File file=null;
+    FileInputStream fis=null;
+    try{
+      file=new File(prvfile);
+      fis=new FileInputStream(prvfile);
+      prvkey=new byte[(int)(file.length())];
+      int len=0;
+      while(true){
+        int i=fis.read(prvkey, len, prvkey.length-len);
+        if(i<=0)
+          break;
+        len+=i;
+      }
+      fis.close();
+    }
+    catch(Exception e){
+      try{ if(fis!=null) fis.close();}
+      catch(Exception ee){}
+      if(e instanceof Throwable)
+        throw new JSchException(e.toString(), (Throwable)e);
+      throw new JSchException(e.toString());
+    }
+
+    String _pubfile=pubfile;
+    if(pubfile==null){
+      _pubfile=prvfile+".pub";
+    }
+
+    try{
+      file=new File(_pubfile);
+      fis = new FileInputStream(_pubfile);
+      pubkey=new byte[(int)(file.length())];
+      int len=0;
+      while(true){
+        int i=fis.read(pubkey, len, pubkey.length-len);
+        if(i<=0)
+          break;
+        len+=i;
+      }
+      fis.close();
+    }
+    catch(Exception e){
+      try{ if(fis!=null) fis.close();}
+      catch(Exception ee){}
+      if(pubfile!=null){  
+        // The pubfile is explicitry given, but not accessible.
+        if(e instanceof Throwable)
+          throw new JSchException(e.toString(), (Throwable)e);
+        throw new JSchException(e.toString());
+      }
+    }
+    return newInstance(prvfile, prvkey, pubkey, jsch);
+  }
+
+  static IdentityFile newInstance(String name, byte[] prvkey, byte[] pubkey, JSch jsch) throws JSchException{
+    try{
+      return new IdentityFile(name, prvkey, pubkey, jsch);
+    }
+    finally{
+      Util.bzero(prvkey);
+    }
+  }
+
+  private IdentityFile(String name, byte[] prvkey, byte[] pubkey, JSch jsch) throws JSchException{
+    this.identity=name;
+    this.jsch=jsch;
+
+    // prvkey from "ssh-add" command on the remote.
+    if(pubkey==null &&
+       prvkey!=null && 
+       (prvkey.length>11 &&
+        prvkey[0]==0 && prvkey[1]==0 && prvkey[2]==0 && prvkey[3]==7)){
+
+      Buffer buf=new Buffer(prvkey);
+      String _type = new String(buf.getString()); // ssh-rsa
+
+      if(_type.equals("ssh-rsa")){
+        type=RSA;
+        n_array=buf.getString();
+        e_array=buf.getString();
+        d_array=buf.getString();
+        buf.getString();
+        buf.getString();
+        buf.getString();
+        this.identity += new String(buf.getString());
+      }
+      else if(_type.equals("ssh-dss")){
+        type=DSS;
+        P_array=buf.getString();
+        Q_array=buf.getString();
+        G_array=buf.getString();
+        pub_array=buf.getString();
+        prv_array=buf.getString();
+        this.identity += new String(buf.getString());
+      }
+      else{
+        throw new JSchException("privatekey: invalid key "+new String(prvkey, 4, 7));
+      }
+      encoded_data=prvkey;
+      encrypted=false;
+      keytype=OPENSSH;
+      return;
+    }
+
+    /* TODO: IdentityFile should use KeyPair.
+     * The following logic exists also in KeyPair. It is redundant.
+     */
+    try{
+      Class c;
+      c=Class.forName((String)jsch.getConfig("3des-cbc"));
+      cipher=(Cipher)(c.newInstance());
+      key=new byte[cipher.getBlockSize()];   // 24
+      iv=new byte[cipher.getIVSize()];       // 8
+      c=Class.forName((String)jsch.getConfig("md5"));
+      hash=(HASH)(c.newInstance());
+      hash.init();
+
+      byte[] buf=prvkey;
+      int len=buf.length;
+
+      int i=0;
+
+      while(i<len){
+        if(buf[i] == '-' && i+4<len && 
+           buf[i+1] == '-' && buf[i+2] == '-' && 
+           buf[i+3] == '-' && buf[i+4] == '-'){
+          break;
+        }
+        i++;
+      }
+
+      while(i<len){
+        if(buf[i]=='B'&& i+3<len && buf[i+1]=='E'&& buf[i+2]=='G'&& buf[i+3]=='I'){
+          i+=6;	    
+          if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSS; }
+	  else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; }
+	  else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H'){ // FSecure
+	    type=UNKNOWN;
+	    keytype=FSECURE;
+	  }
+	  else{
+            //System.err.println("invalid format: "+identity);
+	    throw new JSchException("invalid privatekey: "+identity);
+	  }
+          i+=3;
+	  continue;
+	}
+        if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && 
+           buf[i+4]=='2'&& buf[i+5]=='5'&& buf[i+6]=='6'&& buf[i+7]=='-'){
+          i+=8;
+          if(Session.checkCipher((String)jsch.getConfig("aes256-cbc"))){
+            c=Class.forName((String)jsch.getConfig("aes256-cbc"));
+            cipher=(Cipher)(c.newInstance());
+            key=new byte[cipher.getBlockSize()];
+            iv=new byte[cipher.getIVSize()];
+          }
+          else{
+            throw new JSchException("privatekey: aes256-cbc is not available "+identity);
+          }
+          continue;
+        }
+        if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && 
+           buf[i+4]=='1'&& buf[i+5]=='9'&& buf[i+6]=='2'&& buf[i+7]=='-'){
+          i+=8;
+          if(Session.checkCipher((String)jsch.getConfig("aes192-cbc"))){
+            c=Class.forName((String)jsch.getConfig("aes192-cbc"));
+            cipher=(Cipher)(c.newInstance());
+            key=new byte[cipher.getBlockSize()];
+            iv=new byte[cipher.getIVSize()];
+          }
+          else{
+            throw new JSchException("privatekey: aes192-cbc is not available "+identity);
+          }
+          continue;
+        }
+        if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && 
+           buf[i+4]=='1'&& buf[i+5]=='2'&& buf[i+6]=='8'&& buf[i+7]=='-'){
+          i+=8;
+          if(Session.checkCipher((String)jsch.getConfig("aes128-cbc"))){
+            c=Class.forName((String)jsch.getConfig("aes128-cbc"));
+            cipher=(Cipher)(c.newInstance());
+            key=new byte[cipher.getBlockSize()];
+            iv=new byte[cipher.getIVSize()];
+          }
+          else{
+            throw new JSchException("privatekey: aes128-cbc is not available "+identity);
+          }
+          continue;
+        }
+        if(buf[i]=='C'&& i+3<len && buf[i+1]=='B'&& buf[i+2]=='C'&& buf[i+3]==','){
+          i+=4;
+	  for(int ii=0; ii<iv.length; ii++){
+            iv[ii]=(byte)(((a2b(buf[i++])<<4)&0xf0)+
+			  (a2b(buf[i++])&0xf));
+  	  }
+	  continue;
+	}
+	if(buf[i]==0x0d && i+1<len && buf[i+1]==0x0a){
+	  i++;
+	  continue;
+	}
+	if(buf[i]==0x0a && i+1<len){
+	  if(buf[i+1]==0x0a){ i+=2; break; }
+	  if(buf[i+1]==0x0d &&
+	     i+2<len && buf[i+2]==0x0a){
+	     i+=3; break;
+	  }
+	  boolean inheader=false;
+	  for(int j=i+1; j<len; j++){
+	    if(buf[j]==0x0a) break;
+	    //if(buf[j]==0x0d) break;
+	    if(buf[j]==':'){inheader=true; break;}
+	  }
+	  if(!inheader){
+	    i++; 
+	    encrypted=false;    // no passphrase
+	    break;
+	  }
+	}
+	i++;
+      }
+
+      if(type==ERROR){
+	throw new JSchException("invalid privatekey: "+identity);
+      }
+
+      int start=i;
+      while(i<len){
+        if(buf[i]==0x0a){
+	  boolean xd=(buf[i-1]==0x0d);
+          System.arraycopy(buf, i+1, 
+			   buf, 
+			   i-(xd ? 1 : 0), 
+			   len-i-1-(xd ? 1 : 0)
+			   );
+	  if(xd)len--;
+          len--;
+          continue;
+        }
+        if(buf[i]=='-'){  break; }
+        i++;
+      }
+      encoded_data=Util.fromBase64(buf, start, i-start);
+
+      if(encoded_data.length>4 &&            // FSecure
+	 encoded_data[0]==(byte)0x3f &&
+	 encoded_data[1]==(byte)0x6f &&
+	 encoded_data[2]==(byte)0xf9 &&
+	 encoded_data[3]==(byte)0xeb){
+
+	Buffer _buf=new Buffer(encoded_data);
+	_buf.getInt();  // 0x3f6ff9be
+	_buf.getInt();
+	byte[]_type=_buf.getString();
+	//System.err.println("type: "+new String(_type)); 
+	byte[] _cipher=_buf.getString();
+	String cipher=Util.byte2str(_cipher);
+	//System.err.println("cipher: "+cipher); 
+	if(cipher.equals("3des-cbc")){
+  	   _buf.getInt();
+	   byte[] foo=new byte[encoded_data.length-_buf.getOffSet()];
+	   _buf.getByte(foo);
+	   encoded_data=foo;
+	   encrypted=true;
+	   throw new JSchException("unknown privatekey format: "+identity);
+	}
+	else if(cipher.equals("none")){
+  	   _buf.getInt();
+  	   //_buf.getInt();
+
+           encrypted=false;
+
+	   byte[] foo=new byte[encoded_data.length-_buf.getOffSet()];
+	   _buf.getByte(foo);
+	   encoded_data=foo;
+	}
+
+      }
+
+      if(pubkey==null){
+        return;
+      }
+      
+      buf=pubkey;
+      len=buf.length;
+
+      if(buf.length>4 &&             // FSecure's public key
+	 buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-'){
+	i=0;
+	do{i++;}while(len>i && buf[i]!=0x0a);
+	if(len<=i) return;
+	while(i<len){
+	  if(buf[i]==0x0a){
+	    boolean inheader=false;
+	    for(int j=i+1; j<len; j++){
+	      if(buf[j]==0x0a) break;
+	      if(buf[j]==':'){inheader=true; break;}
+	    }
+	    if(!inheader){
+	      i++; 
+	      break;
+	    }
+	  }
+	  i++;
+	}
+	if(len<=i) return;
+
+	start=i;
+	while(i<len){
+	  if(buf[i]==0x0a){
+	    System.arraycopy(buf, i+1, buf, i, len-i-1);
+	    len--;
+	    continue;
+	  }
+	  if(buf[i]=='-'){  break; }
+	  i++;
+	}
+	publickeyblob=Util.fromBase64(buf, start, i-start);
+
+	if(type==UNKNOWN && publickeyblob.length>8){
+	  if(publickeyblob[8]=='d'){
+	    type=DSS;
+	  }
+	  else if(publickeyblob[8]=='r'){
+	    type=RSA;
+	  }
+	}
+      }
+      else{
+	if(buf[0]!='s'|| buf[1]!='s'|| buf[2]!='h'|| buf[3]!='-') return;
+	i=0;
+	while(i<len){ if(buf[i]==' ')break; i++;} i++;
+	if(i>=len) return;
+	start=i;
+	while(i<len){ if(buf[i]==' ' || buf[i]=='\n')break; i++;}
+	publickeyblob=Util.fromBase64(buf, start, i-start);
+        if(publickeyblob.length<4+7){  // It must start with "ssh-XXX".
+          if(JSch.getLogger().isEnabled(Logger.WARN)){
+            JSch.getLogger().log(Logger.WARN, 
+                                 "failed to parse the public key");
+          }
+          publickeyblob=null;
+        }
+      }
+    }
+    catch(Exception e){
+      //System.err.println("IdentityFile: "+e);
+      if(e instanceof JSchException) throw (JSchException)e;
+      if(e instanceof Throwable)
+        throw new JSchException(e.toString(), (Throwable)e);
+      throw new JSchException(e.toString());
+    }
+  }
+
+  public String getAlgName(){
+    if(type==RSA) return "ssh-rsa";
+    return "ssh-dss"; 
+  }
+
+  public boolean setPassphrase(byte[] _passphrase) throws JSchException{
+    /*
+      hash is MD5
+      h(0) <- hash(passphrase, iv);
+      h(n) <- hash(h(n-1), passphrase, iv);
+      key <- (h(0),...,h(n))[0,..,key.length];
+    */
+    try{
+      if(encrypted){
+	if(_passphrase==null) return false;
+	byte[] passphrase=_passphrase;
+	int hsize=hash.getBlockSize();
+	byte[] hn=new byte[key.length/hsize*hsize+
+			   (key.length%hsize==0?0:hsize)];
+	byte[] tmp=null;
+	if(keytype==OPENSSH){
+	  for(int index=0; index+hsize<=hn.length;){
+	    if(tmp!=null){ hash.update(tmp, 0, tmp.length); }
+	    hash.update(passphrase, 0, passphrase.length);
+	    hash.update(iv, 0, iv.length > 8 ? 8: iv.length);
+	    tmp=hash.digest();
+	    System.arraycopy(tmp, 0, hn, index, tmp.length);
+	    index+=tmp.length;
+	  }
+	  System.arraycopy(hn, 0, key, 0, key.length); 
+	}
+	else if(keytype==FSECURE){
+	  for(int index=0; index+hsize<=hn.length;){
+	    if(tmp!=null){ hash.update(tmp, 0, tmp.length); }
+	    hash.update(passphrase, 0, passphrase.length);
+	    tmp=hash.digest();
+	    System.arraycopy(tmp, 0, hn, index, tmp.length);
+	    index+=tmp.length;
+	  }
+	  System.arraycopy(hn, 0, key, 0, key.length); 
+	}
+        Util.bzero(passphrase);
+      }
+      if(decrypt()){
+	encrypted=false;
+	return true;
+      }
+      P_array=Q_array=G_array=pub_array=prv_array=null;
+      return false;
+    }
+    catch(Exception e){
+      if(e instanceof JSchException) throw (JSchException)e;
+      if(e instanceof Throwable)
+        throw new JSchException(e.toString(), (Throwable)e);
+      throw new JSchException(e.toString());
+    }
+  }
+
+  public byte[] getPublicKeyBlob(){
+    if(publickeyblob!=null) return publickeyblob;
+    if(type==RSA) return getPublicKeyBlob_rsa();
+    return getPublicKeyBlob_dss();
+  }
+
+  byte[] getPublicKeyBlob_rsa(){
+    if(e_array==null) return null;
+    Buffer buf=new Buffer("ssh-rsa".length()+4+
+			   e_array.length+4+ 
+ 			   n_array.length+4);
+    buf.putString(Util.str2byte("ssh-rsa"));
+    buf.putString(e_array);
+    buf.putString(n_array);
+    return buf.buffer;
+  }
+
+  byte[] getPublicKeyBlob_dss(){
+    if(P_array==null) return null;
+    Buffer buf=new Buffer("ssh-dss".length()+4+
+			   P_array.length+4+ 
+			   Q_array.length+4+ 
+			   G_array.length+4+ 
+			   pub_array.length+4);
+    buf.putString(Util.str2byte("ssh-dss"));
+    buf.putString(P_array);
+    buf.putString(Q_array);
+    buf.putString(G_array);
+    buf.putString(pub_array);
+    return buf.buffer;
+  }
+
+  public byte[] getSignature(byte[] data){
+    if(type==RSA) return getSignature_rsa(data);
+    return getSignature_dss(data);
+  }
+
+  byte[] getSignature_rsa(byte[] data){
+    try{      
+      Class c=Class.forName((String)jsch.getConfig("signature.rsa"));
+      SignatureRSA rsa=(SignatureRSA)(c.newInstance());
+
+      rsa.init();
+      rsa.setPrvKey(d_array, n_array);
+
+      rsa.update(data);
+      byte[] sig = rsa.sign();
+      Buffer buf=new Buffer("ssh-rsa".length()+4+
+			    sig.length+4);
+      buf.putString(Util.str2byte("ssh-rsa"));
+      buf.putString(sig);
+      return buf.buffer;
+    }
+    catch(Exception e){
+    }
+    return null;
+  }
+
+  byte[] getSignature_dss(byte[] data){
+/*
+    byte[] foo;
+    int i;
+    System.err.print("P ");
+    foo=P_array;
+    for(i=0;  i<foo.length; i++){
+      System.err.print(Integer.toHexString(foo[i]&0xff)+":");
+    }
+    System.err.println("");
+    System.err.print("Q ");
+    foo=Q_array;
+    for(i=0;  i<foo.length; i++){
+      System.err.print(Integer.toHexString(foo[i]&0xff)+":");
+    }
+    System.err.println("");
+    System.err.print("G ");
+    foo=G_array;
+    for(i=0;  i<foo.length; i++){
+      System.err.print(Integer.toHexString(foo[i]&0xff)+":");
+    }
+    System.err.println("");
+*/
+
+    try{      
+      Class c=Class.forName((String)jsch.getConfig("signature.dss"));
+      SignatureDSA dsa=(SignatureDSA)(c.newInstance());
+      dsa.init();
+      dsa.setPrvKey(prv_array, P_array, Q_array, G_array);
+
+      dsa.update(data);
+      byte[] sig = dsa.sign();
+      Buffer buf=new Buffer("ssh-dss".length()+4+
+			    sig.length+4);
+      buf.putString(Util.str2byte("ssh-dss"));
+      buf.putString(sig);
+      return buf.buffer;
+    }
+    catch(Exception e){
+      //System.err.println("e "+e);
+    }
+    return null;
+  }
+
+  public boolean decrypt(){
+    if(type==RSA) return decrypt_rsa();
+    return decrypt_dss();
+  }
+
+  boolean decrypt_rsa(){
+    byte[] p_array;
+    byte[] q_array;
+    byte[] dmp1_array;
+    byte[] dmq1_array;
+    byte[] iqmp_array;
+
+    try{
+      byte[] plain;
+      if(encrypted){
+	if(keytype==OPENSSH){
+	  cipher.init(Cipher.DECRYPT_MODE, key, iv);
+	  plain=new byte[encoded_data.length];
+	  cipher.update(encoded_data, 0, encoded_data.length, plain, 0);
+	}
+	else if(keytype==FSECURE){
+	  for(int i=0; i<iv.length; i++)iv[i]=0;
+	  cipher.init(Cipher.DECRYPT_MODE, key, iv);
+	  plain=new byte[encoded_data.length];
+	  cipher.update(encoded_data, 0, encoded_data.length, plain, 0);
+	}
+	else{
+	  return false;
+	}
+      }
+      else{
+	if(n_array!=null) return true;
+	plain=encoded_data;
+      }
+
+      if(keytype==FSECURE){              // FSecure   
+	Buffer buf=new Buffer(plain);
+        int foo=buf.getInt();
+        if(plain.length!=foo+4){
+          return false;
+        }
+	e_array=buf.getMPIntBits();
+        d_array=buf.getMPIntBits();
+	n_array=buf.getMPIntBits();
+	byte[] u_array=buf.getMPIntBits();
+	p_array=buf.getMPIntBits();
+	q_array=buf.getMPIntBits();
+        return true;
+      }
+
+      int index=0;
+      int length=0;
+
+      if(plain[index]!=0x30)return false;
+      index++; // SEQUENCE
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+
+      if(plain[index]!=0x02)return false;
+      index++; // INTEGER
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      index+=length;
+
+//System.err.println("int: len="+length);
+//System.err.print(Integer.toHexString(plain[index-1]&0xff)+":");
+//System.err.println("");
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      n_array=new byte[length];
+      System.arraycopy(plain, index, n_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: N len="+length);
+for(int i=0; i<n_array.length; i++){
+System.err.print(Integer.toHexString(n_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      e_array=new byte[length];
+      System.arraycopy(plain, index, e_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: E len="+length);
+for(int i=0; i<e_array.length; i++){
+System.err.print(Integer.toHexString(e_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      d_array=new byte[length];
+      System.arraycopy(plain, index, d_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: D len="+length);
+for(int i=0; i<d_array.length; i++){
+System.err.print(Integer.toHexString(d_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      p_array=new byte[length];
+      System.arraycopy(plain, index, p_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: P len="+length);
+for(int i=0; i<p_array.length; i++){
+System.err.print(Integer.toHexString(p_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      q_array=new byte[length];
+      System.arraycopy(plain, index, q_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: q len="+length);
+for(int i=0; i<q_array.length; i++){
+System.err.print(Integer.toHexString(q_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      dmp1_array=new byte[length];
+      System.arraycopy(plain, index, dmp1_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: dmp1 len="+length);
+for(int i=0; i<dmp1_array.length; i++){
+System.err.print(Integer.toHexString(dmp1_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      dmq1_array=new byte[length];
+      System.arraycopy(plain, index, dmq1_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: dmq1 len="+length);
+for(int i=0; i<dmq1_array.length; i++){
+System.err.print(Integer.toHexString(dmq1_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      iqmp_array=new byte[length];
+      System.arraycopy(plain, index, iqmp_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: iqmp len="+length);
+for(int i=0; i<iqmp_array.length; i++){
+System.err.print(Integer.toHexString(iqmp_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+    }
+    catch(Exception e){
+      //System.err.println(e);
+      return false;
+    }
+    return true;
+  }
+
+  boolean decrypt_dss(){
+    try{
+      byte[] plain;
+      if(encrypted){
+	if(keytype==OPENSSH){
+	  cipher.init(Cipher.DECRYPT_MODE, key, iv);
+	  plain=new byte[encoded_data.length];
+	  cipher.update(encoded_data, 0, encoded_data.length, plain, 0);
+/*
+for(int i=0; i<plain.length; i++){
+System.err.print(Integer.toHexString(plain[i]&0xff)+":");
+}
+System.err.println("");
+*/
+	}
+	else if(keytype==FSECURE){
+	  for(int i=0; i<iv.length; i++)iv[i]=0;
+	  cipher.init(Cipher.DECRYPT_MODE, key, iv);
+	  plain=new byte[encoded_data.length];
+	  cipher.update(encoded_data, 0, encoded_data.length, plain, 0);
+	}
+	else{
+	  return false;
+	}
+      }
+      else{
+	if(P_array!=null) return true;
+	plain=encoded_data;
+      }
+
+      if(keytype==FSECURE){              // FSecure   
+	Buffer buf=new Buffer(plain);
+        int foo=buf.getInt();
+        if(plain.length!=foo+4){
+          return false;
+        }
+	P_array=buf.getMPIntBits();
+        G_array=buf.getMPIntBits();
+	Q_array=buf.getMPIntBits();
+	pub_array=buf.getMPIntBits();
+	prv_array=buf.getMPIntBits();
+        return true;
+      }
+
+      int index=0;
+      int length=0;
+      if(plain[index]!=0x30)return false;
+      index++; // SEQUENCE
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      if(plain[index]!=0x02)return false;
+      index++; // INTEGER
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      index+=length;
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      P_array=new byte[length];
+      System.arraycopy(plain, index, P_array, 0, length);
+      index+=length;
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      Q_array=new byte[length];
+      System.arraycopy(plain, index, Q_array, 0, length);
+      index+=length;
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      G_array=new byte[length];
+      System.arraycopy(plain, index, G_array, 0, length);
+      index+=length;
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      pub_array=new byte[length];
+      System.arraycopy(plain, index, pub_array, 0, length);
+      index+=length;
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      prv_array=new byte[length];
+      System.arraycopy(plain, index, prv_array, 0, length);
+      index+=length;
+    }
+    catch(Exception e){
+      //System.err.println(e);
+      //e.printStackTrace();
+      return false;
+    }
+    return true;
+  }
+
+  public boolean isEncrypted(){
+    return encrypted;
+  }
+
+  public String getName(){
+    return identity;
+  }
+
+  private byte a2b(byte c){
+    if('0'<=c&&c<='9') return (byte)(c-'0');
+    if('a'<=c&&c<='z') return (byte)(c-'a'+10);
+    return (byte)(c-'A'+10);
+  }
+
+  public boolean equals(Object o){
+    if(!(o instanceof IdentityFile)) return super.equals(o);
+    IdentityFile foo=(IdentityFile)o;
+    return getName().equals(foo.getName());
+  }
+
+  public void clear(){
+    Util.bzero(encoded_data);
+    Util.bzero(prv_array);
+    Util.bzero(d_array);
+    Util.bzero(key);
+    Util.bzero(iv);
+  }
+
+  public void finalize (){
+    clear();
+  }
+}
diff --git a/java/com/jcraft/jsch/IdentityRepository.java b/java/com/jcraft/jsch/IdentityRepository.java
new file mode 100644
index 0000000..1f75a01
--- /dev/null
+++ b/java/com/jcraft/jsch/IdentityRepository.java
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.util.Vector;
+
+public interface IdentityRepository {
+  public Vector getIdentities();
+  public boolean add(byte[] identity);
+  public boolean remove(byte[] blob);
+  public void removeAll();
+}
diff --git a/java/com/jcraft/jsch/JSch.java b/java/com/jcraft/jsch/JSch.java
new file mode 100644
index 0000000..20c39e5
--- /dev/null
+++ b/java/com/jcraft/jsch/JSch.java
@@ -0,0 +1,316 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.InputStream;
+import java.util.Vector;
+
+public class JSch{
+  public static final String VERSION  = "0.1.46";
+
+  static java.util.Hashtable config=new java.util.Hashtable();
+  static{
+//  config.put("kex", "diffie-hellman-group-exchange-sha1");
+    config.put("kex", "diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1");
+    config.put("server_host_key", "ssh-rsa,ssh-dss");
+//    config.put("server_host_key", "ssh-dss,ssh-rsa");
+
+    config.put("cipher.s2c", 
+               "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc");
+    config.put("cipher.c2s",
+               "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc");
+
+    config.put("mac.s2c", "hmac-md5,hmac-sha1,hmac-sha1-96,hmac-md5-96");
+    config.put("mac.c2s", "hmac-md5,hmac-sha1,hmac-sha1-96,hmac-md5-96");
+    config.put("compression.s2c", "none");
+    // config.put("compression.s2c", "zlib@openssh.com,zlib,none");
+    config.put("compression.c2s", "none");
+    // config.put("compression.c2s", "zlib@openssh.com,zlib,none");
+
+    config.put("lang.s2c", "");
+    config.put("lang.c2s", "");
+
+    config.put("compression_level", "6");
+
+    config.put("diffie-hellman-group-exchange-sha1", 
+                                "com.jcraft.jsch.DHGEX");
+    config.put("diffie-hellman-group1-sha1", 
+	                        "com.jcraft.jsch.DHG1");
+    config.put("diffie-hellman-group14-sha1", 
+	                        "com.jcraft.jsch.DHG14");
+
+    config.put("dh",            "com.jcraft.jsch.jce.DH");
+    config.put("3des-cbc",      "com.jcraft.jsch.jce.TripleDESCBC");
+    config.put("blowfish-cbc",  "com.jcraft.jsch.jce.BlowfishCBC");
+    config.put("hmac-sha1",     "com.jcraft.jsch.jce.HMACSHA1");
+    config.put("hmac-sha1-96",  "com.jcraft.jsch.jce.HMACSHA196");
+    config.put("hmac-md5",      "com.jcraft.jsch.jce.HMACMD5");
+    config.put("hmac-md5-96",   "com.jcraft.jsch.jce.HMACMD596");
+    config.put("sha-1",         "com.jcraft.jsch.jce.SHA1");
+    config.put("md5",           "com.jcraft.jsch.jce.MD5");
+    config.put("signature.dss", "com.jcraft.jsch.jce.SignatureDSA");
+    config.put("signature.rsa", "com.jcraft.jsch.jce.SignatureRSA");
+    config.put("keypairgen.dsa",   "com.jcraft.jsch.jce.KeyPairGenDSA");
+    config.put("keypairgen.rsa",   "com.jcraft.jsch.jce.KeyPairGenRSA");
+    config.put("random",        "com.jcraft.jsch.jce.Random");
+
+    config.put("none",           "com.jcraft.jsch.CipherNone");
+
+    config.put("aes128-cbc",    "com.jcraft.jsch.jce.AES128CBC");
+    config.put("aes192-cbc",    "com.jcraft.jsch.jce.AES192CBC");
+    config.put("aes256-cbc",    "com.jcraft.jsch.jce.AES256CBC");
+
+    config.put("aes128-ctr",    "com.jcraft.jsch.jce.AES128CTR");
+    config.put("aes192-ctr",    "com.jcraft.jsch.jce.AES192CTR");
+    config.put("aes256-ctr",    "com.jcraft.jsch.jce.AES256CTR");
+    config.put("3des-ctr",      "com.jcraft.jsch.jce.TripleDESCTR");
+    config.put("arcfour",      "com.jcraft.jsch.jce.ARCFOUR");
+    config.put("arcfour128",      "com.jcraft.jsch.jce.ARCFOUR128");
+    config.put("arcfour256",      "com.jcraft.jsch.jce.ARCFOUR256");
+
+    config.put("userauth.none",    "com.jcraft.jsch.UserAuthNone");
+    config.put("userauth.password",    "com.jcraft.jsch.UserAuthPassword");
+    config.put("userauth.keyboard-interactive",    "com.jcraft.jsch.UserAuthKeyboardInteractive");
+    config.put("userauth.publickey",    "com.jcraft.jsch.UserAuthPublicKey");
+    config.put("userauth.gssapi-with-mic",    "com.jcraft.jsch.UserAuthGSSAPIWithMIC");
+    config.put("gssapi-with-mic.krb5",    "com.jcraft.jsch.jgss.GSSContextKrb5");
+
+    config.put("zlib",             "com.jcraft.jsch.jcraft.Compression");
+    config.put("zlib@openssh.com", "com.jcraft.jsch.jcraft.Compression");
+
+    config.put("StrictHostKeyChecking",  "ask");
+    config.put("HashKnownHosts",  "no");
+    //config.put("HashKnownHosts",  "yes");
+    config.put("PreferredAuthentications", "gssapi-with-mic,publickey,keyboard-interactive,password");
+
+    config.put("CheckCiphers", "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-ctr,arcfour,arcfour128,arcfour256");
+    config.put("CheckKexes", "diffie-hellman-group14-sha1");
+
+    config.put("MaxAuthTries", "6");
+  }
+
+  private java.util.Vector sessionPool = new java.util.Vector();
+
+  private IdentityRepository identityRepository =
+    new LocalIdentityRepository(this);
+
+  public synchronized void setIdentityRepository(IdentityRepository identityRepository){
+    this.identityRepository = identityRepository;
+  }
+
+  synchronized IdentityRepository getIdentityRepository(){
+    return this.identityRepository;
+  }
+
+  private HostKeyRepository known_hosts=null;
+
+  private static final Logger DEVNULL=new Logger(){
+      public boolean isEnabled(int level){return false;}
+      public void log(int level, String message){}
+    };
+  static Logger logger=DEVNULL;
+
+  public JSch(){
+
+    try{
+      String osname=(String)(System.getProperties().get("os.name"));
+      if(osname!=null && osname.equals("Mac OS X")){
+        config.put("hmac-sha1",     "com.jcraft.jsch.jcraft.HMACSHA1"); 
+        config.put("hmac-md5",      "com.jcraft.jsch.jcraft.HMACMD5"); 
+        config.put("hmac-md5-96",   "com.jcraft.jsch.jcraft.HMACMD596"); 
+        config.put("hmac-sha1-96",  "com.jcraft.jsch.jcraft.HMACSHA196"); 
+      }
+    }
+    catch(Exception e){
+    }
+
+  }
+
+  public Session getSession(String username, String host) throws JSchException { return getSession(username, host, 22); }
+  public Session getSession(String username, String host, int port) throws JSchException {
+    if(username==null){
+      throw new JSchException("username must not be null.");
+    }
+    if(host==null){
+      throw new JSchException("host must not be null.");
+    }
+    Session s=new Session(this); 
+    s.setUserName(username);
+    s.setHost(host);
+    s.setPort(port);
+    return s;
+  }
+
+  protected void addSession(Session session){
+    synchronized(sessionPool){
+      sessionPool.addElement(session);
+    }
+  }
+
+  protected boolean removeSession(Session session){
+    synchronized(sessionPool){
+      return sessionPool.remove(session);
+    }
+  }
+  public void setHostKeyRepository(HostKeyRepository hkrepo){
+    known_hosts=hkrepo;
+  }
+
+  public void setKnownHosts(String filename) throws JSchException{
+    if(known_hosts==null) known_hosts=new KnownHosts(this);
+    if(known_hosts instanceof KnownHosts){
+      synchronized(known_hosts){
+	((KnownHosts)known_hosts).setKnownHosts(filename); 
+      }
+    }
+  }
+
+  public void setKnownHosts(InputStream stream) throws JSchException{ 
+    if(known_hosts==null) known_hosts=new KnownHosts(this);
+    if(known_hosts instanceof KnownHosts){
+      synchronized(known_hosts){
+	((KnownHosts)known_hosts).setKnownHosts(stream); 
+      }
+    }
+  }
+
+  public HostKeyRepository getHostKeyRepository(){ 
+    if(known_hosts==null) known_hosts=new KnownHosts(this);
+    return known_hosts; 
+  }
+
+  public void addIdentity(String prvkey) throws JSchException{
+    addIdentity(prvkey, (byte[])null);
+  }
+
+  public void addIdentity(String prvkey, String passphrase) throws JSchException{
+    byte[] _passphrase=null;
+    if(passphrase!=null){
+      _passphrase=Util.str2byte(passphrase);
+    }
+    addIdentity(prvkey, _passphrase);
+    if(_passphrase!=null)
+      Util.bzero(_passphrase);
+  }
+
+  public void addIdentity(String prvkey, byte[] passphrase) throws JSchException{
+    Identity identity=IdentityFile.newInstance(prvkey, null, this);
+    addIdentity(identity, passphrase);
+  }
+  public void addIdentity(String prvkey, String pubkey, byte[] passphrase) throws JSchException{
+    Identity identity=IdentityFile.newInstance(prvkey, pubkey, this);
+    addIdentity(identity, passphrase);
+  }
+
+  public void addIdentity(String name, byte[]prvkey, byte[]pubkey, byte[] passphrase) throws JSchException{
+    Identity identity=IdentityFile.newInstance(name, prvkey, pubkey, this);
+    addIdentity(identity, passphrase);
+  }
+
+  public void addIdentity(Identity identity, byte[] passphrase) throws JSchException{
+    if(passphrase!=null){
+      try{ 
+        byte[] goo=new byte[passphrase.length];
+        System.arraycopy(passphrase, 0, goo, 0, passphrase.length);
+        passphrase=goo;
+        identity.setPassphrase(passphrase); 
+      }
+      finally{
+        Util.bzero(passphrase);
+      }
+    }
+
+    if(identityRepository instanceof LocalIdentityRepository){
+      ((LocalIdentityRepository)identityRepository).add(identity);
+    }
+    else {
+      // TODO
+    }
+  }
+
+  /**
+   * @deprecated use JSch#removeIdentity(Identity identity)
+   */
+  public void removeIdentity(String name) throws JSchException{
+    Vector identities = identityRepository.getIdentities();
+    for(int i=0; i<identities.size(); i++){
+      Identity identity=(Identity)(identities.elementAt(i));
+      if(!identity.getName().equals(name))
+        continue;
+      identityRepository.remove(identity.getPublicKeyBlob());
+      break;
+    }
+  }
+
+  public void removeIdentity(Identity identity) throws JSchException{
+    identityRepository.remove(identity.getPublicKeyBlob());
+  }
+
+  public Vector getIdentityNames() throws JSchException{
+    Vector foo=new Vector();
+    Vector identities = identityRepository.getIdentities();
+    for(int i=0; i<identities.size(); i++){
+      Identity identity=(Identity)(identities.elementAt(i));
+      foo.addElement(identity.getName());
+    }
+    return foo;
+  }
+
+  public void removeAllIdentity() throws JSchException{
+    identityRepository.removeAll();
+  }
+
+  public static String getConfig(String key){ 
+    synchronized(config){
+      return (String)(config.get(key));
+    } 
+  }
+
+  public static void setConfig(java.util.Hashtable newconf){
+    synchronized(config){
+      for(java.util.Enumeration e=newconf.keys() ; e.hasMoreElements() ;) {
+	String key=(String)(e.nextElement());
+	config.put(key, (String)(newconf.get(key)));
+      }
+    }
+  }
+
+  public static void setConfig(String key, String value){
+    config.put(key, value);
+  }
+
+  public static void setLogger(Logger logger){
+    if(logger==null) logger=DEVNULL;
+    JSch.logger=logger;
+  }
+  static Logger getLogger(){
+    return logger;
+  }
+}
diff --git a/java/com/jcraft/jsch/JSchAuthCancelException.java b/java/com/jcraft/jsch/JSchAuthCancelException.java
new file mode 100644
index 0000000..65e71f1
--- /dev/null
+++ b/java/com/jcraft/jsch/JSchAuthCancelException.java
@@ -0,0 +1,45 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class JSchAuthCancelException extends JSchException{
+  //private static final long serialVersionUID=3204965907117900987L;
+  String method;
+  JSchAuthCancelException () {
+    super();
+  }
+  JSchAuthCancelException (String s) {
+    super(s);
+    this.method=s;
+  }
+  public String getMethod(){
+    return method;
+  }
+}
diff --git a/java/com/jcraft/jsch/JSchException.java b/java/com/jcraft/jsch/JSchException.java
new file mode 100644
index 0000000..1e9056c
--- /dev/null
+++ b/java/com/jcraft/jsch/JSchException.java
@@ -0,0 +1,48 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class JSchException extends Exception{
+  //private static final long serialVersionUID=-1319309923966731989L;
+  private Throwable cause=null;
+  public JSchException () {
+    super();
+  }
+  public JSchException (String s) {
+    super(s);
+  }
+  public JSchException (String s, Throwable e) {
+    super(s);
+    this.cause=e;
+  }
+  public Throwable getCause(){
+    return this.cause;
+  }
+}
diff --git a/java/com/jcraft/jsch/JSchPartialAuthException.java b/java/com/jcraft/jsch/JSchPartialAuthException.java
new file mode 100644
index 0000000..aa7ac9e
--- /dev/null
+++ b/java/com/jcraft/jsch/JSchPartialAuthException.java
@@ -0,0 +1,45 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class JSchPartialAuthException extends JSchException{
+  //private static final long serialVersionUID=-378849862323360367L;
+  String methods;
+  public JSchPartialAuthException () {
+    super();
+  }
+  public JSchPartialAuthException (String s) {
+    super(s);
+    this.methods=s;
+  }
+  public String getMethods(){
+    return methods;
+  }
+}
diff --git a/java/com/jcraft/jsch/KeyExchange.java b/java/com/jcraft/jsch/KeyExchange.java
new file mode 100644
index 0000000..ef089ba
--- /dev/null
+++ b/java/com/jcraft/jsch/KeyExchange.java
@@ -0,0 +1,159 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public abstract class KeyExchange{
+
+  static final int PROPOSAL_KEX_ALGS=0;
+  static final int PROPOSAL_SERVER_HOST_KEY_ALGS=1;
+  static final int PROPOSAL_ENC_ALGS_CTOS=2;
+  static final int PROPOSAL_ENC_ALGS_STOC=3;
+  static final int PROPOSAL_MAC_ALGS_CTOS=4;
+  static final int PROPOSAL_MAC_ALGS_STOC=5;
+  static final int PROPOSAL_COMP_ALGS_CTOS=6;
+  static final int PROPOSAL_COMP_ALGS_STOC=7;
+  static final int PROPOSAL_LANG_CTOS=8;
+  static final int PROPOSAL_LANG_STOC=9;
+  static final int PROPOSAL_MAX=10;
+
+  //static String kex_algs="diffie-hellman-group-exchange-sha1"+
+  //                       ",diffie-hellman-group1-sha1";
+
+//static String kex="diffie-hellman-group-exchange-sha1";
+  static String kex="diffie-hellman-group1-sha1";
+  static String server_host_key="ssh-rsa,ssh-dss";
+  static String enc_c2s="blowfish-cbc";
+  static String enc_s2c="blowfish-cbc";
+  static String mac_c2s="hmac-md5";     // hmac-md5,hmac-sha1,hmac-ripemd160,
+                                        // hmac-sha1-96,hmac-md5-96
+  static String mac_s2c="hmac-md5";
+//static String comp_c2s="none";        // zlib
+//static String comp_s2c="none";
+  static String lang_c2s="";
+  static String lang_s2c="";
+
+  public static final int STATE_END=0;
+
+  protected Session session=null;
+  protected HASH sha=null;
+  protected byte[] K=null;
+  protected byte[] H=null;
+  protected byte[] K_S=null;
+
+  public abstract void init(Session session, 
+			    byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception;
+  public abstract boolean next(Buffer buf) throws Exception;
+  public abstract String getKeyType();
+  public abstract int getState();
+
+  /*
+  void dump(byte[] foo){
+    for(int i=0; i<foo.length; i++){
+      if((foo[i]&0xf0)==0)System.err.print("0");
+      System.err.print(Integer.toHexString(foo[i]&0xff));
+      if(i%16==15){System.err.println(""); continue;}
+      if(i%2==1)System.err.print(" ");
+    }
+  } 
+  */
+
+  protected static String[] guess(byte[]I_S, byte[]I_C){
+    String[] guess=new String[PROPOSAL_MAX];
+    Buffer sb=new Buffer(I_S); sb.setOffSet(17);
+    Buffer cb=new Buffer(I_C); cb.setOffSet(17);
+
+    for(int i=0; i<PROPOSAL_MAX; i++){
+      byte[] sp=sb.getString();  // server proposal
+      byte[] cp=cb.getString();  // client proposal
+      int j=0;
+      int k=0;
+
+      loop:
+      while(j<cp.length){
+	while(j<cp.length && cp[j]!=',')j++; 
+	if(k==j) return null;
+	String algorithm=Util.byte2str(cp, k, j-k);
+	int l=0;
+	int m=0;
+	while(l<sp.length){
+	  while(l<sp.length && sp[l]!=',')l++; 
+	  if(m==l) return null;
+	  if(algorithm.equals(Util.byte2str(sp, m, l-m))){
+	    guess[i]=algorithm;
+	    break loop;
+	  }
+	  l++;
+	  m=l;
+	}	
+	j++;
+	k=j;
+      }
+      if(j==0){
+	guess[i]="";
+      }
+      else if(guess[i]==null){
+	return null;
+      }
+    }
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "kex: server->client"+
+                           " "+guess[PROPOSAL_ENC_ALGS_STOC]+
+                           " "+guess[PROPOSAL_MAC_ALGS_STOC]+
+                           " "+guess[PROPOSAL_COMP_ALGS_STOC]);
+      JSch.getLogger().log(Logger.INFO, 
+                           "kex: client->server"+
+                           " "+guess[PROPOSAL_ENC_ALGS_CTOS]+
+                           " "+guess[PROPOSAL_MAC_ALGS_CTOS]+
+                           " "+guess[PROPOSAL_COMP_ALGS_CTOS]);
+    }
+
+//    for(int i=0; i<PROPOSAL_MAX; i++){
+//      System.err.println("guess: ["+guess[i]+"]");
+//    }
+
+    return guess;
+  }
+
+  public String getFingerPrint(){
+    HASH hash=null;
+    try{
+      Class c=Class.forName(session.getConfig("md5"));
+      hash=(HASH)(c.newInstance());
+    }
+    catch(Exception e){ System.err.println("getFingerPrint: "+e); }
+    return Util.getFingerPrint(hash, getHostKey());
+  }
+  byte[] getK(){ return K; }
+  byte[] getH(){ return H; }
+  HASH getHash(){ return sha; }
+  byte[] getHostKey(){ return K_S; }
+}
diff --git a/java/com/jcraft/jsch/KeyPair.java b/java/com/jcraft/jsch/KeyPair.java
new file mode 100644
index 0000000..b3f681c
--- /dev/null
+++ b/java/com/jcraft/jsch/KeyPair.java
@@ -0,0 +1,729 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.File;
+
+public abstract class KeyPair{
+  public static final int ERROR=0;
+  public static final int DSA=1;
+  public static final int RSA=2;
+  public static final int UNKNOWN=3;
+
+  static final int VENDOR_OPENSSH=0;
+  static final int VENDOR_FSECURE=1;
+  int vendor=VENDOR_OPENSSH;
+
+  private static final byte[] cr=Util.str2byte("\n");
+
+  public static KeyPair genKeyPair(JSch jsch, int type) throws JSchException{
+    return genKeyPair(jsch, type, 1024);
+  }
+  public static KeyPair genKeyPair(JSch jsch, int type, int key_size) throws JSchException{
+    KeyPair kpair=null;
+    if(type==DSA){ kpair=new KeyPairDSA(jsch); }
+    else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
+    if(kpair!=null){
+      kpair.generate(key_size);
+    }
+    return kpair;
+  }
+
+  abstract void generate(int key_size) throws JSchException;
+
+  abstract byte[] getBegin();
+  abstract byte[] getEnd();
+  abstract int getKeySize();
+
+  public String getPublicKeyComment(){
+    return publicKeyComment;
+  }
+  private String publicKeyComment = "";
+
+  JSch jsch=null;
+  private Cipher cipher;
+  private HASH hash;
+  private Random random;
+
+  private byte[] passphrase;
+
+  public KeyPair(JSch jsch){
+    this.jsch=jsch;
+  }
+
+  static byte[][] header={Util.str2byte("Proc-Type: 4,ENCRYPTED"),
+                          Util.str2byte("DEK-Info: DES-EDE3-CBC,")};
+
+  abstract byte[] getPrivateKey();
+
+  public void writePrivateKey(java.io.OutputStream out){
+    byte[] plain=getPrivateKey();
+    byte[][] _iv=new byte[1][];
+    byte[] encoded=encrypt(plain, _iv);
+    if(encoded!=plain)
+      Util.bzero(plain);
+    byte[] iv=_iv[0];
+    byte[] prv=Util.toBase64(encoded, 0, encoded.length);
+
+    try{
+      out.write(getBegin()); out.write(cr);
+      if(passphrase!=null){
+	out.write(header[0]); out.write(cr);
+	out.write(header[1]); 
+	for(int i=0; i<iv.length; i++){
+	  out.write(b2a((byte)((iv[i]>>>4)&0x0f)));
+	  out.write(b2a((byte)(iv[i]&0x0f)));
+	}
+        out.write(cr);
+	out.write(cr);
+      }
+      int i=0;
+      while(i<prv.length){
+	if(i+64<prv.length){
+	  out.write(prv, i, 64);
+	  out.write(cr);
+	  i+=64;
+	  continue;
+	}
+	out.write(prv, i, prv.length-i);
+	out.write(cr);
+	break;
+      }
+      out.write(getEnd()); out.write(cr);
+      //out.close();
+    }
+    catch(Exception e){
+    }
+  }
+
+  private static byte[] space=Util.str2byte(" ");
+
+  abstract byte[] getKeyTypeName();
+  public abstract int getKeyType();
+
+  public byte[] getPublicKeyBlob(){ return publickeyblob; }
+
+  public void writePublicKey(java.io.OutputStream out, String comment){
+    byte[] pubblob=getPublicKeyBlob();
+    byte[] pub=Util.toBase64(pubblob, 0, pubblob.length);
+    try{
+      out.write(getKeyTypeName()); out.write(space);
+      out.write(pub, 0, pub.length); out.write(space);
+      out.write(Util.str2byte(comment));
+      out.write(cr);
+    }
+    catch(Exception e){
+    }
+  }
+
+  public void writePublicKey(String name, String comment) throws java.io.FileNotFoundException, java.io.IOException{
+    FileOutputStream fos=new FileOutputStream(name);
+    writePublicKey(fos, comment);
+    fos.close();
+  }
+
+  public void writeSECSHPublicKey(java.io.OutputStream out, String comment){
+    byte[] pubblob=getPublicKeyBlob();
+    byte[] pub=Util.toBase64(pubblob, 0, pubblob.length);
+    try{
+      out.write(Util.str2byte("---- BEGIN SSH2 PUBLIC KEY ----")); out.write(cr);
+      out.write(Util.str2byte("Comment: \""+comment+"\"")); out.write(cr);
+      int index=0;
+      while(index<pub.length){
+	int len=70;
+	if((pub.length-index)<len)len=pub.length-index;
+	out.write(pub, index, len); out.write(cr);
+	index+=len;
+      }
+      out.write(Util.str2byte("---- END SSH2 PUBLIC KEY ----")); out.write(cr);
+    }
+    catch(Exception e){
+    }
+  }
+
+  public void writeSECSHPublicKey(String name, String comment) throws java.io.FileNotFoundException, java.io.IOException{
+    FileOutputStream fos=new FileOutputStream(name);
+    writeSECSHPublicKey(fos, comment);
+    fos.close();
+  }
+
+
+  public void writePrivateKey(String name) throws java.io.FileNotFoundException, java.io.IOException{
+    FileOutputStream fos=new FileOutputStream(name);
+    writePrivateKey(fos);
+    fos.close();
+  }
+
+  public String getFingerPrint(){
+    if(hash==null) hash=genHash();
+    byte[] kblob=getPublicKeyBlob();
+    if(kblob==null) return null;
+    return getKeySize()+" "+Util.getFingerPrint(hash, kblob);
+  }
+
+  private byte[] encrypt(byte[] plain, byte[][] _iv){
+    if(passphrase==null) return plain;
+
+    if(cipher==null) cipher=genCipher();
+    byte[] iv=_iv[0]=new byte[cipher.getIVSize()];
+
+    if(random==null) random=genRandom();
+    random.fill(iv, 0, iv.length);
+
+    byte[] key=genKey(passphrase, iv);
+    byte[] encoded=plain;
+
+    // PKCS#5Padding
+    {
+      //int bsize=cipher.getBlockSize();
+      int bsize=cipher.getIVSize();
+      byte[] foo=new byte[(encoded.length/bsize+1)*bsize];
+      System.arraycopy(encoded, 0, foo, 0, encoded.length);
+      int padding=bsize-encoded.length%bsize;
+      for(int i=foo.length-1; (foo.length-padding)<=i; i--){
+        foo[i]=(byte)padding;
+      }
+      encoded=foo;
+    }
+
+    try{
+      cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+      cipher.update(encoded, 0, encoded.length, encoded, 0);
+    }
+    catch(Exception e){
+      //System.err.println(e);
+    }
+    Util.bzero(key);
+    return encoded;
+  }
+
+  abstract boolean parse(byte[] data);
+
+  private byte[] decrypt(byte[] data, byte[] passphrase, byte[] iv){
+    /*
+    if(iv==null){  // FSecure
+      iv=new byte[8];
+      for(int i=0; i<iv.length; i++)iv[i]=0;
+    }
+    */
+    try{
+      byte[] key=genKey(passphrase, iv);
+      cipher.init(Cipher.DECRYPT_MODE, key, iv);
+      Util.bzero(key);
+      byte[] plain=new byte[data.length];
+      cipher.update(data, 0, data.length, plain, 0);
+      return plain;
+    }
+    catch(Exception e){
+      //System.err.println(e);
+    }
+    return null;
+  }
+
+  int writeSEQUENCE(byte[] buf, int index, int len){
+    buf[index++]=0x30;
+    index=writeLength(buf, index, len);
+    return index;
+  }
+  int writeINTEGER(byte[] buf, int index, byte[] data){
+    buf[index++]=0x02;
+    index=writeLength(buf, index, data.length);
+    System.arraycopy(data, 0, buf, index, data.length);
+    index+=data.length;
+    return index;
+  }
+
+  int countLength(int len){
+    int i=1;
+    if(len<=0x7f) return i;
+    while(len>0){
+      len>>>=8;
+      i++;
+    }
+    return i;
+  }
+
+  int writeLength(byte[] data, int index, int len){
+    int i=countLength(len)-1;
+    if(i==0){
+      data[index++]=(byte)len;
+      return index;
+    }
+    data[index++]=(byte)(0x80|i);
+    int j=index+i;
+    while(i>0){
+      data[index+i-1]=(byte)(len&0xff);
+      len>>>=8;
+      i--;
+    }
+    return j;
+  }
+
+  private Random genRandom(){
+    if(random==null){
+      try{
+	Class c=Class.forName(jsch.getConfig("random"));
+        random=(Random)(c.newInstance());
+      }
+      catch(Exception e){ System.err.println("connect: random "+e); }
+    }
+    return random;
+  }
+
+  private HASH genHash(){
+    try{
+      Class c=Class.forName(jsch.getConfig("md5"));
+      hash=(HASH)(c.newInstance());
+      hash.init();
+    }
+    catch(Exception e){
+    }
+    return hash;
+  }
+  private Cipher genCipher(){
+    try{
+      Class c;
+      c=Class.forName(jsch.getConfig("3des-cbc"));
+      cipher=(Cipher)(c.newInstance());
+    }
+    catch(Exception e){
+    }
+    return cipher;
+  }
+
+  /*
+    hash is MD5
+    h(0) <- hash(passphrase, iv);
+    h(n) <- hash(h(n-1), passphrase, iv);
+    key <- (h(0),...,h(n))[0,..,key.length];
+  */
+  synchronized byte[] genKey(byte[] passphrase, byte[] iv){
+    if(cipher==null) cipher=genCipher();
+    if(hash==null) hash=genHash();
+
+    byte[] key=new byte[cipher.getBlockSize()];
+    int hsize=hash.getBlockSize();
+    byte[] hn=new byte[key.length/hsize*hsize+
+		       (key.length%hsize==0?0:hsize)];
+    try{
+      byte[] tmp=null;
+      if(vendor==VENDOR_OPENSSH){
+	for(int index=0; index+hsize<=hn.length;){
+	  if(tmp!=null){ hash.update(tmp, 0, tmp.length); }
+	  hash.update(passphrase, 0, passphrase.length);
+          hash.update(iv, 0, iv.length > 8 ? 8: iv.length);
+	  tmp=hash.digest();
+	  System.arraycopy(tmp, 0, hn, index, tmp.length);
+	  index+=tmp.length;
+	}
+	System.arraycopy(hn, 0, key, 0, key.length); 
+      }
+      else if(vendor==VENDOR_FSECURE){
+	for(int index=0; index+hsize<=hn.length;){
+	  if(tmp!=null){ hash.update(tmp, 0, tmp.length); }
+	  hash.update(passphrase, 0, passphrase.length);
+	  tmp=hash.digest();
+	  System.arraycopy(tmp, 0, hn, index, tmp.length);
+	  index+=tmp.length;
+	}
+	System.arraycopy(hn, 0, key, 0, key.length); 
+      }
+    }
+    catch(Exception e){
+      System.err.println(e);
+    }
+    return key;
+  } 
+
+  public void setPassphrase(String passphrase){
+    if(passphrase==null || passphrase.length()==0){
+      setPassphrase((byte[])null);
+    }
+    else{
+      setPassphrase(Util.str2byte(passphrase));
+    }
+  }
+  public void setPassphrase(byte[] passphrase){
+    if(passphrase!=null && passphrase.length==0) 
+      passphrase=null;
+    this.passphrase=passphrase;
+  }
+
+  private boolean encrypted=false;
+  private byte[] data=null;
+  private byte[] iv=null;
+  private byte[] publickeyblob=null;
+
+  public boolean isEncrypted(){ return encrypted; }
+  public boolean decrypt(String _passphrase){
+    if(_passphrase==null || _passphrase.length()==0){
+      return !encrypted;
+    }
+    return decrypt(Util.str2byte(_passphrase));
+  }
+  public boolean decrypt(byte[] _passphrase){
+    if(!encrypted){
+      return true;
+    }
+    if(_passphrase==null){
+      return !encrypted;
+    }
+    byte[] bar=new byte[_passphrase.length];
+    System.arraycopy(_passphrase, 0, bar, 0, bar.length);
+    _passphrase=bar;
+    byte[] foo=decrypt(data, _passphrase, iv);
+    Util.bzero(_passphrase);
+    if(parse(foo)){
+      encrypted=false;
+    }
+    return !encrypted;
+  }
+
+  public static KeyPair load(JSch jsch, String prvkey) throws JSchException{
+    String pubkey=prvkey+".pub";
+    if(!new File(pubkey).exists()){
+      pubkey=null;
+    }
+    return load(jsch, prvkey, pubkey);
+  }
+  public static KeyPair load(JSch jsch, String prvkey, String pubkey) throws JSchException{
+
+    byte[] iv=new byte[8];       // 8
+    boolean encrypted=true;
+    byte[] data=null;
+
+    byte[] publickeyblob=null;
+
+    int type=ERROR;
+    int vendor=VENDOR_OPENSSH;
+    String publicKeyComment = "";
+    Cipher cipher=null;
+
+    try{
+      File file=new File(prvkey);
+      FileInputStream fis=new FileInputStream(prvkey);
+      byte[] buf=new byte[(int)(file.length())];
+      int len=0;
+      while(true){
+        int i=fis.read(buf, len, buf.length-len);
+        if(i<=0)
+          break;
+        len+=i;
+      }
+      fis.close();
+
+      int i=0;
+
+      while(i<len){
+        if(buf[i] == '-' && i+4<len && 
+           buf[i+1] == '-' && buf[i+2] == '-' && 
+           buf[i+3] == '-' && buf[i+4] == '-'){
+          break;
+        }
+        i++;
+      }
+
+      while(i<len){
+        if(buf[i]=='B'&& i+3<len && buf[i+1]=='E'&& buf[i+2]=='G'&& buf[i+3]=='I'){
+          i+=6;	    
+          if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSA; }
+	  else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; }
+	  else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H'){ // FSecure
+	    type=UNKNOWN;
+	    vendor=VENDOR_FSECURE;
+	  }
+	  else{
+	    throw new JSchException("invalid privatekey: "+prvkey);
+	  }
+          i+=3;
+	  continue;
+	}
+        if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && 
+           buf[i+4]=='2'&& buf[i+5]=='5'&& buf[i+6]=='6'&& buf[i+7]=='-'){
+          i+=8;
+          if(Session.checkCipher((String)jsch.getConfig("aes256-cbc"))){
+            Class c=Class.forName((String)jsch.getConfig("aes256-cbc"));
+            cipher=(Cipher)(c.newInstance());
+            // key=new byte[cipher.getBlockSize()];
+            iv=new byte[cipher.getIVSize()];
+          }
+          else{
+            throw new JSchException("privatekey: aes256-cbc is not available "+prvkey);
+          }
+          continue;
+        }
+        if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && 
+           buf[i+4]=='1'&& buf[i+5]=='9'&& buf[i+6]=='2'&& buf[i+7]=='-'){
+          i+=8;
+          if(Session.checkCipher((String)jsch.getConfig("aes192-cbc"))){
+            Class c=Class.forName((String)jsch.getConfig("aes192-cbc"));
+            cipher=(Cipher)(c.newInstance());
+            // key=new byte[cipher.getBlockSize()];
+            iv=new byte[cipher.getIVSize()];
+          }
+          else{
+            throw new JSchException("privatekey: aes192-cbc is not available "+prvkey);
+          }
+          continue;
+        }
+        if(buf[i]=='A'&& i+7<len && buf[i+1]=='E'&& buf[i+2]=='S'&& buf[i+3]=='-' && 
+           buf[i+4]=='1'&& buf[i+5]=='2'&& buf[i+6]=='8'&& buf[i+7]=='-'){
+          i+=8;
+          if(Session.checkCipher((String)jsch.getConfig("aes128-cbc"))){
+            Class c=Class.forName((String)jsch.getConfig("aes128-cbc"));
+            cipher=(Cipher)(c.newInstance());
+            // key=new byte[cipher.getBlockSize()];
+            iv=new byte[cipher.getIVSize()];
+          }
+          else{
+            throw new JSchException("privatekey: aes128-cbc is not available "+prvkey);
+          }
+          continue;
+        }
+        if(buf[i]=='C'&& i+3<len && buf[i+1]=='B'&& buf[i+2]=='C'&& buf[i+3]==','){
+          i+=4;
+	  for(int ii=0; ii<iv.length; ii++){
+            iv[ii]=(byte)(((a2b(buf[i++])<<4)&0xf0)+(a2b(buf[i++])&0xf));
+  	  }
+	  continue;
+	}
+	if(buf[i]==0x0d && i+1<buf.length && buf[i+1]==0x0a){
+	  i++;
+	  continue;
+	}
+	if(buf[i]==0x0a && i+1<buf.length){
+	  if(buf[i+1]==0x0a){ i+=2; break; }
+	  if(buf[i+1]==0x0d &&
+	     i+2<buf.length && buf[i+2]==0x0a){
+	     i+=3; break;
+	  }
+	  boolean inheader=false;
+	  for(int j=i+1; j<buf.length; j++){
+	    if(buf[j]==0x0a) break;
+	    //if(buf[j]==0x0d) break;
+	    if(buf[j]==':'){inheader=true; break;}
+	  }
+	  if(!inheader){
+	    i++; 
+	    encrypted=false;    // no passphrase
+	    break;
+	  }
+	}
+	i++;
+      }
+
+      if(type==ERROR){
+	throw new JSchException("invalid privatekey: "+prvkey);
+      }
+
+      int start=i;
+      while(i<len){
+        if(buf[i]==0x0a){
+	  boolean xd=(buf[i-1]==0x0d);
+          System.arraycopy(buf, i+1, 
+			   buf, 
+			   i-(xd ? 1 : 0), 
+			   len-i-1-(xd ? 1 : 0)
+			   );
+	  if(xd)len--;
+          len--;
+          continue;
+        }
+        if(buf[i]=='-'){  break; }
+        i++;
+      }
+      data=Util.fromBase64(buf, start, i-start);
+
+      if(data.length>4 &&            // FSecure
+	 data[0]==(byte)0x3f &&
+	 data[1]==(byte)0x6f &&
+	 data[2]==(byte)0xf9 &&
+	 data[3]==(byte)0xeb){
+
+	Buffer _buf=new Buffer(data);
+	_buf.getInt();  // 0x3f6ff9be
+	_buf.getInt();
+	byte[]_type=_buf.getString();
+	//System.err.println("type: "+new String(_type)); 
+	String _cipher=Util.byte2str(_buf.getString());
+	//System.err.println("cipher: "+_cipher); 
+	if(_cipher.equals("3des-cbc")){
+  	   _buf.getInt();
+	   byte[] foo=new byte[data.length-_buf.getOffSet()];
+	   _buf.getByte(foo);
+	   data=foo;
+	   encrypted=true;
+	   throw new JSchException("unknown privatekey format: "+prvkey);
+	}
+	else if(_cipher.equals("none")){
+  	   _buf.getInt();
+  	   _buf.getInt();
+
+           encrypted=false;
+
+	   byte[] foo=new byte[data.length-_buf.getOffSet()];
+	   _buf.getByte(foo);
+	   data=foo;
+	}
+      }
+
+      if(pubkey!=null){
+	try{
+	  file=new File(pubkey);
+	  fis=new FileInputStream(pubkey);
+	  buf=new byte[(int)(file.length())];
+          len=0;
+          while(true){
+            i=fis.read(buf, len, buf.length-len);
+            if(i<=0)
+              break;
+            len+=i;
+          }
+	  fis.close();
+
+	  if(buf.length>4 &&             // FSecure's public key
+	     buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-'){
+
+	    boolean valid=true;
+	    i=0;
+	    do{i++;}while(buf.length>i && buf[i]!=0x0a);
+	    if(buf.length<=i) {valid=false;}
+
+	    while(valid){
+	      if(buf[i]==0x0a){
+		boolean inheader=false;
+		for(int j=i+1; j<buf.length; j++){
+		  if(buf[j]==0x0a) break;
+		  if(buf[j]==':'){inheader=true; break;}
+		}
+		if(!inheader){
+		  i++; 
+		  break;
+		}
+	      }
+	      i++;
+	    }
+	    if(buf.length<=i){valid=false;}
+
+	    start=i;
+	    while(valid && i<len){
+	      if(buf[i]==0x0a){
+		System.arraycopy(buf, i+1, buf, i, len-i-1);
+		len--;
+		continue;
+	      }
+	      if(buf[i]=='-'){  break; }
+	      i++;
+	    }
+	    if(valid){
+	      publickeyblob=Util.fromBase64(buf, start, i-start);
+	      if(type==UNKNOWN){
+		if(publickeyblob[8]=='d'){ type=DSA; }
+		else if(publickeyblob[8]=='r'){ type=RSA; }
+	      }
+	    }
+	  }
+	  else{
+	    if(buf[0]=='s'&& buf[1]=='s'&& buf[2]=='h' && buf[3]=='-'){
+	      i=0;
+	      while(i<len){ if(buf[i]==' ')break; i++;} i++;
+	      if(i<len){
+		start=i;
+		while(i<len){ if(buf[i]==' ')break; i++;}
+		publickeyblob=Util.fromBase64(buf, start, i-start);
+	      }
+              if(i++<len){
+                int s=i;
+                while(i<len){ if(buf[i]=='\n')break; i++;}
+                if(i<len){
+                  publicKeyComment = new String(buf, s, i-s);
+                }
+              } 
+	    }
+	  }
+	}
+	catch(Exception ee){
+	}
+      }
+    }
+    catch(Exception e){
+      if(e instanceof JSchException) throw (JSchException)e;
+      if(e instanceof Throwable)
+        throw new JSchException(e.toString(), (Throwable)e);
+      throw new JSchException(e.toString());
+    }
+
+    KeyPair kpair=null;
+    if(type==DSA){ kpair=new KeyPairDSA(jsch); }
+    else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
+
+    if(kpair!=null){
+      kpair.encrypted=encrypted;
+      kpair.publickeyblob=publickeyblob;
+      kpair.vendor=vendor;
+      kpair.publicKeyComment=publicKeyComment;
+      kpair.cipher=cipher;
+
+      if(encrypted){
+	kpair.iv=iv;
+	kpair.data=data;
+      }
+      else{
+	if(kpair.parse(data)){
+	  return kpair;
+	}
+	else{
+	  throw new JSchException("invalid privatekey: "+prvkey);
+	}
+      }
+    }
+
+    return kpair;
+  }
+
+  static private byte a2b(byte c){
+    if('0'<=c&&c<='9') return (byte)(c-'0');
+    return (byte)(c-'a'+10);
+  }
+  static private byte b2a(byte c){
+    if(0<=c&&c<=9) return (byte)(c+'0');
+    return (byte)(c-10+'A');
+  }
+
+  public void dispose(){
+    Util.bzero(passphrase);
+  }
+
+  public void finalize (){
+    dispose();
+  }
+}
diff --git a/java/com/jcraft/jsch/KeyPairDSA.java b/java/com/jcraft/jsch/KeyPairDSA.java
new file mode 100644
index 0000000..f65d8c0
--- /dev/null
+++ b/java/com/jcraft/jsch/KeyPairDSA.java
@@ -0,0 +1,221 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class KeyPairDSA extends KeyPair{
+  private byte[] P_array;
+  private byte[] Q_array;
+  private byte[] G_array;
+  private byte[] pub_array;
+  private byte[] prv_array;
+
+  //private int key_size=0;
+  private int key_size=1024;
+
+  public KeyPairDSA(JSch jsch){
+    super(jsch);
+  }
+
+  void generate(int key_size) throws JSchException{
+    this.key_size=key_size;
+    try{
+      Class c=Class.forName(jsch.getConfig("keypairgen.dsa"));
+      KeyPairGenDSA keypairgen=(KeyPairGenDSA)(c.newInstance());
+      keypairgen.init(key_size);
+      P_array=keypairgen.getP();
+      Q_array=keypairgen.getQ();
+      G_array=keypairgen.getG();
+      pub_array=keypairgen.getY();
+      prv_array=keypairgen.getX();
+
+      keypairgen=null;
+    }
+    catch(Exception e){
+      //System.err.println("KeyPairDSA: "+e); 
+      if(e instanceof Throwable)
+        throw new JSchException(e.toString(), (Throwable)e);
+      throw new JSchException(e.toString());
+    }
+  }
+
+  private static final byte[] begin=Util.str2byte("-----BEGIN DSA PRIVATE KEY-----");
+  private static final byte[] end=Util.str2byte("-----END DSA PRIVATE KEY-----");
+
+  byte[] getBegin(){ return begin; }
+  byte[] getEnd(){ return end; }
+
+  byte[] getPrivateKey(){
+    int content=
+      1+countLength(1) + 1 +                           // INTEGER
+      1+countLength(P_array.length) + P_array.length + // INTEGER  P
+      1+countLength(Q_array.length) + Q_array.length + // INTEGER  Q
+      1+countLength(G_array.length) + G_array.length + // INTEGER  G
+      1+countLength(pub_array.length) + pub_array.length + // INTEGER  pub
+      1+countLength(prv_array.length) + prv_array.length;  // INTEGER  prv
+
+    int total=
+      1+countLength(content)+content;   // SEQUENCE
+
+    byte[] plain=new byte[total];
+    int index=0;
+    index=writeSEQUENCE(plain, index, content);
+    index=writeINTEGER(plain, index, new byte[1]);  // 0
+    index=writeINTEGER(plain, index, P_array);
+    index=writeINTEGER(plain, index, Q_array);
+    index=writeINTEGER(plain, index, G_array);
+    index=writeINTEGER(plain, index, pub_array);
+    index=writeINTEGER(plain, index, prv_array);
+    return plain;
+  }
+
+  boolean parse(byte[] plain){
+    try{
+
+      if(vendor==VENDOR_FSECURE){
+	if(plain[0]!=0x30){              // FSecure
+	  Buffer buf=new Buffer(plain);
+	  buf.getInt();
+	  P_array=buf.getMPIntBits();
+	  G_array=buf.getMPIntBits();
+	  Q_array=buf.getMPIntBits();
+	  pub_array=buf.getMPIntBits();
+	  prv_array=buf.getMPIntBits();
+	  return true;
+	}
+	return false;
+      }
+
+      int index=0;
+      int length=0;
+
+      if(plain[index]!=0x30)return false;
+      index++; // SEQUENCE
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+
+      if(plain[index]!=0x02)return false;
+      index++; // INTEGER
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      index+=length;
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      P_array=new byte[length];
+      System.arraycopy(plain, index, P_array, 0, length);
+      index+=length;
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      Q_array=new byte[length];
+      System.arraycopy(plain, index, Q_array, 0, length);
+      index+=length;
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      G_array=new byte[length];
+      System.arraycopy(plain, index, G_array, 0, length);
+      index+=length;
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      pub_array=new byte[length];
+      System.arraycopy(plain, index, pub_array, 0, length);
+      index+=length;
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      prv_array=new byte[length];
+      System.arraycopy(plain, index, prv_array, 0, length);
+      index+=length;
+    }
+    catch(Exception e){
+      //System.err.println(e);
+      //e.printStackTrace();
+      return false;
+    }
+    return true;
+  }
+
+  public byte[] getPublicKeyBlob(){
+    byte[] foo=super.getPublicKeyBlob();
+    if(foo!=null) return foo;
+
+    if(P_array==null) return null;
+
+    Buffer buf=new Buffer(sshdss.length+4+
+			  P_array.length+4+ 
+			  Q_array.length+4+ 
+			  G_array.length+4+ 
+			  pub_array.length+4);
+    buf.putString(sshdss);
+    buf.putString(P_array);
+    buf.putString(Q_array);
+    buf.putString(G_array);
+    buf.putString(pub_array);
+    return buf.buffer;
+  }
+
+  private static final byte[] sshdss=Util.str2byte("ssh-dss");
+  byte[] getKeyTypeName(){return sshdss;}
+  public int getKeyType(){return DSA;}
+
+  public int getKeySize(){return key_size; }
+  public void dispose(){
+    super.dispose();
+    Util.bzero(prv_array);
+  }
+}
diff --git a/java/com/jcraft/jsch/KeyPairGenDSA.java b/java/com/jcraft/jsch/KeyPairGenDSA.java
new file mode 100644
index 0000000..f6507f2
--- /dev/null
+++ b/java/com/jcraft/jsch/KeyPairGenDSA.java
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface KeyPairGenDSA{
+  void init(int key_size) throws Exception;
+  byte[] getX();
+  byte[] getY();
+  byte[] getP();
+  byte[] getQ();
+  byte[] getG();
+}
diff --git a/java/com/jcraft/jsch/KeyPairGenRSA.java b/java/com/jcraft/jsch/KeyPairGenRSA.java
new file mode 100644
index 0000000..3a84907
--- /dev/null
+++ b/java/com/jcraft/jsch/KeyPairGenRSA.java
@@ -0,0 +1,43 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface KeyPairGenRSA{
+  void init(int key_size) throws Exception;
+  byte[] getD();
+  byte[] getE();
+  byte[] getN();
+
+  byte[] getC();
+  byte[] getEP();
+  byte[] getEQ();
+  byte[] getP();
+  byte[] getQ();
+}
diff --git a/java/com/jcraft/jsch/KeyPairRSA.java b/java/com/jcraft/jsch/KeyPairRSA.java
new file mode 100644
index 0000000..dfc202f
--- /dev/null
+++ b/java/com/jcraft/jsch/KeyPairRSA.java
@@ -0,0 +1,320 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class KeyPairRSA extends KeyPair{
+  private byte[] prv_array;
+  private byte[] pub_array;
+  private byte[] n_array;
+
+  private byte[] p_array;  // prime p
+  private byte[] q_array;  // prime q
+  private byte[] ep_array; // prime exponent p
+  private byte[] eq_array; // prime exponent q
+  private byte[] c_array;  // coefficient
+
+  //private int key_size=0;
+  private int key_size=1024;
+
+  public KeyPairRSA(JSch jsch){
+    super(jsch);
+  }
+
+  void generate(int key_size) throws JSchException{
+    this.key_size=key_size;
+    try{
+      Class c=Class.forName(jsch.getConfig("keypairgen.rsa"));
+      KeyPairGenRSA keypairgen=(KeyPairGenRSA)(c.newInstance());
+      keypairgen.init(key_size);
+      pub_array=keypairgen.getE();
+      prv_array=keypairgen.getD();
+      n_array=keypairgen.getN();
+
+      p_array=keypairgen.getP();
+      q_array=keypairgen.getQ();
+      ep_array=keypairgen.getEP();
+      eq_array=keypairgen.getEQ();
+      c_array=keypairgen.getC();
+
+      keypairgen=null;
+    }
+    catch(Exception e){
+      //System.err.println("KeyPairRSA: "+e); 
+      if(e instanceof Throwable)
+        throw new JSchException(e.toString(), (Throwable)e);
+      throw new JSchException(e.toString());
+    }
+  }
+
+  private static final byte[] begin=Util.str2byte("-----BEGIN RSA PRIVATE KEY-----");
+  private static final byte[] end=Util.str2byte("-----END RSA PRIVATE KEY-----");
+
+  byte[] getBegin(){ return begin; }
+  byte[] getEnd(){ return end; }
+
+  byte[] getPrivateKey(){
+    int content=
+      1+countLength(1) + 1 +                           // INTEGER
+      1+countLength(n_array.length) + n_array.length + // INTEGER  N
+      1+countLength(pub_array.length) + pub_array.length + // INTEGER  pub
+      1+countLength(prv_array.length) + prv_array.length+  // INTEGER  prv
+      1+countLength(p_array.length) + p_array.length+      // INTEGER  p
+      1+countLength(q_array.length) + q_array.length+      // INTEGER  q
+      1+countLength(ep_array.length) + ep_array.length+    // INTEGER  ep
+      1+countLength(eq_array.length) + eq_array.length+    // INTEGER  eq
+      1+countLength(c_array.length) + c_array.length;      // INTEGER  c
+
+    int total=
+      1+countLength(content)+content;   // SEQUENCE
+
+    byte[] plain=new byte[total];
+    int index=0;
+    index=writeSEQUENCE(plain, index, content);
+    index=writeINTEGER(plain, index, new byte[1]);  // 0
+    index=writeINTEGER(plain, index, n_array);
+    index=writeINTEGER(plain, index, pub_array);
+    index=writeINTEGER(plain, index, prv_array);
+    index=writeINTEGER(plain, index, p_array);
+    index=writeINTEGER(plain, index, q_array);
+    index=writeINTEGER(plain, index, ep_array);
+    index=writeINTEGER(plain, index, eq_array);
+    index=writeINTEGER(plain, index, c_array);
+    return plain;
+  }
+
+  boolean parse(byte [] plain){
+    /*
+    byte[] p_array;
+    byte[] q_array;
+    byte[] dmp1_array;
+    byte[] dmq1_array;
+    byte[] iqmp_array;
+    */
+    try{
+      int index=0;
+      int length=0;
+
+      if(vendor==VENDOR_FSECURE){
+	if(plain[index]!=0x30){                  // FSecure
+	  Buffer buf=new Buffer(plain);
+	  pub_array=buf.getMPIntBits();
+	  prv_array=buf.getMPIntBits();
+	  n_array=buf.getMPIntBits();
+	  byte[] u_array=buf.getMPIntBits();
+	  p_array=buf.getMPIntBits();
+	  q_array=buf.getMPIntBits();
+	  return true;
+	}
+	return false;
+      }
+
+      index++; // SEQUENCE
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+
+      if(plain[index]!=0x02)return false;
+      index++; // INTEGER
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      index+=length;
+
+//System.err.println("int: len="+length);
+//System.err.print(Integer.toHexString(plain[index-1]&0xff)+":");
+//System.err.println("");
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      n_array=new byte[length];
+      System.arraycopy(plain, index, n_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: N len="+length);
+for(int i=0; i<n_array.length; i++){
+System.err.print(Integer.toHexString(n_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      pub_array=new byte[length];
+      System.arraycopy(plain, index, pub_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: E len="+length);
+for(int i=0; i<pub_array.length; i++){
+System.err.print(Integer.toHexString(pub_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      prv_array=new byte[length];
+      System.arraycopy(plain, index, prv_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: prv len="+length);
+for(int i=0; i<prv_array.length; i++){
+System.err.print(Integer.toHexString(prv_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      p_array=new byte[length];
+      System.arraycopy(plain, index, p_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: P len="+length);
+for(int i=0; i<p_array.length; i++){
+System.err.print(Integer.toHexString(p_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      q_array=new byte[length];
+      System.arraycopy(plain, index, q_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: q len="+length);
+for(int i=0; i<q_array.length; i++){
+System.err.print(Integer.toHexString(q_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      ep_array=new byte[length];
+      System.arraycopy(plain, index, ep_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: ep len="+length);
+for(int i=0; i<ep_array.length; i++){
+System.err.print(Integer.toHexString(ep_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      eq_array=new byte[length];
+      System.arraycopy(plain, index, eq_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: eq len="+length);
+for(int i=0; i<eq_array.length; i++){
+System.err.print(Integer.toHexString(eq_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+      index++;
+      length=plain[index++]&0xff;
+      if((length&0x80)!=0){
+        int foo=length&0x7f; length=0;
+        while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
+      }
+      c_array=new byte[length];
+      System.arraycopy(plain, index, c_array, 0, length);
+      index+=length;
+/*
+System.err.println("int: c len="+length);
+for(int i=0; i<c_array.length; i++){
+System.err.print(Integer.toHexString(c_array[i]&0xff)+":");
+}
+System.err.println("");
+*/
+    }
+    catch(Exception e){
+      //System.err.println(e);
+      return false;
+    }
+    return true;
+  }
+
+
+  public byte[] getPublicKeyBlob(){
+    byte[] foo=super.getPublicKeyBlob();
+    if(foo!=null) return foo;
+
+    if(pub_array==null) return null;
+
+    Buffer buf=new Buffer(sshrsa.length+4+
+			  pub_array.length+4+ 
+			  n_array.length+4);
+    buf.putString(sshrsa);
+    buf.putString(pub_array);
+    buf.putString(n_array);
+    return buf.buffer;
+  }
+
+  private static final byte[] sshrsa=Util.str2byte("ssh-rsa");
+  byte[] getKeyTypeName(){return sshrsa;}
+  public int getKeyType(){return RSA;}
+
+  public int getKeySize(){return key_size; }
+  public void dispose(){
+    super.dispose();
+    Util.bzero(prv_array);
+  }
+}
diff --git a/java/com/jcraft/jsch/KnownHosts.java b/java/com/jcraft/jsch/KnownHosts.java
new file mode 100644
index 0000000..ac76477
--- /dev/null
+++ b/java/com/jcraft/jsch/KnownHosts.java
@@ -0,0 +1,506 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.*;
+
+public
+class KnownHosts implements HostKeyRepository{
+  private static final String _known_hosts="known_hosts";
+
+  /*
+  static final int SSHDSS=0;
+  static final int SSHRSA=1;
+  static final int UNKNOWN=2;
+  */
+
+  private JSch jsch=null;
+  private String known_hosts=null;
+  private java.util.Vector pool=null;
+
+  private MAC hmacsha1=null;
+
+  KnownHosts(JSch jsch){
+    super();
+    this.jsch=jsch;
+    pool=new java.util.Vector();
+  }
+
+  void setKnownHosts(String foo) throws JSchException{
+    try{
+      known_hosts=foo;
+      FileInputStream fis=new FileInputStream(foo);
+      setKnownHosts(fis);
+    }
+    catch(FileNotFoundException e){
+    } 
+  }
+  void setKnownHosts(InputStream foo) throws JSchException{
+    pool.removeAllElements();
+    StringBuffer sb=new StringBuffer();
+    byte i;
+    int j;
+    boolean error=false;
+    try{
+      InputStream fis=foo;
+      String host;
+      String key=null;
+      int type;
+      byte[] buf=new byte[1024];
+      int bufl=0;
+loop:
+      while(true){
+	bufl=0;
+        while(true){
+          j=fis.read();
+          if(j==-1){
+            if(bufl==0){ break loop; }
+            else{ break; }
+          }
+	  if(j==0x0d){ continue; }
+	  if(j==0x0a){ break; }
+          if(buf.length<=bufl){
+            if(bufl>1024*10) break;   // too long...
+            byte[] newbuf=new byte[buf.length*2];
+            System.arraycopy(buf, 0, newbuf, 0, buf.length);
+            buf=newbuf;
+          }
+          buf[bufl++]=(byte)j;
+	}
+
+	j=0;
+        while(j<bufl){
+          i=buf[j];
+	  if(i==' '||i=='\t'){ j++; continue; }
+	  if(i=='#'){
+	    addInvalidLine(Util.byte2str(buf, 0, bufl));
+	    continue loop;
+	  }
+	  break;
+	}
+	if(j>=bufl){ 
+	  addInvalidLine(Util.byte2str(buf, 0, bufl));
+	  continue loop; 
+	}
+
+        sb.setLength(0);
+        while(j<bufl){
+          i=buf[j++];
+          if(i==0x20 || i=='\t'){ break; }
+          sb.append((char)i);
+	}
+	host=sb.toString();
+	if(j>=bufl || host.length()==0){
+	  addInvalidLine(Util.byte2str(buf, 0, bufl));
+	  continue loop; 
+	}
+
+        sb.setLength(0);
+	type=-1;
+        while(j<bufl){
+          i=buf[j++];
+          if(i==0x20 || i=='\t'){ break; }
+          sb.append((char)i);
+	}
+	if(sb.toString().equals("ssh-dss")){ type=HostKey.SSHDSS; }
+	else if(sb.toString().equals("ssh-rsa")){ type=HostKey.SSHRSA; }
+	else { j=bufl; }
+	if(j>=bufl){
+	  addInvalidLine(Util.byte2str(buf, 0, bufl));
+	  continue loop; 
+	}
+
+        sb.setLength(0);
+        while(j<bufl){
+          i=buf[j++];
+          if(i==0x0d){ continue; }
+          if(i==0x0a){ break; }
+          sb.append((char)i);
+	}
+	key=sb.toString();
+	if(key.length()==0){
+	  addInvalidLine(Util.byte2str(buf, 0, bufl));
+	  continue loop; 
+	}
+
+	//System.err.println(host);
+	//System.err.println("|"+key+"|");
+
+	HostKey hk = null;
+        hk = new HashedHostKey(host, type, 
+                               Util.fromBase64(Util.str2byte(key), 0, 
+                                               key.length()));
+	pool.addElement(hk);
+      }
+      fis.close();
+      if(error){
+	throw new JSchException("KnownHosts: invalid format");
+      }
+    }
+    catch(Exception e){
+      if(e instanceof JSchException)
+	throw (JSchException)e;         
+      if(e instanceof Throwable)
+        throw new JSchException(e.toString(), (Throwable)e);
+      throw new JSchException(e.toString());
+    }
+  }
+  private void addInvalidLine(String line) throws JSchException {
+    HostKey hk = new HostKey(line, HostKey.UNKNOWN, null);
+    pool.addElement(hk);
+  }
+  String getKnownHostsFile(){ return known_hosts; }
+  public String getKnownHostsRepositoryID(){ return known_hosts; }
+
+  public int check(String host, byte[] key){
+    int result=NOT_INCLUDED;
+    if(host==null){
+      return result;
+    }
+
+    int type=getType(key);
+    HostKey hk;
+
+    synchronized(pool){
+      for(int i=0; i<pool.size(); i++){
+        hk=(HostKey)(pool.elementAt(i));
+        if(hk.isMatched(host) && hk.type==type){
+          if(Util.array_equals(hk.key, key)){
+            return OK;
+          }
+          else{
+            result=CHANGED;
+	  }
+        }
+      }
+    }
+
+    if(result==NOT_INCLUDED &&
+       host.startsWith("[") &&
+       host.indexOf("]:")>1
+       ){
+      return check(host.substring(1, host.indexOf("]:")), key);
+    }
+
+    return result;
+  }
+  public void add(HostKey hostkey, UserInfo userinfo){
+    int type=hostkey.type;
+    String host=hostkey.getHost();
+    byte[] key=hostkey.key;
+
+    HostKey hk=null;
+    synchronized(pool){
+      for(int i=0; i<pool.size(); i++){
+        hk=(HostKey)(pool.elementAt(i));
+        if(hk.isMatched(host) && hk.type==type){
+/*
+	  if(Util.array_equals(hk.key, key)){ return; }
+	  if(hk.host.equals(host)){
+	    hk.key=key;
+	    return;
+	  }
+	  else{
+	    hk.host=deleteSubString(hk.host, host);
+	    break;
+	  }
+*/
+        }
+      }
+    }
+
+    hk=hostkey;
+
+    pool.addElement(hk);
+
+    String bar=getKnownHostsRepositoryID();
+    if(bar!=null){
+      boolean foo=true;
+      File goo=new File(bar);
+      if(!goo.exists()){
+        foo=false;
+        if(userinfo!=null){
+          foo=userinfo.promptYesNo(bar+" does not exist.\n"+
+                                   "Are you sure you want to create it?"
+                                   );
+          goo=goo.getParentFile();
+          if(foo && goo!=null && !goo.exists()){
+            foo=userinfo.promptYesNo("The parent directory "+goo+" does not exist.\n"+
+                                     "Are you sure you want to create it?"
+                                     );
+            if(foo){
+              if(!goo.mkdirs()){
+                userinfo.showMessage(goo+" has not been created.");
+                foo=false;
+              }
+              else{
+                userinfo.showMessage(goo+" has been succesfully created.\nPlease check its access permission.");
+              }
+            }
+          }
+          if(goo==null)foo=false;
+        }
+      }
+      if(foo){
+        try{ 
+          sync(bar); 
+        }
+        catch(Exception e){ System.err.println("sync known_hosts: "+e); }
+      }
+    }
+  }
+
+  public HostKey[] getHostKey(){
+    return getHostKey(null, null);
+  }
+  public HostKey[] getHostKey(String host, String type){
+    synchronized(pool){
+      int count=0;
+      for(int i=0; i<pool.size(); i++){
+	HostKey hk=(HostKey)pool.elementAt(i);
+	if(hk.type==HostKey.UNKNOWN) continue;
+	if(host==null || 
+	   (hk.isMatched(host) && 
+	    (type==null || hk.getType().equals(type)))){
+	  count++;
+	}
+      }
+      if(count==0)return null;
+      HostKey[] foo=new HostKey[count];
+      int j=0;
+      for(int i=0; i<pool.size(); i++){
+	HostKey hk=(HostKey)pool.elementAt(i);
+	if(hk.type==HostKey.UNKNOWN) continue;
+	if(host==null || 
+	   (hk.isMatched(host) && 
+	    (type==null || hk.getType().equals(type)))){
+	  foo[j++]=hk;
+	}
+      }
+      return foo;
+    }
+  }
+  public void remove(String host, String type){
+    remove(host, type, null);
+  }
+  public void remove(String host, String type, byte[] key){
+    boolean sync=false;
+    synchronized(pool){
+    for(int i=0; i<pool.size(); i++){
+      HostKey hk=(HostKey)(pool.elementAt(i));
+      if(host==null ||
+	 (hk.isMatched(host) && 
+	  (type==null || (hk.getType().equals(type) &&
+			  (key==null || Util.array_equals(key, hk.key)))))){
+        String hosts=hk.getHost();
+        if(hosts.equals(host) || 
+           ((hk instanceof HashedHostKey) &&
+            ((HashedHostKey)hk).isHashed())){
+          pool.removeElement(hk);
+        }
+        else{
+          hk.host=deleteSubString(hosts, host);
+        }
+	sync=true;
+      }
+    }
+    }
+    if(sync){
+      try{sync();}catch(Exception e){};
+    }
+  }
+
+  protected void sync() throws IOException { 
+    if(known_hosts!=null)
+      sync(known_hosts); 
+  }
+  protected synchronized void sync(String foo) throws IOException {
+    if(foo==null) return;
+    FileOutputStream fos=new FileOutputStream(foo);
+    dump(fos);
+    fos.close();
+  }
+
+  private static final byte[] space={(byte)0x20};
+  private static final byte[] cr=Util.str2byte("\n");
+  void dump(OutputStream out) throws IOException {
+    try{
+      HostKey hk;
+      synchronized(pool){
+      for(int i=0; i<pool.size(); i++){
+        hk=(HostKey)(pool.elementAt(i));
+        //hk.dump(out);
+	String host=hk.getHost();
+	String type=hk.getType();
+	if(type.equals("UNKNOWN")){
+	  out.write(Util.str2byte(host));
+	  out.write(cr);
+	  continue;
+	}
+	out.write(Util.str2byte(host));
+	out.write(space);
+	out.write(Util.str2byte(type));
+	out.write(space);
+	out.write(Util.str2byte(hk.getKey()));
+	out.write(cr);
+      }
+      }
+    }
+    catch(Exception e){
+      System.err.println(e);
+    }
+  }
+  private int getType(byte[] key){
+    if(key[8]=='d') return HostKey.SSHDSS;
+    if(key[8]=='r') return HostKey.SSHRSA;
+    return HostKey.UNKNOWN;
+  }
+  private String deleteSubString(String hosts, String host){
+    int i=0;
+    int hostlen=host.length();
+    int hostslen=hosts.length();
+    int j;
+    while(i<hostslen){
+      j=hosts.indexOf(',', i);
+      if(j==-1) break;
+      if(!host.equals(hosts.substring(i, j))){
+        i=j+1;	  
+        continue;
+      }
+      return hosts.substring(0, i)+hosts.substring(j+1);
+    }
+    if(hosts.endsWith(host) && hostslen-i==hostlen){
+      return hosts.substring(0, (hostlen==hostslen) ? 0 :hostslen-hostlen-1);
+    }
+    return hosts;
+  }
+
+  private synchronized MAC getHMACSHA1(){
+    if(hmacsha1==null){
+      try{
+        Class c=Class.forName(jsch.getConfig("hmac-sha1"));
+        hmacsha1=(MAC)(c.newInstance());
+      }
+      catch(Exception e){ 
+        System.err.println("hmacsha1: "+e); 
+      }
+    }
+    return hmacsha1;
+  }
+
+  HostKey createHashedHostKey(String host, byte[]key) throws JSchException {
+    HashedHostKey hhk=new HashedHostKey(host, key);
+    hhk.hash();
+    return hhk;
+  } 
+  class HashedHostKey extends HostKey{
+    private static final String HASH_MAGIC="|1|";
+    private static final String HASH_DELIM="|";
+
+    private boolean hashed=false;
+    byte[] salt=null;
+    byte[] hash=null;
+
+
+    HashedHostKey(String host, byte[] key) throws JSchException {
+      this(host, GUESS, key);
+    }
+    HashedHostKey(String host, int type, byte[] key) throws JSchException {
+      super(host, type, key);
+      if(this.host.startsWith(HASH_MAGIC) &&
+         this.host.substring(HASH_MAGIC.length()).indexOf(HASH_DELIM)>0){
+        String data=this.host.substring(HASH_MAGIC.length());
+        String _salt=data.substring(0, data.indexOf(HASH_DELIM));
+        String _hash=data.substring(data.indexOf(HASH_DELIM)+1);
+        salt=Util.fromBase64(Util.str2byte(_salt), 0, _salt.length());
+        hash=Util.fromBase64(Util.str2byte(_hash), 0, _hash.length());
+        if(salt.length!=20 ||  // block size of hmac-sha1
+           hash.length!=20){
+          salt=null;
+          hash=null;
+          return;
+        }
+        hashed=true;
+      }
+    }
+
+    boolean isMatched(String _host){
+      if(!hashed){
+        return super.isMatched(_host);
+      }
+      MAC macsha1=getHMACSHA1();
+      try{
+        synchronized(macsha1){
+          macsha1.init(salt);
+          byte[] foo=Util.str2byte(_host);
+          macsha1.update(foo, 0, foo.length);
+          byte[] bar=new byte[macsha1.getBlockSize()];
+          macsha1.doFinal(bar, 0);
+          return Util.array_equals(hash, bar);
+        }
+      }
+      catch(Exception e){
+        System.out.println(e);
+      }
+      return false;
+    }
+
+    boolean isHashed(){
+      return hashed;
+    }
+
+    void hash(){
+      if(hashed)
+        return;
+      MAC macsha1=getHMACSHA1();
+      if(salt==null){
+        Random random=Session.random;
+        synchronized(random){
+          salt=new byte[macsha1.getBlockSize()];
+          random.fill(salt, 0, salt.length);
+        }
+      }
+      try{
+        synchronized(macsha1){
+          macsha1.init(salt);
+          byte[] foo=Util.str2byte(host);
+          macsha1.update(foo, 0, foo.length);
+          hash=new byte[macsha1.getBlockSize()];
+          macsha1.doFinal(hash, 0);
+        }
+      }
+      catch(Exception e){
+      }
+      host=HASH_MAGIC+Util.byte2str(Util.toBase64(salt, 0, salt.length))+
+        HASH_DELIM+Util.byte2str(Util.toBase64(hash, 0, hash.length));
+      hashed=true;
+    }
+  }
+}
diff --git a/java/com/jcraft/jsch/LICENSE.txt b/java/com/jcraft/jsch/LICENSE.txt
new file mode 100644
index 0000000..81c8eac
--- /dev/null
+++ b/java/com/jcraft/jsch/LICENSE.txt
@@ -0,0 +1,30 @@
+JSch 0.0.* was released under the GNU LGPL license.  Later, we have switched 
+over to a BSD-style license. 
+
+------------------------------------------------------------------------------
+Copyright (c) 2002-2012 Atsuhiko Yamanaka, JCraft,Inc. 
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/java/com/jcraft/jsch/LocalIdentityRepository.java b/java/com/jcraft/jsch/LocalIdentityRepository.java
new file mode 100644
index 0000000..be81415
--- /dev/null
+++ b/java/com/jcraft/jsch/LocalIdentityRepository.java
@@ -0,0 +1,90 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.util.Vector;
+
+class LocalIdentityRepository implements IdentityRepository {
+
+  private Vector identities = new Vector();
+  private JSch jsch;
+
+  LocalIdentityRepository(JSch jsch){
+    this.jsch = jsch;
+  }
+
+  public synchronized Vector getIdentities() {
+    Vector v = new Vector();
+    for(int i=0; i<identities.size(); i++){
+      v.addElement(identities.elementAt(i));
+    }
+    return v;
+  }
+
+  public synchronized void add(Identity identity) {
+    if(!identities.contains(identity)) {
+      identities.addElement(identity);
+    }
+  }
+
+  public synchronized boolean add(byte[] identity) {
+    try{
+      Identity _identity =
+        IdentityFile.newInstance("from remote:", identity, null, jsch);
+      identities.addElement(_identity);
+      return true;
+    }
+    catch(JSchException e){
+      return false;
+    }
+  }
+
+  public synchronized boolean remove(byte[] blob) {
+    if(blob == null) return false;
+    for(int i=0; i<identities.size(); i++) {
+      Identity _identity = (Identity)(identities.elementAt(i));
+      byte[] _blob = _identity.getPublicKeyBlob();
+      if(_blob == null || !Util.array_equals(blob, _blob))
+        continue;
+      identities.removeElement(_identity);
+      _identity.clear();
+      return true;
+    }
+    return false;
+  }
+
+  public synchronized void removeAll() {
+    for(int i=0; i<identities.size(); i++) {
+      Identity identity=(Identity)(identities.elementAt(i));
+      identity.clear();
+    }
+    identities.removeAllElements();
+  } 
+}
diff --git a/java/com/jcraft/jsch/Logger.java b/java/com/jcraft/jsch/Logger.java
new file mode 100644
index 0000000..1263722
--- /dev/null
+++ b/java/com/jcraft/jsch/Logger.java
@@ -0,0 +1,54 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface Logger{
+
+  public final int DEBUG=0;
+  public final int INFO=1;
+  public final int WARN=2;
+  public final int ERROR=3;
+  public final int FATAL=4;
+
+  public boolean isEnabled(int level);
+
+  public void log(int level, String message);
+
+  /*
+  public final Logger SIMPLE_LOGGER=new Logger(){
+      public boolean isEnabled(int level){return true;}
+      public void log(int level, String message){System.err.println(message);}
+    };
+  final Logger DEVNULL=new Logger(){
+      public boolean isEnabled(int level){return false;}
+      public void log(int level, String message){}
+    };
+  */
+}
diff --git a/java/com/jcraft/jsch/MAC.java b/java/com/jcraft/jsch/MAC.java
new file mode 100644
index 0000000..0475ce2
--- /dev/null
+++ b/java/com/jcraft/jsch/MAC.java
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface MAC{
+  String getName();
+  int getBlockSize(); 
+  void init(byte[] key) throws Exception; 
+  void update(byte[] foo, int start, int len);
+  void update(int foo);
+  void doFinal(byte[] buf, int offset);
+}
diff --git a/java/com/jcraft/jsch/Packet.java b/java/com/jcraft/jsch/Packet.java
new file mode 100644
index 0000000..9c44157
--- /dev/null
+++ b/java/com/jcraft/jsch/Packet.java
@@ -0,0 +1,115 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class Packet{
+
+  private static Random random=null;
+  static void setRandom(Random foo){ random=foo;}
+
+  Buffer buffer;
+  byte[] ba4=new byte[4]; 
+  public Packet(Buffer buffer){
+    this.buffer=buffer;
+  }
+  public void reset(){
+    buffer.index=5;
+  }
+  void padding(int bsize){
+    int len=buffer.index;
+    int pad=(-len)&(bsize-1);
+    if(pad<bsize){
+      pad+=bsize;
+    }
+    len=len+pad-4;
+    ba4[0]=(byte)(len>>>24);
+    ba4[1]=(byte)(len>>>16);
+    ba4[2]=(byte)(len>>>8);
+    ba4[3]=(byte)(len);
+    System.arraycopy(ba4, 0, buffer.buffer, 0, 4);
+    buffer.buffer[4]=(byte)pad;
+    synchronized(random){
+      random.fill(buffer.buffer, buffer.index, pad);
+    }
+    buffer.skip(pad);
+    //buffer.putPad(pad);
+/*
+for(int i=0; i<buffer.index; i++){
+System.err.print(Integer.toHexString(buffer.buffer[i]&0xff)+":");
+}
+System.err.println("");
+*/
+  }
+
+  int shift(int len, int bsize, int mac){
+    int s=len+5+9;
+    int pad=(-s)&(bsize-1);
+    if(pad<bsize)pad+=bsize;
+    s+=pad;
+    s+=mac;
+    s+=32; // margin for deflater; deflater may inflate data
+
+    /**/
+    if(buffer.buffer.length<s+buffer.index-5-9-len){
+      byte[] foo=new byte[s+buffer.index-5-9-len];
+      System.arraycopy(buffer.buffer, 0, foo, 0, buffer.buffer.length);
+      buffer.buffer=foo;
+    }
+    /**/
+
+//if(buffer.buffer.length<len+5+9)
+//  System.err.println("buffer.buffer.length="+buffer.buffer.length+" len+5+9="+(len+5+9));
+
+//if(buffer.buffer.length<s)
+//  System.err.println("buffer.buffer.length="+buffer.buffer.length+" s="+(s));
+
+    System.arraycopy(buffer.buffer, 
+		     len+5+9, 
+		     buffer.buffer, s, buffer.index-5-9-len);
+
+    buffer.index=10;
+    buffer.putInt(len);
+    buffer.index=len+5+9;
+    return s;
+  }
+  void unshift(byte command, int recipient, int s, int len){
+    System.arraycopy(buffer.buffer, 
+		     s, 
+		     buffer.buffer, 5+9, len);
+    buffer.buffer[5]=command;
+    buffer.index=6;
+    buffer.putInt(recipient);
+    buffer.putInt(len);
+    buffer.index=len+5+9;
+  }
+  Buffer getBuffer(){
+    return buffer;
+  }
+}
diff --git a/java/com/jcraft/jsch/PortWatcher.java b/java/com/jcraft/jsch/PortWatcher.java
new file mode 100644
index 0000000..508e16b
--- /dev/null
+++ b/java/com/jcraft/jsch/PortWatcher.java
@@ -0,0 +1,194 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.net.*;
+import java.io.*;
+
+class PortWatcher implements Runnable{
+  private static java.util.Vector pool=new java.util.Vector();
+  private static InetAddress anyLocalAddress=null;
+  static{
+    // 0.0.0.0
+/*
+    try{ anyLocalAddress=InetAddress.getByAddress(new byte[4]); }
+    catch(UnknownHostException e){
+    }
+*/
+    try{ anyLocalAddress=InetAddress.getByName("0.0.0.0"); }
+    catch(UnknownHostException e){
+    }
+  }
+
+  Session session;
+  int lport;
+  int rport;
+  String host;
+  InetAddress boundaddress;
+  Runnable thread;
+  ServerSocket ss;
+
+  static String[] getPortForwarding(Session session){
+    java.util.Vector foo=new java.util.Vector();
+    synchronized(pool){
+      for(int i=0; i<pool.size(); i++){
+	PortWatcher p=(PortWatcher)(pool.elementAt(i));
+	if(p.session==session){
+	  foo.addElement(p.lport+":"+p.host+":"+p.rport);
+	}
+      }
+    }
+    String[] bar=new String[foo.size()];
+    for(int i=0; i<foo.size(); i++){
+      bar[i]=(String)(foo.elementAt(i));
+    }
+    return bar;
+  }
+  static PortWatcher getPort(Session session, String address, int lport) throws JSchException{
+    InetAddress addr;
+    try{
+      addr=InetAddress.getByName(address);
+    }
+    catch(UnknownHostException uhe){
+      throw new JSchException("PortForwardingL: invalid address "+address+" specified.", uhe);
+    }
+    synchronized(pool){
+      for(int i=0; i<pool.size(); i++){
+	PortWatcher p=(PortWatcher)(pool.elementAt(i));
+	if(p.session==session && p.lport==lport){
+	  if(/*p.boundaddress.isAnyLocalAddress() ||*/
+             (anyLocalAddress!=null &&  p.boundaddress.equals(anyLocalAddress)) ||
+	     p.boundaddress.equals(addr))
+	  return p;
+	}
+      }
+      return null;
+    }
+  }
+  static PortWatcher addPort(Session session, String address, int lport, String host, int rport, ServerSocketFactory ssf) throws JSchException{
+    if(getPort(session, address, lport)!=null){
+      throw new JSchException("PortForwardingL: local port "+ address+":"+lport+" is already registered.");
+    }
+    PortWatcher pw=new PortWatcher(session, address, lport, host, rport, ssf);
+    pool.addElement(pw);
+    return pw;
+  }
+  static void delPort(Session session, String address, int lport) throws JSchException{
+    PortWatcher pw=getPort(session, address, lport);
+    if(pw==null){
+      throw new JSchException("PortForwardingL: local port "+address+":"+lport+" is not registered.");
+    }
+    pw.delete();
+    pool.removeElement(pw);
+  }
+  static void delPort(Session session){
+    synchronized(pool){
+      PortWatcher[] foo=new PortWatcher[pool.size()];
+      int count=0;
+      for(int i=0; i<pool.size(); i++){
+	PortWatcher p=(PortWatcher)(pool.elementAt(i));
+	if(p.session==session) {
+	  p.delete();
+	  foo[count++]=p;
+	}
+      }
+      for(int i=0; i<count; i++){
+	PortWatcher p=foo[i];
+	pool.removeElement(p);
+      }
+    }
+  }
+  PortWatcher(Session session, 
+	      String address, int lport, 
+	      String host, int rport,
+              ServerSocketFactory factory) throws JSchException{
+    this.session=session;
+    this.lport=lport;
+    this.host=host;
+    this.rport=rport;
+    try{
+      boundaddress=InetAddress.getByName(address);
+      ss=(factory==null) ? 
+        new ServerSocket(lport, 0, boundaddress) :
+        factory.createServerSocket(lport, 0, boundaddress);
+    }
+    catch(Exception e){ 
+      //System.err.println(e);
+      String message="PortForwardingL: local port "+address+":"+lport+" cannot be bound.";
+      if(e instanceof Throwable)
+        throw new JSchException(message, (Throwable)e);
+      throw new JSchException(message);
+    }
+    if(lport==0){
+      int assigned=ss.getLocalPort();
+      if(assigned!=-1)
+        this.lport=assigned;
+    }
+  }
+
+  public void run(){
+    thread=this;
+    try{
+      while(thread!=null){
+        Socket socket=ss.accept();
+	socket.setTcpNoDelay(true);
+        InputStream in=socket.getInputStream();
+        OutputStream out=socket.getOutputStream();
+        ChannelDirectTCPIP channel=new ChannelDirectTCPIP();
+        channel.init();
+        channel.setInputStream(in);
+        channel.setOutputStream(out);
+	session.addChannel(channel);
+	((ChannelDirectTCPIP)channel).setHost(host);
+	((ChannelDirectTCPIP)channel).setPort(rport);
+	((ChannelDirectTCPIP)channel).setOrgIPAddress(socket.getInetAddress().getHostAddress());
+	((ChannelDirectTCPIP)channel).setOrgPort(socket.getPort());
+        channel.connect();
+	if(channel.exitstatus!=-1){
+	}
+      }
+    }
+    catch(Exception e){
+      //System.err.println("! "+e);
+    }
+
+    delete();
+  }
+
+  void delete(){
+    thread=null;
+    try{ 
+      if(ss!=null)ss.close();
+      ss=null;
+    }
+    catch(Exception e){
+    }
+  }
+}
diff --git a/java/com/jcraft/jsch/Proxy.java b/java/com/jcraft/jsch/Proxy.java
new file mode 100644
index 0000000..7d05caa
--- /dev/null
+++ b/java/com/jcraft/jsch/Proxy.java
@@ -0,0 +1,40 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.*;
+import java.net.Socket;
+public interface Proxy{
+  void connect(SocketFactory socket_factory, String host, int port, int timeout) throws Exception;
+  InputStream getInputStream();
+  OutputStream getOutputStream();
+  Socket getSocket();
+  void close();
+}
diff --git a/java/com/jcraft/jsch/ProxyHTTP.java b/java/com/jcraft/jsch/ProxyHTTP.java
new file mode 100644
index 0000000..df23114
--- /dev/null
+++ b/java/com/jcraft/jsch/ProxyHTTP.java
@@ -0,0 +1,180 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.*;
+import java.net.*;
+
+public class ProxyHTTP implements Proxy{
+  private static int DEFAULTPORT=80;
+  private String proxy_host;
+  private int proxy_port;
+  private InputStream in;
+  private OutputStream out;
+  private Socket socket;
+
+  private String user;
+  private String passwd;
+
+  public ProxyHTTP(String proxy_host){
+    int port=DEFAULTPORT;
+    String host=proxy_host;
+    if(proxy_host.indexOf(':')!=-1){
+      try{
+	host=proxy_host.substring(0, proxy_host.indexOf(':'));
+	port=Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':')+1));
+      }
+      catch(Exception e){
+      }
+    }
+    this.proxy_host=host;
+    this.proxy_port=port;
+  }
+  public ProxyHTTP(String proxy_host, int proxy_port){
+    this.proxy_host=proxy_host;
+    this.proxy_port=proxy_port;
+  }
+  public void setUserPasswd(String user, String passwd){
+    this.user=user;
+    this.passwd=passwd;
+  }
+  public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws JSchException{
+    try{
+      if(socket_factory==null){
+        socket=Util.createSocket(proxy_host, proxy_port, timeout);    
+        in=socket.getInputStream();
+        out=socket.getOutputStream();
+      }
+      else{
+        socket=socket_factory.createSocket(proxy_host, proxy_port);
+        in=socket_factory.getInputStream(socket);
+        out=socket_factory.getOutputStream(socket);
+      }
+      if(timeout>0){
+        socket.setSoTimeout(timeout);
+      }
+      socket.setTcpNoDelay(true);
+
+      out.write(Util.str2byte("CONNECT "+host+":"+port+" HTTP/1.0\r\n"));
+
+      if(user!=null && passwd!=null){
+	byte[] code=Util.str2byte(user+":"+passwd);
+	code=Util.toBase64(code, 0, code.length);
+	out.write(Util.str2byte("Proxy-Authorization: Basic "));
+	out.write(code);
+	out.write(Util.str2byte("\r\n"));
+      }
+
+      out.write(Util.str2byte("\r\n"));
+      out.flush();
+
+      int foo=0;
+
+      StringBuffer sb=new StringBuffer();
+      while(foo>=0){
+        foo=in.read(); if(foo!=13){sb.append((char)foo);  continue;}
+        foo=in.read(); if(foo!=10){continue;}
+        break;
+      }
+      if(foo<0){
+        throw new IOException();
+      }
+
+      String response=sb.toString(); 
+      String reason="Unknow reason";
+      int code=-1;
+      try{
+        foo=response.indexOf(' ');
+        int bar=response.indexOf(' ', foo+1);
+        code=Integer.parseInt(response.substring(foo+1, bar));
+        reason=response.substring(bar+1);
+      }
+      catch(Exception e){
+      }
+      if(code!=200){
+        throw new IOException("proxy error: "+reason);
+      }
+
+      /*
+      while(foo>=0){
+        foo=in.read(); if(foo!=13) continue;
+        foo=in.read(); if(foo!=10) continue;
+        foo=in.read(); if(foo!=13) continue;      
+        foo=in.read(); if(foo!=10) continue;
+        break;
+      }
+      */
+
+      int count=0;
+      while(true){
+        count=0;
+        while(foo>=0){
+          foo=in.read(); if(foo!=13){count++;  continue;}
+          foo=in.read(); if(foo!=10){continue;}
+          break;
+        }
+        if(foo<0){
+          throw new IOException();
+        }
+        if(count==0)break;
+      }
+    }
+    catch(RuntimeException e){
+      throw e;
+    }
+    catch(Exception e){
+      try{ if(socket!=null)socket.close(); }
+      catch(Exception eee){
+      }
+      String message="ProxyHTTP: "+e.toString();
+      if(e instanceof Throwable)
+        throw new JSchException(message, (Throwable)e);
+      throw new JSchException(message);
+    }
+  }
+  public InputStream getInputStream(){ return in; }
+  public OutputStream getOutputStream(){ return out; }
+  public Socket getSocket(){ return socket; }
+  public void close(){
+    try{
+      if(in!=null)in.close();
+      if(out!=null)out.close();
+      if(socket!=null)socket.close();
+    }
+    catch(Exception e){
+    }
+    in=null;
+    out=null;
+    socket=null;
+  }
+  public static int getDefaultPort(){
+    return DEFAULTPORT;
+  }
+}
diff --git a/java/com/jcraft/jsch/ProxySOCKS4.java b/java/com/jcraft/jsch/ProxySOCKS4.java
new file mode 100644
index 0000000..cb50616
--- /dev/null
+++ b/java/com/jcraft/jsch/ProxySOCKS4.java
@@ -0,0 +1,212 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ This file depends on following documents,
+   - SOCKS: A protocol for TCP proxy across firewalls, Ying-Da Lee
+     http://www.socks.nec.com/protocol/socks4.protocol
+ */
+
+package com.jcraft.jsch;
+
+import java.io.*;
+import java.net.*;
+
+public class ProxySOCKS4 implements Proxy{
+  private static int DEFAULTPORT=1080;
+  private String proxy_host;
+  private int proxy_port;
+  private InputStream in;
+  private OutputStream out;
+  private Socket socket;
+  private String user;
+  private String passwd;
+
+  public ProxySOCKS4(String proxy_host){
+    int port=DEFAULTPORT;
+    String host=proxy_host;
+    if(proxy_host.indexOf(':')!=-1){
+      try{
+	host=proxy_host.substring(0, proxy_host.indexOf(':'));
+	port=Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':')+1));
+      }
+      catch(Exception e){
+      }
+    }
+    this.proxy_host=host;
+    this.proxy_port=port;
+  }
+  public ProxySOCKS4(String proxy_host, int proxy_port){
+    this.proxy_host=proxy_host;
+    this.proxy_port=proxy_port;
+  }
+  public void setUserPasswd(String user, String passwd){
+    this.user=user;
+    this.passwd=passwd;
+  }
+  public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws JSchException{
+    try{
+      if(socket_factory==null){
+        socket=Util.createSocket(proxy_host, proxy_port, timeout);
+        //socket=new Socket(proxy_host, proxy_port);    
+        in=socket.getInputStream();
+        out=socket.getOutputStream();
+      }
+      else{
+        socket=socket_factory.createSocket(proxy_host, proxy_port);
+        in=socket_factory.getInputStream(socket);
+        out=socket_factory.getOutputStream(socket);
+      }
+      if(timeout>0){
+        socket.setSoTimeout(timeout);
+      }
+      socket.setTcpNoDelay(true);
+
+      byte[] buf=new byte[1024];
+      int index=0;
+
+/*
+   1) CONNECT
+   
+   The client connects to the SOCKS server and sends a CONNECT request when
+   it wants to establish a connection to an application server. The client
+   includes in the request packet the IP address and the port number of the
+   destination host, and userid, in the following format.
+   
+               +----+----+----+----+----+----+----+----+----+----+....+----+
+               | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
+               +----+----+----+----+----+----+----+----+----+----+....+----+
+   # of bytes:   1    1      2              4           variable       1
+   
+   VN is the SOCKS protocol version number and should be 4. CD is the
+   SOCKS command code and should be 1 for CONNECT request. NULL is a byte
+   of all zero bits.
+*/
+     
+      index=0;
+      buf[index++]=4;
+      buf[index++]=1;
+
+      buf[index++]=(byte)(port>>>8);
+      buf[index++]=(byte)(port&0xff);
+
+      try{
+        InetAddress addr=InetAddress.getByName(host);
+        byte[] byteAddress = addr.getAddress();
+        for (int i = 0; i < byteAddress.length; i++) {
+          buf[index++]=byteAddress[i];
+        }
+      }
+      catch(UnknownHostException uhe){
+        throw new JSchException("ProxySOCKS4: "+uhe.toString(), uhe);
+      }
+
+      if(user!=null){
+        System.arraycopy(Util.str2byte(user), 0, buf, index, user.length());
+        index+=user.length();
+      }
+      buf[index++]=0;
+      out.write(buf, 0, index);
+
+/*
+   The SOCKS server checks to see whether such a request should be granted
+   based on any combination of source IP address, destination IP address,
+   destination port number, the userid, and information it may obtain by
+   consulting IDENT, cf. RFC 1413.  If the request is granted, the SOCKS
+   server makes a connection to the specified port of the destination host.
+   A reply packet is sent to the client when this connection is established,
+   or when the request is rejected or the operation fails. 
+   
+               +----+----+----+----+----+----+----+----+
+               | VN | CD | DSTPORT |      DSTIP        |
+               +----+----+----+----+----+----+----+----+
+   # of bytes:   1    1      2              4
+   
+   VN is the version of the reply code and should be 0. CD is the result
+   code with one of the following values:
+   
+   90: request granted
+   91: request rejected or failed
+   92: request rejected becasue SOCKS server cannot connect to
+       identd on the client
+   93: request rejected because the client program and identd
+       report different user-ids
+   
+   The remaining fields are ignored.
+*/
+
+      int len=8;
+      int s=0;
+      while(s<len){
+        int i=in.read(buf, s, len-s);
+        if(i<=0){
+          throw new JSchException("ProxySOCKS4: stream is closed");
+        }
+        s+=i;
+      }
+      if(buf[0]!=0){
+        throw new JSchException("ProxySOCKS4: server returns VN "+buf[0]);
+      }
+      if(buf[1]!=90){
+        try{ socket.close(); }
+	catch(Exception eee){
+	}
+        String message="ProxySOCKS4: server returns CD "+buf[1];
+        throw new JSchException(message);
+      }
+    }
+    catch(RuntimeException e){
+      throw e;
+    }
+    catch(Exception e){
+      try{ if(socket!=null)socket.close(); }
+      catch(Exception eee){
+      }
+      throw new JSchException("ProxySOCKS4: "+e.toString());
+    }
+  }
+  public InputStream getInputStream(){ return in; }
+  public OutputStream getOutputStream(){ return out; }
+  public Socket getSocket(){ return socket; }
+  public void close(){
+    try{
+      if(in!=null)in.close();
+      if(out!=null)out.close();
+      if(socket!=null)socket.close();
+    }
+    catch(Exception e){
+    }
+    in=null;
+    out=null;
+    socket=null;
+  }
+  public static int getDefaultPort(){
+    return DEFAULTPORT;
+  }
+}
diff --git a/java/com/jcraft/jsch/ProxySOCKS5.java b/java/com/jcraft/jsch/ProxySOCKS5.java
new file mode 100644
index 0000000..7960135
--- /dev/null
+++ b/java/com/jcraft/jsch/ProxySOCKS5.java
@@ -0,0 +1,349 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ This file depends on following documents,
+   - RFC 1928  SOCKS Protocol Verseion 5  
+   - RFC 1929  Username/Password Authentication for SOCKS V5. 
+ */
+
+package com.jcraft.jsch;
+
+import java.io.*;
+import java.net.*;
+
+public class ProxySOCKS5 implements Proxy{
+  private static int DEFAULTPORT=1080;
+  private String proxy_host;
+  private int proxy_port;
+  private InputStream in;
+  private OutputStream out;
+  private Socket socket;
+  private String user;
+  private String passwd;
+
+  public ProxySOCKS5(String proxy_host){
+    int port=DEFAULTPORT;
+    String host=proxy_host;
+    if(proxy_host.indexOf(':')!=-1){
+      try{
+	host=proxy_host.substring(0, proxy_host.indexOf(':'));
+	port=Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':')+1));
+      }
+      catch(Exception e){
+      }
+    }
+    this.proxy_host=host;
+    this.proxy_port=port;
+  }
+  public ProxySOCKS5(String proxy_host, int proxy_port){
+    this.proxy_host=proxy_host;
+    this.proxy_port=proxy_port;
+  }
+  public void setUserPasswd(String user, String passwd){
+    this.user=user;
+    this.passwd=passwd;
+  }
+  public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws JSchException{
+    try{
+      if(socket_factory==null){
+        socket=Util.createSocket(proxy_host, proxy_port, timeout);
+        //socket=new Socket(proxy_host, proxy_port);    
+        in=socket.getInputStream();
+        out=socket.getOutputStream();
+      }
+      else{
+        socket=socket_factory.createSocket(proxy_host, proxy_port);
+        in=socket_factory.getInputStream(socket);
+        out=socket_factory.getOutputStream(socket);
+      }
+      if(timeout>0){
+        socket.setSoTimeout(timeout);
+      }
+      socket.setTcpNoDelay(true);
+
+      byte[] buf=new byte[1024];
+      int index=0;
+
+/*
+                   +----+----------+----------+
+                   |VER | NMETHODS | METHODS  |
+                   +----+----------+----------+
+                   | 1  |    1     | 1 to 255 |
+                   +----+----------+----------+
+
+   The VER field is set to X'05' for this version of the protocol.  The
+   NMETHODS field contains the number of method identifier octets that
+   appear in the METHODS field.
+
+   The values currently defined for METHOD are:
+
+          o  X'00' NO AUTHENTICATION REQUIRED
+          o  X'01' GSSAPI
+          o  X'02' USERNAME/PASSWORD
+          o  X'03' to X'7F' IANA ASSIGNED
+          o  X'80' to X'FE' RESERVED FOR PRIVATE METHODS
+          o  X'FF' NO ACCEPTABLE METHODS
+*/
+
+      buf[index++]=5;
+
+      buf[index++]=2;
+      buf[index++]=0;           // NO AUTHENTICATION REQUIRED
+      buf[index++]=2;           // USERNAME/PASSWORD
+
+      out.write(buf, 0, index);
+
+/*
+    The server selects from one of the methods given in METHODS, and
+    sends a METHOD selection message:
+
+                         +----+--------+
+                         |VER | METHOD |
+                         +----+--------+
+                         | 1  |   1    |
+                         +----+--------+
+*/
+      //in.read(buf, 0, 2);
+      fill(in, buf, 2);
+ 
+      boolean check=false;
+      switch((buf[1])&0xff){
+        case 0:                // NO AUTHENTICATION REQUIRED
+          check=true;
+          break;
+        case 2:                // USERNAME/PASSWORD
+          if(user==null || passwd==null)break;
+
+/*
+   Once the SOCKS V5 server has started, and the client has selected the
+   Username/Password Authentication protocol, the Username/Password
+   subnegotiation begins.  This begins with the client producing a
+   Username/Password request:
+
+           +----+------+----------+------+----------+
+           |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
+           +----+------+----------+------+----------+
+           | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
+           +----+------+----------+------+----------+
+
+   The VER field contains the current version of the subnegotiation,
+   which is X'01'. The ULEN field contains the length of the UNAME field
+   that follows. The UNAME field contains the username as known to the
+   source operating system. The PLEN field contains the length of the
+   PASSWD field that follows. The PASSWD field contains the password
+   association with the given UNAME.
+*/
+          index=0;
+          buf[index++]=1;
+          buf[index++]=(byte)(user.length());
+	  System.arraycopy(Util.str2byte(user), 0, buf, index, user.length());
+	  index+=user.length();
+          buf[index++]=(byte)(passwd.length());
+	  System.arraycopy(Util.str2byte(passwd), 0, buf, index, passwd.length());
+	  index+=passwd.length();
+
+          out.write(buf, 0, index);
+
+/*
+   The server verifies the supplied UNAME and PASSWD, and sends the
+   following response:
+
+                        +----+--------+
+                        |VER | STATUS |
+                        +----+--------+
+                        | 1  |   1    |
+                        +----+--------+
+
+   A STATUS field of X'00' indicates success. If the server returns a
+   `failure' (STATUS value other than X'00') status, it MUST close the
+   connection.
+*/
+          //in.read(buf, 0, 2);
+          fill(in, buf, 2);
+          if(buf[1]==0)
+            check=true;
+          break;
+        default:
+      }
+
+      if(!check){
+        try{ socket.close(); }
+	catch(Exception eee){
+	}
+        throw new JSchException("fail in SOCKS5 proxy");
+      }
+
+/*
+      The SOCKS request is formed as follows:
+
+        +----+-----+-------+------+----------+----------+
+        |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
+        +----+-----+-------+------+----------+----------+
+        | 1  |  1  | X'00' |  1   | Variable |    2     |
+        +----+-----+-------+------+----------+----------+
+
+      Where:
+
+      o  VER    protocol version: X'05'
+      o  CMD
+         o  CONNECT X'01'
+         o  BIND X'02'
+         o  UDP ASSOCIATE X'03'
+      o  RSV    RESERVED
+         o  ATYP   address type of following address
+         o  IP V4 address: X'01'
+         o  DOMAINNAME: X'03'
+         o  IP V6 address: X'04'
+      o  DST.ADDR       desired destination address
+      o  DST.PORT desired destination port in network octet
+         order
+*/
+     
+      index=0;
+      buf[index++]=5;
+      buf[index++]=1;       // CONNECT
+      buf[index++]=0;
+
+      byte[] hostb=Util.str2byte(host);
+      int len=hostb.length;
+      buf[index++]=3;      // DOMAINNAME
+      buf[index++]=(byte)(len);
+      System.arraycopy(hostb, 0, buf, index, len);
+      index+=len;
+      buf[index++]=(byte)(port>>>8);
+      buf[index++]=(byte)(port&0xff);
+
+      out.write(buf, 0, index);
+
+/*
+   The SOCKS request information is sent by the client as soon as it has
+   established a connection to the SOCKS server, and completed the
+   authentication negotiations.  The server evaluates the request, and
+   returns a reply formed as follows:
+
+        +----+-----+-------+------+----------+----------+
+        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
+        +----+-----+-------+------+----------+----------+
+        | 1  |  1  | X'00' |  1   | Variable |    2     |
+        +----+-----+-------+------+----------+----------+
+
+   Where:
+
+   o  VER    protocol version: X'05'
+   o  REP    Reply field:
+      o  X'00' succeeded
+      o  X'01' general SOCKS server failure
+      o  X'02' connection not allowed by ruleset
+      o  X'03' Network unreachable
+      o  X'04' Host unreachable
+      o  X'05' Connection refused
+      o  X'06' TTL expired
+      o  X'07' Command not supported
+      o  X'08' Address type not supported
+      o  X'09' to X'FF' unassigned
+    o  RSV    RESERVED
+    o  ATYP   address type of following address
+      o  IP V4 address: X'01'
+      o  DOMAINNAME: X'03'
+      o  IP V6 address: X'04'
+    o  BND.ADDR       server bound address
+    o  BND.PORT       server bound port in network octet order
+*/
+
+      //in.read(buf, 0, 4);
+      fill(in, buf, 4);
+
+      if(buf[1]!=0){
+        try{ socket.close(); }
+	catch(Exception eee){
+	}
+        throw new JSchException("ProxySOCKS5: server returns "+buf[1]);
+      }
+
+      switch(buf[3]&0xff){
+        case 1:
+          //in.read(buf, 0, 6);
+          fill(in, buf, 6);
+	  break;
+        case 3:
+          //in.read(buf, 0, 1);
+          fill(in, buf, 1);
+          //in.read(buf, 0, buf[0]+2);
+          fill(in, buf, (buf[0]&0xff)+2);
+	  break;
+        case 4:
+          //in.read(buf, 0, 18);
+          fill(in, buf, 18);
+          break;
+        default:
+      }
+    }
+    catch(RuntimeException e){
+      throw e;
+    }
+    catch(Exception e){
+      try{ if(socket!=null)socket.close(); }
+      catch(Exception eee){
+      }
+      String message="ProxySOCKS5: "+e.toString();
+      if(e instanceof Throwable)
+        throw new JSchException(message, (Throwable)e);
+      throw new JSchException(message);
+    }
+  }
+  public InputStream getInputStream(){ return in; }
+  public OutputStream getOutputStream(){ return out; }
+  public Socket getSocket(){ return socket; }
+  public void close(){
+    try{
+      if(in!=null)in.close();
+      if(out!=null)out.close();
+      if(socket!=null)socket.close();
+    }
+    catch(Exception e){
+    }
+    in=null;
+    out=null;
+    socket=null;
+  }
+  public static int getDefaultPort(){
+    return DEFAULTPORT;
+  }
+  private void fill(InputStream in, byte[] buf, int len) throws JSchException, IOException{
+    int s=0;
+    while(s<len){
+      int i=in.read(buf, s, len-s);
+      if(i<=0){
+        throw new JSchException("ProxySOCKS5: stream is closed");
+      }
+      s+=i;
+    }
+  }
+}
diff --git a/java/com/jcraft/jsch/README b/java/com/jcraft/jsch/README
new file mode 100644
index 0000000..a52886e
--- /dev/null
+++ b/java/com/jcraft/jsch/README
@@ -0,0 +1,221 @@
+
+                                      JSch
+
+                                Java Secure Channel
+                         by ymnk@jcraft.com, JCraft,Inc.
+
+                            http://www.jcraft.com/jsch/
+
+Last modified: Wed Nov  1 14:43:31 UTC 2006
+
+
+Description
+===========
+JSch is a pure Java implementation of SSH2.  JSch allows you to 
+connect to an sshd server and use port forwarding, X11 forwarding, 
+file transfer, etc., and you can integrate its functionality
+into your own Java programs. JSch is licensed under BSD style license.
+
+
+Documentation
+=============
+* README files all over the source tree have info related to the stuff
+  in the directories. 
+* ChangeLog: what changed from the previous version?
+
+
+Directories & Files in the Source Tree
+======================================
+* src/com/ has source trees of JSch
+* example/ has some samples, which demonstrate the usages.
+* tools/ has scripts for Ant.
+
+
+Why JSch?
+==========
+Our intension in developing this stuff is to enable users of our pure
+java X servers, WiredX(http://wiredx.net/) and WeirdX, to enjoy secure X
+sessions.  Our efforts have mostly targeted the SSH2 protocol in relation
+to X Window System and X11 forwarding.  Of course, we are also interested in 
+adding other functionality - port forward, file transfer, terminal emulation, etc.
+
+
+Features
+========
+* JSch is in pure Java, but it depends on JavaTM Cryptography
+  Extension (JCE).  JSch is know to work with:
+  o J2SE 1.4.0 or later (no additional libraries required).
+  o J2SE 1.3 and Sun's JCE reference implementation that can be
+    obtained at http://java.sun.com/products/jce/
+  o J2SE 1.2.2 and later and Bouncycastle's JCE implementation that
+    can be obtained at http://www.bouncycastle.org/
+* SSH2 protocol support.
+* Key exchange: diffie-hellman-group-exchange-sha1, diffie-hellman-group1-sha1
+* Cipher: blowfish-cbc,3des-cbc,aes128-cbc,aes192-cbc,aes256-cbc
+          3des-ctr,aes128-ctr,aes192-ctr,aes256-ctc,
+          arcfour,arcfour128,arcfour256
+* MAC: hmac-md5,hmac-md5-96,hmac-sha1,hmac-sha1-96
+* Host key type: ssh-dss, ssh-rsa
+* Userauth: password
+* Userauth: publickey(DSA,RSA)
+* Userauth: keyboard-interactive
+* Userauth: gssapi-with-mic
+* X11 forwarding.
+* xauth spoofing.
+* connection through HTTP proxy.
+* connection through SOCKS5, SOCKS4 proxy.
+* port forwarding.
+* stream forwarding.
+* signal sending.
+  The unofficial patch for sshd of openssh will be find in the thread
+  http://marc.theaimsgroup.com/?l=openssh-unix-dev&m=104295745607575&w=2
+* envrironment variable passing.
+* remote exec.
+* generating DSA and RSA key pairs.
+* SSH File Transfer Protocol(version 0, 1, 2, 3)
+* partial authentication
+* packet compression: zlib, zlib@openssh.com
+  JZlib(http://www.jcraft.com/jzlib/) has been used.
+* hashed known_hosts file.
+* NONE Cipher switching.
+  http://www.psc.edu/networking/projects/hpn-ssh/none.php
+* JSch is licensed under BSD style license(refer to LICENSE.txt).
+
+
+How To Try
+==========
+This archive does not include java byte code, so please compile
+the source code by your self.
+  $ cd jsch-?.?.?/src
+  $ javac com/jcraft/jsch/*java com/jcraft/jsch/jce/*java com/jcraft/jzlib/*.java
+'/examples/' directory has included some samples to demonstrate what 
+JSch can do.  Please refer to '/examples/README' file.
+
+
+AES cipher
+==========
+JSch supports aes128-cbc,aes192-cbc,aes256-cbc,aes128-ctr,aes192-ctr,
+aes256-ctr but you require AES support in your J2SE to choose some of them.  
+If you are using Sun's J2SE, J2SE 1.4.2 or later is required.  
+And then, J2SE 1.4.2(or later) does not support aes256 by the default, 
+because of 'import control restrictions of some countries'.
+We have confirmed that by applying
+  "Java Cryptography Extension (JCE)
+  Unlimited Strength Jurisdiction Policy Files 1.4.2"
+on
+  http://java.sun.com/j2se/1.4.2/download.html#docs
+we can enjoy 'aes256-cbc,aes256-ctr'.
+
+
+Stream Forwarding
+=================
+JSch has a unique functionality, Stream Forwarding.
+Stream Forwarding allows you to plug Java I/O streams directly into a remote TCP
+port without assigning and opening a local TCP port.
+In port forwarding, as with the -L option of ssh command, you have to assign
+and open a local TCP port and that port is also accessible by crackers
+on localhost.  In some case, that local TCP port may be plugged to a
+secret port via SSH session.
+A sample program, /example/StreamForwarding.java , demonstrates
+this functionality.
+
+
+Generating Authentication Keys
+==============================
+JSch allows you to generate DSA and RSA key pairs, which are in OpenSSH format.
+Please refer to 'examples/KeyGen.java'.
+
+
+Packet Compression
+==================
+According to the draft from IETF sesch working group, the packet
+compression can be applied to each data stream directions; from sshd
+server to ssh client and from ssh client to sshd server.  So, jsch
+allows you to choose which data stream direction will be compressed or not.
+For example, in X11 forwarding session, the packet compression for data
+stream from sshd to ssh client will save the network traffic, but
+usually the traffic from ssh client to sshd is light, so by omitting
+the compression for this direction, you may be able to save some CPU time.
+Please refer to a sample program 'examples/Compression.java'.
+
+
+Property
+========
+By setting properties, you can control the behavior of jsch.
+Here is an example of enabling the packet compression,
+
+      Session session=jsch.getSession(user, host, 22);
+      java.util.Properties config=new java.util.Properties();
+      config.put("compression.s2c", "zlib,none");
+      config.put("compression.c2s", "zlib,none");
+      session.setConfig(config);
+      session.connect();
+
+Current release has supported following properties,
+* compression.s2c: zlib, none
+  default: none
+  Specifies whether to use compression for the data stream
+  from sshd to jsch.  If "zlib,none" is given and the remote sshd does
+  not allow the packet compression, compression will not be done.
+* compression.c2s: zlib, none
+  default: none
+  Specifies whether to use compression for the data stream
+  from jsch to sshd.
+* StrictHostKeyChecking: ask | yes | no
+  default: ask
+  If this property is set to ``yes'', jsch will never automatically add
+  host keys to the $HOME/.ssh/known_hosts file, and refuses to connect
+  to hosts whose host key has changed.  This property forces the user
+  to manually add all new hosts.  If this property is set to ``no'', 
+  jsch will automatically add new host keys to the user known hosts
+  files.  If this property is set to ``ask'', new  host keys will be
+  added to the user known host files only after the user has confirmed 
+  that is what they really want to do, and jsch will refuse to connect 
+  to hosts whose host key has changed.
+
+
+TODO
+====
+* re-implementation with java.nio.
+* replacing cipher, hash by JCE with pure Java code.
+* SSH File Transfer Protocol version 4.
+* error handling.
+
+
+Copyrights & Disclaimers
+========================
+JSch is copyrighted by ymnk, JCraft,Inc. and is licensed through BSD style license.
+Read the LICENSE.txt file for the complete license.
+
+
+Credits and Acknowledgments
+============================
+JSch has been developed by ymnk@jcraft.com and it can not be hacked
+without several help.
+* First of all, we want to thank JCE team at Sun Microsystems.
+  For long time, we had planed to implement SSH2 in pure Java,
+  but we had hesitated to do because tons of work must be done for
+  implementing ciphers, hashes, etc., from the scratch.
+  Thanks to newly added functionalities to J2SE 1.4.0, we could
+  start this project.
+* We appreciate the OpenSSH project.
+  The options '-ddd' of sshd, '---vvv' of ssh and the compile options 
+  '-DPACKET_DEBUG', '-DDEBUG_KEXDH' and  '-DDEBUG_KEX' were very
+  useful in debugging JSch.
+* We appreciate IETF sesch working group and SSH Communications Security Corp.
+  Without the standardization of the protocol, we could not get the
+  chance to implement JSch.
+* We appreciate Seigo Haruyama(http://www.unixuser.org/~haruyama/),
+  who are interpreting drafts of SSH2 protocol in Japanese.
+  His works were very useful for us to understand the technical terms
+  in our native language.
+* We also appreciate SourceForge.net's awesome service to the 
+  Open Source Community.
+
+
+If you have any comments, suggestions and questions, write us 
+at jsch@jcraft.com
+
+
+``SSH is a registered trademark and Secure Shell is a trademark of
+SSH Communications Security Corp (www.ssh.com)''.
diff --git a/java/com/jcraft/jsch/Random.java b/java/com/jcraft/jsch/Random.java
new file mode 100644
index 0000000..879b777
--- /dev/null
+++ b/java/com/jcraft/jsch/Random.java
@@ -0,0 +1,34 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface Random{
+  void fill(byte[] foo, int start, int len);
+}
diff --git a/java/com/jcraft/jsch/Request.java b/java/com/jcraft/jsch/Request.java
new file mode 100644
index 0000000..94f9b01
--- /dev/null
+++ b/java/com/jcraft/jsch/Request.java
@@ -0,0 +1,69 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+abstract class Request{
+  private boolean reply=false;
+  private Session session=null;
+  private Channel channel=null;
+  void request(Session session, Channel channel) throws Exception{
+    this.session=session;
+    this.channel=channel;
+    if(channel.connectTimeout>0){
+      setReply(true);
+    }
+  }
+  boolean waitForReply(){ return reply; }
+  void setReply(boolean reply){ this.reply=reply; }
+  void write(Packet packet) throws Exception{
+    if(reply){
+      channel.reply=-1;
+    }
+    session.write(packet);
+    if(reply){
+      long start=System.currentTimeMillis();
+      long timeout=channel.connectTimeout;
+      while(channel.isConnected() && channel.reply==-1){
+	try{Thread.sleep(10);}
+	catch(Exception ee){
+	}
+        if(timeout>0L &&
+           (System.currentTimeMillis()-start)>timeout){
+          channel.reply=0;
+          throw new JSchException("channel request: timeout");
+        }
+      }
+
+      if(channel.reply==0){
+	throw new JSchException("failed to send channel request");
+      }
+    }
+  }
+}
diff --git a/java/com/jcraft/jsch/RequestAgentForwarding.java b/java/com/jcraft/jsch/RequestAgentForwarding.java
new file mode 100644
index 0000000..66d328c
--- /dev/null
+++ b/java/com/jcraft/jsch/RequestAgentForwarding.java
@@ -0,0 +1,53 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class RequestAgentForwarding extends Request{
+  public void request(Session session, Channel channel) throws Exception{
+    super.request(session, channel);
+
+    setReply(false);
+
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+
+    // byte      SSH_MSG_CHANNEL_REQUEST(98)
+    // uint32 recipient channel
+    // string request type        // "auth-agent-req@openssh.com"
+    // boolean want reply         // 0
+    packet.reset();
+    buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST);
+    buf.putInt(channel.getRecipient());
+    buf.putString(Util.str2byte("auth-agent-req@openssh.com"));
+    buf.putByte((byte)(waitForReply() ? 1 : 0));
+    write(packet);
+    session.agent_forwarding=true;
+  }
+}
diff --git a/java/com/jcraft/jsch/RequestEnv.java b/java/com/jcraft/jsch/RequestEnv.java
new file mode 100644
index 0000000..cccda4d
--- /dev/null
+++ b/java/com/jcraft/jsch/RequestEnv.java
@@ -0,0 +1,54 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class RequestEnv extends Request{
+  byte[] name=new byte[0];
+  byte[] value=new byte[0];
+  void setEnv(byte[] name, byte[] value){
+    this.name=name;
+    this.value=value;
+  }
+  public void request(Session session, Channel channel) throws Exception{
+    super.request(session, channel);
+
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+
+    packet.reset();
+    buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST);
+    buf.putInt(channel.getRecipient());
+    buf.putString(Util.str2byte("env"));
+    buf.putByte((byte)(waitForReply() ? 1 : 0));
+    buf.putString(name);
+    buf.putString(value);
+    write(packet);
+  }
+}
diff --git a/java/com/jcraft/jsch/RequestExec.java b/java/com/jcraft/jsch/RequestExec.java
new file mode 100644
index 0000000..318d99a
--- /dev/null
+++ b/java/com/jcraft/jsch/RequestExec.java
@@ -0,0 +1,58 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class RequestExec extends Request{
+  private byte[] command=new byte[0];
+  RequestExec(byte[] command){
+    this.command=command;
+  }
+  public void request(Session session, Channel channel) throws Exception{
+    super.request(session, channel);
+
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+
+    // send
+    // byte     SSH_MSG_CHANNEL_REQUEST(98)
+    // uint32 recipient channel
+    // string request type       // "exec"
+    // boolean want reply        // 0
+    // string command
+    packet.reset();
+    buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST);
+    buf.putInt(channel.getRecipient());
+    buf.putString(Util.str2byte("exec"));
+    buf.putByte((byte)(waitForReply() ? 1 : 0));
+    buf.checkFreeSize(4+command.length);
+    buf.putString(command);
+    write(packet);
+  }
+}
diff --git a/java/com/jcraft/jsch/RequestPtyReq.java b/java/com/jcraft/jsch/RequestPtyReq.java
new file mode 100644
index 0000000..86bf4f1
--- /dev/null
+++ b/java/com/jcraft/jsch/RequestPtyReq.java
@@ -0,0 +1,78 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class RequestPtyReq extends Request{
+  private String ttype="vt100";
+  private int tcol=80;
+  private int trow=24;
+  private int twp=640;
+  private int thp=480;
+
+  private byte[] terminal_mode=Util.empty;
+
+  void setCode(String cookie){
+  }
+
+  void setTType(String ttype){
+    this.ttype=ttype;
+  }
+  
+  void setTerminalMode(byte[] terminal_mode){
+    this.terminal_mode=terminal_mode;
+  }
+
+  void setTSize(int tcol, int trow, int twp, int thp){
+    this.tcol=tcol;
+    this.trow=trow;
+    this.twp=twp;
+    this.thp=thp;
+  }
+
+  public void request(Session session, Channel channel) throws Exception{
+    super.request(session, channel);
+
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+
+    packet.reset();
+    buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST);
+    buf.putInt(channel.getRecipient());
+    buf.putString(Util.str2byte("pty-req"));
+    buf.putByte((byte)(waitForReply() ? 1 : 0));
+    buf.putString(Util.str2byte(ttype));
+    buf.putInt(tcol);
+    buf.putInt(trow);
+    buf.putInt(twp);
+    buf.putInt(thp);
+    buf.putString(terminal_mode);
+    write(packet);
+  }
+}
diff --git a/java/com/jcraft/jsch/RequestSftp.java b/java/com/jcraft/jsch/RequestSftp.java
new file mode 100644
index 0000000..483c296
--- /dev/null
+++ b/java/com/jcraft/jsch/RequestSftp.java
@@ -0,0 +1,49 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class RequestSftp extends Request{
+  RequestSftp(){
+    setReply(true);
+  }
+  public void request(Session session, Channel channel) throws Exception{
+    super.request(session, channel);
+
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+    packet.reset();
+    buf.putByte((byte)Session.SSH_MSG_CHANNEL_REQUEST);
+    buf.putInt(channel.getRecipient());
+    buf.putString(Util.str2byte("subsystem"));
+    buf.putByte((byte)(waitForReply() ? 1 : 0));
+    buf.putString(Util.str2byte("sftp"));
+    write(packet);
+  }
+}
diff --git a/java/com/jcraft/jsch/RequestShell.java b/java/com/jcraft/jsch/RequestShell.java
new file mode 100644
index 0000000..e037ba9
--- /dev/null
+++ b/java/com/jcraft/jsch/RequestShell.java
@@ -0,0 +1,51 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class RequestShell extends Request{
+  public void request(Session session, Channel channel) throws Exception{
+    super.request(session, channel);
+
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+
+    // send
+    // byte     SSH_MSG_CHANNEL_REQUEST(98)
+    // uint32 recipient channel
+    // string request type       // "shell"
+    // boolean want reply        // 0
+    packet.reset();
+    buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST);
+    buf.putInt(channel.getRecipient());
+    buf.putString(Util.str2byte("shell"));
+    buf.putByte((byte)(waitForReply() ? 1 : 0));
+    write(packet);
+  }
+}
diff --git a/java/com/jcraft/jsch/RequestSignal.java b/java/com/jcraft/jsch/RequestSignal.java
new file mode 100644
index 0000000..a692617
--- /dev/null
+++ b/java/com/jcraft/jsch/RequestSignal.java
@@ -0,0 +1,49 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class RequestSignal extends Request{
+  private String signal="KILL";
+  public void setSignal(String foo){ signal=foo; }
+  public void request(Session session, Channel channel) throws Exception{
+    super.request(session, channel);
+
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+
+    packet.reset();
+    buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST);
+    buf.putInt(channel.getRecipient());
+    buf.putString(Util.str2byte("signal"));
+    buf.putByte((byte)(waitForReply() ? 1 : 0));
+    buf.putString(Util.str2byte(signal));
+    write(packet);
+  }
+}
diff --git a/java/com/jcraft/jsch/RequestSubsystem.java b/java/com/jcraft/jsch/RequestSubsystem.java
new file mode 100644
index 0000000..b6fee4f
--- /dev/null
+++ b/java/com/jcraft/jsch/RequestSubsystem.java
@@ -0,0 +1,53 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class RequestSubsystem extends Request{
+  private String subsystem=null;
+  public void request(Session session, Channel channel, String subsystem, boolean want_reply) throws Exception{
+    setReply(want_reply);
+    this.subsystem=subsystem;
+    this.request(session, channel);
+  }
+  public void request(Session session, Channel channel) throws Exception{
+    super.request(session, channel);
+
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+
+    packet.reset();
+    buf.putByte((byte)Session.SSH_MSG_CHANNEL_REQUEST);
+    buf.putInt(channel.getRecipient());
+    buf.putString(Util.str2byte("subsystem"));
+    buf.putByte((byte)(waitForReply() ? 1 : 0));
+    buf.putString(Util.str2byte(subsystem));
+    write(packet);
+  }
+}
diff --git a/java/com/jcraft/jsch/RequestWindowChange.java b/java/com/jcraft/jsch/RequestWindowChange.java
new file mode 100644
index 0000000..43600ac
--- /dev/null
+++ b/java/com/jcraft/jsch/RequestWindowChange.java
@@ -0,0 +1,68 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class RequestWindowChange extends Request{
+  int width_columns=80;
+  int height_rows=24;
+  int width_pixels=640;
+  int height_pixels=480;
+  void setSize(int col, int row, int wp, int hp){
+    this.width_columns=col; 
+    this.height_rows=row; 
+    this.width_pixels=wp;
+    this.height_pixels=hp;
+  }
+  public void request(Session session, Channel channel) throws Exception{
+    super.request(session, channel);
+
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+
+    //byte      SSH_MSG_CHANNEL_REQUEST
+    //uint32    recipient_channel
+    //string    "window-change"
+    //boolean   FALSE
+    //uint32    terminal width, columns
+    //uint32    terminal height, rows
+    //uint32    terminal width, pixels
+    //uint32    terminal height, pixels
+    packet.reset();
+    buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST);
+    buf.putInt(channel.getRecipient());
+    buf.putString(Util.str2byte("window-change"));
+    buf.putByte((byte)(waitForReply() ? 1 : 0));
+    buf.putInt(width_columns);
+    buf.putInt(height_rows);
+    buf.putInt(width_pixels);
+    buf.putInt(height_pixels);
+    write(packet);
+  }
+}
diff --git a/java/com/jcraft/jsch/RequestX11.java b/java/com/jcraft/jsch/RequestX11.java
new file mode 100644
index 0000000..3bdaca9
--- /dev/null
+++ b/java/com/jcraft/jsch/RequestX11.java
@@ -0,0 +1,63 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class RequestX11 extends Request{
+  public void setCookie(String cookie){
+    ChannelX11.cookie=Util.str2byte(cookie);
+  }
+  public void request(Session session, Channel channel) throws Exception{
+    super.request(session, channel);
+
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+
+    // byte      SSH_MSG_CHANNEL_REQUEST(98)
+    // uint32 recipient channel
+    // string request type        // "x11-req"
+    // boolean want reply         // 0
+    // boolean   single connection
+    // string    x11 authentication protocol // "MIT-MAGIC-COOKIE-1".
+    // string    x11 authentication cookie
+    // uint32    x11 screen number
+    packet.reset();
+    buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST);
+    buf.putInt(channel.getRecipient());
+    buf.putString(Util.str2byte("x11-req"));
+    buf.putByte((byte)(waitForReply() ? 1 : 0));
+    buf.putByte((byte)0);
+    buf.putString(Util.str2byte("MIT-MAGIC-COOKIE-1"));
+    buf.putString(ChannelX11.getFakedCookie(session));
+    buf.putInt(0);
+    write(packet);
+
+    session.x11_forwarding=true;
+  }
+}
diff --git a/java/com/jcraft/jsch/ServerSocketFactory.java b/java/com/jcraft/jsch/ServerSocketFactory.java
new file mode 100644
index 0000000..682b4c4
--- /dev/null
+++ b/java/com/jcraft/jsch/ServerSocketFactory.java
@@ -0,0 +1,37 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.net.*;
+import java.io.*;
+
+public interface ServerSocketFactory{
+  public ServerSocket createServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException;
+}
diff --git a/java/com/jcraft/jsch/Session.java b/java/com/jcraft/jsch/Session.java
new file mode 100644
index 0000000..962f52f
--- /dev/null
+++ b/java/com/jcraft/jsch/Session.java
@@ -0,0 +1,2054 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.*;
+import java.net.*;
+
+public class Session implements Runnable{
+
+  // http://ietf.org/internet-drafts/draft-ietf-secsh-assignednumbers-01.txt
+  static final int SSH_MSG_DISCONNECT=                      1;
+  static final int SSH_MSG_IGNORE=                          2;
+  static final int SSH_MSG_UNIMPLEMENTED=                   3;
+  static final int SSH_MSG_DEBUG=                           4;
+  static final int SSH_MSG_SERVICE_REQUEST=                 5;
+  static final int SSH_MSG_SERVICE_ACCEPT=                  6;
+  static final int SSH_MSG_KEXINIT=                        20;
+  static final int SSH_MSG_NEWKEYS=                        21;
+  static final int SSH_MSG_KEXDH_INIT=                     30;
+  static final int SSH_MSG_KEXDH_REPLY=                    31;
+  static final int SSH_MSG_KEX_DH_GEX_GROUP=               31;
+  static final int SSH_MSG_KEX_DH_GEX_INIT=                32;
+  static final int SSH_MSG_KEX_DH_GEX_REPLY=               33;
+  static final int SSH_MSG_KEX_DH_GEX_REQUEST=             34;
+  static final int SSH_MSG_GLOBAL_REQUEST=                 80;
+  static final int SSH_MSG_REQUEST_SUCCESS=                81;
+  static final int SSH_MSG_REQUEST_FAILURE=                82;
+  static final int SSH_MSG_CHANNEL_OPEN=                   90;
+  static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION=      91;
+  static final int SSH_MSG_CHANNEL_OPEN_FAILURE=           92;
+  static final int SSH_MSG_CHANNEL_WINDOW_ADJUST=          93;
+  static final int SSH_MSG_CHANNEL_DATA=                   94;
+  static final int SSH_MSG_CHANNEL_EXTENDED_DATA=          95;
+  static final int SSH_MSG_CHANNEL_EOF=                    96;
+  static final int SSH_MSG_CHANNEL_CLOSE=                  97;
+  static final int SSH_MSG_CHANNEL_REQUEST=                98;
+  static final int SSH_MSG_CHANNEL_SUCCESS=                99;
+  static final int SSH_MSG_CHANNEL_FAILURE=               100;
+
+  private static final int PACKET_MAX_SIZE = 256 * 1024;
+
+  private byte[] V_S;                                 // server version
+  private byte[] V_C=Util.str2byte("SSH-2.0-JSCH-"+JSch.VERSION); // client version
+
+  private byte[] I_C; // the payload of the client's SSH_MSG_KEXINIT
+  private byte[] I_S; // the payload of the server's SSH_MSG_KEXINIT
+  private byte[] K_S; // the host key
+
+  private byte[] session_id;
+
+  private byte[] IVc2s;
+  private byte[] IVs2c;
+  private byte[] Ec2s;
+  private byte[] Es2c;
+  private byte[] MACc2s;
+  private byte[] MACs2c;
+
+  private int seqi=0;
+  private int seqo=0;
+
+  String[] guess=null;
+  private Cipher s2ccipher;
+  private Cipher c2scipher;
+  private MAC s2cmac;
+  private MAC c2smac;
+  //private byte[] mac_buf;
+  private byte[] s2cmac_result1;
+  private byte[] s2cmac_result2;
+
+  private Compression deflater;
+  private Compression inflater;
+
+  private IO io;
+  private Socket socket;
+  private int timeout=0;
+
+  private volatile boolean isConnected=false;
+
+  private boolean isAuthed=false;
+
+  private Thread connectThread=null;
+  private Object lock=new Object();
+
+  boolean x11_forwarding=false;
+  boolean agent_forwarding=false;
+
+  InputStream in=null;
+  OutputStream out=null;
+
+  static Random random;
+
+  Buffer buf;
+  Packet packet;
+
+  SocketFactory socket_factory=null;
+
+  static final int buffer_margin = 32 + // maximum padding length
+                                   20 + // maximum mac length
+                                   32;  // margin for deflater; deflater may inflate data
+
+  private java.util.Hashtable config=null;
+
+  private Proxy proxy=null;
+  private UserInfo userinfo;
+
+  private String hostKeyAlias=null;
+  private int serverAliveInterval=0;
+  private int serverAliveCountMax=1;
+
+  protected boolean daemon_thread=false;
+
+  private long kex_start_time=0L;
+
+  int max_auth_tries = 6;
+  int auth_failures = 0;
+
+  String host="127.0.0.1";
+  int port=22;
+
+  String username=null;
+  byte[] password=null;
+
+  JSch jsch;
+
+  Session(JSch jsch) throws JSchException{
+    super();
+    this.jsch=jsch;
+    buf=new Buffer();
+    packet=new Packet(buf);
+  }
+
+  public void connect() throws JSchException{
+    connect(timeout);
+  }
+
+  public void connect(int connectTimeout) throws JSchException{
+    if(isConnected){
+      throw new JSchException("session is already connected");
+    }
+
+    io=new IO();
+    if(random==null){
+      try{
+	Class c=Class.forName(getConfig("random"));
+        random=(Random)(c.newInstance());
+      }
+      catch(Exception e){ 
+        throw new JSchException(e.toString(), e);
+      }
+    }
+    Packet.setRandom(random);
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "Connecting to "+host+" port "+port);
+    }
+
+    try	{
+      int i, j;
+
+      if(proxy==null){
+        InputStream in;
+        OutputStream out;
+	if(socket_factory==null){
+          socket=Util.createSocket(host, port, connectTimeout);
+	  in=socket.getInputStream();
+	  out=socket.getOutputStream();
+	}
+	else{
+          socket=socket_factory.createSocket(host, port);
+	  in=socket_factory.getInputStream(socket);
+	  out=socket_factory.getOutputStream(socket);
+	}
+	//if(timeout>0){ socket.setSoTimeout(timeout); }
+        socket.setTcpNoDelay(true);
+        io.setInputStream(in);
+        io.setOutputStream(out);
+      }
+      else{
+	synchronized(proxy){
+          proxy.connect(socket_factory, host, port, connectTimeout);
+	  io.setInputStream(proxy.getInputStream());
+	  io.setOutputStream(proxy.getOutputStream());
+          socket=proxy.getSocket();
+	}
+      }
+
+      if(connectTimeout>0 && socket!=null){
+        socket.setSoTimeout(connectTimeout);
+      }
+
+      isConnected=true;
+
+      if(JSch.getLogger().isEnabled(Logger.INFO)){
+        JSch.getLogger().log(Logger.INFO, 
+                             "Connection established");
+      }
+
+      jsch.addSession(this);
+
+      {
+	// Some Cisco devices will miss to read '\n' if it is sent separately.
+	byte[] foo=new byte[V_C.length+1];
+	System.arraycopy(V_C, 0, foo, 0, V_C.length);
+	foo[foo.length-1]=(byte)'\n';
+	io.put(foo, 0, foo.length);
+      }
+
+      while(true){
+        i=0;
+        j=0;
+        while(i<buf.buffer.length){
+          j=io.getByte();
+          if(j<0)break;
+          buf.buffer[i]=(byte)j; i++; 
+          if(j==10)break;
+        }
+        if(j<0){
+          throw new JSchException("connection is closed by foreign host");
+        }
+
+        if(buf.buffer[i-1]==10){    // 0x0a
+          i--;
+          if(i>0 && buf.buffer[i-1]==13){  // 0x0d
+            i--;
+          }
+        }
+
+        if(i<=3 || 
+           ((i!=buf.buffer.length) &&
+            (buf.buffer[0]!='S'||buf.buffer[1]!='S'||
+             buf.buffer[2]!='H'||buf.buffer[3]!='-'))){
+          // It must not start with 'SSH-'
+          //System.err.println(new String(buf.buffer, 0, i);
+          continue;
+        }
+
+        if(i==buf.buffer.length ||
+           i<7 ||                                      // SSH-1.99 or SSH-2.0
+           (buf.buffer[4]=='1' && buf.buffer[6]!='9')  // SSH-1.5
+           ){
+          throw new JSchException("invalid server's version string");
+        }
+        break;
+      }
+
+      V_S=new byte[i]; System.arraycopy(buf.buffer, 0, V_S, 0, i);
+      //System.err.println("V_S: ("+i+") ["+new String(V_S)+"]");
+
+      if(JSch.getLogger().isEnabled(Logger.INFO)){
+        JSch.getLogger().log(Logger.INFO, 
+                             "Remote version string: "+Util.byte2str(V_S));
+        JSch.getLogger().log(Logger.INFO, 
+                             "Local version string: "+Util.byte2str(V_C));
+      }
+
+      send_kexinit();
+
+      buf=read(buf);
+      if(buf.getCommand()!=SSH_MSG_KEXINIT){
+        in_kex=false;
+	throw new JSchException("invalid protocol: "+buf.getCommand());
+      }
+
+      if(JSch.getLogger().isEnabled(Logger.INFO)){
+        JSch.getLogger().log(Logger.INFO, 
+                             "SSH_MSG_KEXINIT received");
+      }
+
+      KeyExchange kex=receive_kexinit(buf);
+
+      while(true){
+	buf=read(buf);
+	if(kex.getState()==buf.getCommand()){
+          kex_start_time=System.currentTimeMillis();
+          boolean result=kex.next(buf);
+	  if(!result){
+	    //System.err.println("verify: "+result);
+            in_kex=false;
+	    throw new JSchException("verify: "+result);
+	  }
+	}
+	else{
+          in_kex=false;
+	  throw new JSchException("invalid protocol(kex): "+buf.getCommand());
+	}
+	if(kex.getState()==KeyExchange.STATE_END){
+	  break;
+	}
+      }
+
+      try{ checkHost(host, port, kex); }
+      catch(JSchException ee){
+        in_kex=false;
+        throw ee;
+      }
+
+      send_newkeys();
+
+      // receive SSH_MSG_NEWKEYS(21)
+      buf=read(buf);
+      //System.err.println("read: 21 ? "+buf.getCommand());
+      if(buf.getCommand()==SSH_MSG_NEWKEYS){
+
+        if(JSch.getLogger().isEnabled(Logger.INFO)){
+          JSch.getLogger().log(Logger.INFO, 
+                               "SSH_MSG_NEWKEYS received");
+        }
+
+	receive_newkeys(buf, kex);
+      }
+      else{
+        in_kex=false;
+	throw new JSchException("invalid protocol(newkyes): "+buf.getCommand());
+      }
+
+      try{
+        String s = getConfig("MaxAuthTries");
+        if(s!=null){
+          max_auth_tries = Integer.parseInt(s);
+        }
+      }
+      catch(NumberFormatException e){
+        throw new JSchException("MaxAuthTries: "+getConfig("MaxAuthTries"), e);
+      }
+
+      boolean auth=false;
+      boolean auth_cancel=false;
+
+      UserAuth ua=null;
+      try{
+	Class c=Class.forName(getConfig("userauth.none"));
+        ua=(UserAuth)(c.newInstance());
+      }
+      catch(Exception e){ 
+        throw new JSchException(e.toString(), e);
+      }
+
+      auth=ua.start(this);
+
+      String cmethods=getConfig("PreferredAuthentications");
+
+      String[] cmethoda=Util.split(cmethods, ",");
+
+      String smethods=null;
+      if(!auth){
+        smethods=((UserAuthNone)ua).getMethods();
+        if(smethods!=null){
+          smethods=smethods.toLowerCase();
+        }
+        else{
+          // methods: publickey,password,keyboard-interactive
+          //smethods="publickey,password,keyboard-interactive";
+          smethods=cmethods;
+        }
+      }
+
+      String[] smethoda=Util.split(smethods, ",");
+
+      int methodi=0;
+
+      loop:
+      while(true){
+
+	while(!auth && 
+	      cmethoda!=null && methodi<cmethoda.length){
+
+          String method=cmethoda[methodi++];
+          boolean acceptable=false;
+          for(int k=0; k<smethoda.length; k++){
+            if(smethoda[k].equals(method)){
+              acceptable=true;
+              break;
+            }
+          }
+          if(!acceptable){
+            continue;
+          }
+
+          //System.err.println("  method: "+method);
+
+          if(JSch.getLogger().isEnabled(Logger.INFO)){
+            String str="Authentications that can continue: ";
+            for(int k=methodi-1; k<cmethoda.length; k++){
+              str+=cmethoda[k];
+              if(k+1<cmethoda.length)
+                str+=",";
+            }
+            JSch.getLogger().log(Logger.INFO, 
+                                 str);
+            JSch.getLogger().log(Logger.INFO, 
+                                 "Next authentication method: "+method);
+          }
+
+	  ua=null;
+          try{
+            Class c=null;
+            if(getConfig("userauth."+method)!=null){
+              c=Class.forName(getConfig("userauth."+method));
+              ua=(UserAuth)(c.newInstance());
+            }
+          }
+          catch(Exception e){
+            if(JSch.getLogger().isEnabled(Logger.WARN)){
+              JSch.getLogger().log(Logger.WARN, 
+                                   "failed to load "+method+" method");
+            }
+          }
+
+	  if(ua!=null){
+            auth_cancel=false;
+	    try{ 
+	      auth=ua.start(this); 
+              if(auth && 
+                 JSch.getLogger().isEnabled(Logger.INFO)){
+                JSch.getLogger().log(Logger.INFO, 
+                                     "Authentication succeeded ("+method+").");
+              }
+	    }
+	    catch(JSchAuthCancelException ee){
+	      auth_cancel=true;
+	    }
+	    catch(JSchPartialAuthException ee){
+              String tmp = smethods;
+              smethods=ee.getMethods();
+              smethoda=Util.split(smethods, ",");
+              if(!tmp.equals(smethods)){
+                methodi=0;
+              }
+	      //System.err.println("PartialAuth: "+methods);
+	      auth_cancel=false;
+	      continue loop;
+	    }
+	    catch(RuntimeException ee){
+	      throw ee;
+	    }
+	    catch(Exception ee){
+	      //System.err.println("ee: "+ee); // SSH_MSG_DISCONNECT: 2 Too many authentication failures
+              break loop;
+	    }
+	  }
+	}
+        break;
+      }
+
+      if(!auth){
+        if(auth_failures >= max_auth_tries){
+          if(JSch.getLogger().isEnabled(Logger.INFO)){
+            JSch.getLogger().log(Logger.INFO, 
+                                 "Login trials exceeds "+max_auth_tries);
+          }
+        }
+        if(auth_cancel)
+          throw new JSchException("Auth cancel");
+        throw new JSchException("Auth fail");
+      }
+
+      if(connectTimeout>0 || timeout>0){
+        socket.setSoTimeout(timeout);
+      }
+
+      isAuthed=true;
+
+      synchronized(lock){
+        if(isConnected){
+          connectThread=new Thread(this);
+          connectThread.setName("Connect thread "+host+" session");
+          if(daemon_thread){
+            connectThread.setDaemon(daemon_thread);
+          }
+          connectThread.start();
+        }
+        else{
+          // The session has been already down and
+          // we don't have to start new thread.
+        }
+      }
+    }
+    catch(Exception e) {
+      in_kex=false;
+      if(isConnected){
+	try{
+	  packet.reset();
+	  buf.putByte((byte)SSH_MSG_DISCONNECT);
+	  buf.putInt(3);
+	  buf.putString(Util.str2byte(e.toString()));
+	  buf.putString(Util.str2byte("en"));
+	  write(packet);
+	  disconnect();
+	}
+	catch(Exception ee){
+	}
+      }
+      isConnected=false;
+      //e.printStackTrace();
+      if(e instanceof RuntimeException) throw (RuntimeException)e;
+      if(e instanceof JSchException) throw (JSchException)e;
+      throw new JSchException("Session.connect: "+e);
+    }
+    finally{
+      Util.bzero(this.password);
+      this.password=null;
+    }
+  }
+
+  private KeyExchange receive_kexinit(Buffer buf) throws Exception {
+    int j=buf.getInt();
+    if(j!=buf.getLength()){    // packet was compressed and
+      buf.getByte();           // j is the size of deflated packet.
+      I_S=new byte[buf.index-5];
+    }
+    else{
+      I_S=new byte[j-1-buf.getByte()];
+    }
+   System.arraycopy(buf.buffer, buf.s, I_S, 0, I_S.length);
+
+   if(!in_kex){     // We are in rekeying activated by the remote!
+     send_kexinit();
+   }
+
+    guess=KeyExchange.guess(I_S, I_C);
+    if(guess==null){
+      throw new JSchException("Algorithm negotiation fail");
+    }
+
+    if(!isAuthed &&
+       (guess[KeyExchange.PROPOSAL_ENC_ALGS_CTOS].equals("none") ||
+        (guess[KeyExchange.PROPOSAL_ENC_ALGS_STOC].equals("none")))){
+      throw new JSchException("NONE Cipher should not be chosen before authentification is successed.");
+    }
+
+    KeyExchange kex=null;
+    try{
+      Class c=Class.forName(getConfig(guess[KeyExchange.PROPOSAL_KEX_ALGS]));
+      kex=(KeyExchange)(c.newInstance());
+    }
+    catch(Exception e){ 
+      throw new JSchException(e.toString(), e);
+    }
+
+    kex.init(this, V_S, V_C, I_S, I_C);
+    return kex;
+  }
+
+  private boolean in_kex=false;
+  public void rekey() throws Exception {
+    send_kexinit();
+  }
+  private void send_kexinit() throws Exception {
+    if(in_kex)
+      return;
+
+    String cipherc2s=getConfig("cipher.c2s");
+    String ciphers2c=getConfig("cipher.s2c");
+
+    String[] not_available_ciphers=checkCiphers(getConfig("CheckCiphers"));
+    if(not_available_ciphers!=null && not_available_ciphers.length>0){
+      cipherc2s=Util.diffString(cipherc2s, not_available_ciphers);
+      ciphers2c=Util.diffString(ciphers2c, not_available_ciphers);
+      if(cipherc2s==null || ciphers2c==null){
+        throw new JSchException("There are not any available ciphers.");
+      }
+    }
+
+    String kex=getConfig("kex");
+    String[] not_available_kexes=checkKexes(getConfig("CheckKexes"));
+    if(not_available_kexes!=null && not_available_kexes.length>0){
+      kex=Util.diffString(kex, not_available_kexes);
+      if(kex==null){
+        throw new JSchException("There are not any available kexes.");
+      }
+    }
+
+    in_kex=true;
+    kex_start_time=System.currentTimeMillis();
+
+    // byte      SSH_MSG_KEXINIT(20)
+    // byte[16]  cookie (random bytes)
+    // string    kex_algorithms
+    // string    server_host_key_algorithms
+    // string    encryption_algorithms_client_to_server
+    // string    encryption_algorithms_server_to_client
+    // string    mac_algorithms_client_to_server
+    // string    mac_algorithms_server_to_client
+    // string    compression_algorithms_client_to_server
+    // string    compression_algorithms_server_to_client
+    // string    languages_client_to_server
+    // string    languages_server_to_client
+    Buffer buf = new Buffer();                // send_kexinit may be invoked
+    Packet packet = new Packet(buf);          // by user thread.
+    packet.reset();
+    buf.putByte((byte) SSH_MSG_KEXINIT);
+    synchronized(random){
+      random.fill(buf.buffer, buf.index, 16); buf.skip(16);
+    }
+    buf.putString(Util.str2byte(kex));
+    buf.putString(Util.str2byte(getConfig("server_host_key")));
+    buf.putString(Util.str2byte(cipherc2s));
+    buf.putString(Util.str2byte(ciphers2c));
+    buf.putString(Util.str2byte(getConfig("mac.c2s")));
+    buf.putString(Util.str2byte(getConfig("mac.s2c")));
+    buf.putString(Util.str2byte(getConfig("compression.c2s")));
+    buf.putString(Util.str2byte(getConfig("compression.s2c")));
+    buf.putString(Util.str2byte(getConfig("lang.c2s")));
+    buf.putString(Util.str2byte(getConfig("lang.s2c")));
+    buf.putByte((byte)0);
+    buf.putInt(0);
+
+    buf.setOffSet(5);
+    I_C=new byte[buf.getLength()];
+    buf.getByte(I_C);
+
+    write(packet);
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "SSH_MSG_KEXINIT sent");
+    }
+  }
+
+  private void send_newkeys() throws Exception {
+    // send SSH_MSG_NEWKEYS(21)
+    packet.reset();
+    buf.putByte((byte)SSH_MSG_NEWKEYS);
+    write(packet);
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "SSH_MSG_NEWKEYS sent");
+    }
+  }
+
+  private void checkHost(String chost, int port, KeyExchange kex) throws JSchException {
+    String shkc=getConfig("StrictHostKeyChecking");
+
+    if(hostKeyAlias!=null){
+      chost=hostKeyAlias;
+    }
+
+    //System.err.println("shkc: "+shkc);
+
+    byte[] K_S=kex.getHostKey();
+    String key_type=kex.getKeyType();
+    String key_fprint=kex.getFingerPrint();
+
+    if(hostKeyAlias==null && port!=22){
+      chost=("["+chost+"]:"+port);
+    }
+
+//    hostkey=new HostKey(chost, K_S);
+
+    HostKeyRepository hkr=jsch.getHostKeyRepository();
+    int i=0;
+    synchronized(hkr){
+      i=hkr.check(chost, K_S);
+    }
+
+    boolean insert=false;
+
+    if((shkc.equals("ask") || shkc.equals("yes")) &&
+       i==HostKeyRepository.CHANGED){
+      String file=null;
+      synchronized(hkr){
+	file=hkr.getKnownHostsRepositoryID();
+      }
+      if(file==null){file="known_hosts";}
+
+      boolean b=false;
+
+      if(userinfo!=null){
+        String message=
+"WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!\n"+
+"IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n"+
+"Someone could be eavesdropping on you right now (man-in-the-middle attack)!\n"+
+"It is also possible that the "+key_type+" host key has just been changed.\n"+
+"The fingerprint for the "+key_type+" key sent by the remote host is\n"+
+key_fprint+".\n"+
+"Please contact your system administrator.\n"+
+"Add correct host key in "+file+" to get rid of this message.";
+
+        if(shkc.equals("ask")){
+          b=userinfo.promptYesNo(message+
+                                 "\nDo you want to delete the old key and insert the new key?");
+        }
+        else{  // shkc.equals("yes")
+          userinfo.showMessage(message);
+        }
+      }
+
+      if(!b){
+        throw new JSchException("HostKey has been changed: "+chost);
+      }
+
+      synchronized(hkr){
+        hkr.remove(chost, 
+                   (key_type.equals("DSA") ? "ssh-dss" : "ssh-rsa"), 
+                   null);
+        insert=true;
+      }
+    }
+
+    if((shkc.equals("ask") || shkc.equals("yes")) &&
+       (i!=HostKeyRepository.OK) && !insert){
+      if(shkc.equals("yes")){
+	throw new JSchException("reject HostKey: "+host);
+      }
+      //System.err.println("finger-print: "+key_fprint);
+      if(userinfo!=null){
+	boolean foo=userinfo.promptYesNo(
+"The authenticity of host '"+host+"' can't be established.\n"+
+key_type+" key fingerprint is "+key_fprint+".\n"+
+"Are you sure you want to continue connecting?"
+					 );
+	if(!foo){
+	  throw new JSchException("reject HostKey: "+host);
+	}
+	insert=true;
+      }
+      else{
+	if(i==HostKeyRepository.NOT_INCLUDED) 
+	  throw new JSchException("UnknownHostKey: "+host+". "+key_type+" key fingerprint is "+key_fprint);
+	else 
+          throw new JSchException("HostKey has been changed: "+host);
+      }
+    }
+
+    if(shkc.equals("no") && 
+       HostKeyRepository.NOT_INCLUDED==i){
+      insert=true;
+    }
+
+    if(i==HostKeyRepository.OK &&
+       JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "Host '"+host+"' is known and mathces the "+key_type+" host key");
+    }
+
+    if(insert &&
+       JSch.getLogger().isEnabled(Logger.WARN)){
+      JSch.getLogger().log(Logger.WARN, 
+                           "Permanently added '"+host+"' ("+key_type+") to the list of known hosts.");
+    }
+
+    String hkh=getConfig("HashKnownHosts");
+    if(hkh.equals("yes") && (hkr instanceof KnownHosts)){
+      hostkey=((KnownHosts)hkr).createHashedHostKey(chost, K_S);
+    }
+    else{
+      hostkey=new HostKey(chost, K_S);
+    }
+
+    if(insert){
+      synchronized(hkr){
+	hkr.add(hostkey, userinfo);
+      }
+
+    }
+
+  }
+
+//public void start(){ (new Thread(this)).start();  }
+
+  public Channel openChannel(String type) throws JSchException{
+    if(!isConnected){
+      throw new JSchException("session is down");
+    }
+    try{
+      Channel channel=Channel.getChannel(type);
+      addChannel(channel);
+      channel.init();
+      return channel;
+    }
+    catch(Exception e){
+      //e.printStackTrace();
+    }
+    return null;
+  }
+
+  // encode will bin invoked in write with synchronization.
+  public void encode(Packet packet) throws Exception{
+//System.err.println("encode: "+packet.buffer.getCommand());
+//System.err.println("        "+packet.buffer.index);
+//if(packet.buffer.getCommand()==96){
+//Thread.dumpStack();
+//}
+    if(deflater!=null){
+      compress_len[0]=packet.buffer.index;
+      packet.buffer.buffer=deflater.compress(packet.buffer.buffer, 
+                                             5, compress_len);
+      packet.buffer.index=compress_len[0];
+    }
+    if(c2scipher!=null){
+      //packet.padding(c2scipher.getIVSize());
+      packet.padding(c2scipher_size);
+      int pad=packet.buffer.buffer[4];
+      synchronized(random){
+	random.fill(packet.buffer.buffer, packet.buffer.index-pad, pad);
+      }
+    }
+    else{
+      packet.padding(8);
+    }
+
+    if(c2smac!=null){
+      c2smac.update(seqo);
+      c2smac.update(packet.buffer.buffer, 0, packet.buffer.index);
+      c2smac.doFinal(packet.buffer.buffer, packet.buffer.index);
+    }
+    if(c2scipher!=null){
+      byte[] buf=packet.buffer.buffer;
+      c2scipher.update(buf, 0, packet.buffer.index, buf, 0);
+    }
+    if(c2smac!=null){
+      packet.buffer.skip(c2smac.getBlockSize());
+    }
+  }
+
+  int[] uncompress_len=new int[1];
+  int[] compress_len=new int[1];
+
+  private int s2ccipher_size=8;
+  private int c2scipher_size=8;
+  public Buffer read(Buffer buf) throws Exception{
+    int j=0;
+    while(true){
+      buf.reset();
+      io.getByte(buf.buffer, buf.index, s2ccipher_size); 
+      buf.index+=s2ccipher_size;
+      if(s2ccipher!=null){
+        s2ccipher.update(buf.buffer, 0, s2ccipher_size, buf.buffer, 0);
+      }
+      j=((buf.buffer[0]<<24)&0xff000000)|
+        ((buf.buffer[1]<<16)&0x00ff0000)|
+        ((buf.buffer[2]<< 8)&0x0000ff00)|
+        ((buf.buffer[3]    )&0x000000ff);
+      // RFC 4253 6.1. Maximum Packet Length
+      if(j<5 || j>PACKET_MAX_SIZE){
+        start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE);
+      }
+      int need = j+4-s2ccipher_size;
+      //if(need<0){
+      //  throw new IOException("invalid data");
+      //}
+      if((buf.index+need)>buf.buffer.length){
+        byte[] foo=new byte[buf.index+need];
+        System.arraycopy(buf.buffer, 0, foo, 0, buf.index);
+        buf.buffer=foo;
+      }
+
+      if((need%s2ccipher_size)!=0){
+        String message="Bad packet length "+need;
+        if(JSch.getLogger().isEnabled(Logger.FATAL)){
+          JSch.getLogger().log(Logger.FATAL, message); 
+        }
+        start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE-s2ccipher_size);
+      }
+
+      if(need>0){
+	io.getByte(buf.buffer, buf.index, need); buf.index+=(need);
+	if(s2ccipher!=null){
+	  s2ccipher.update(buf.buffer, s2ccipher_size, need, buf.buffer, s2ccipher_size);
+	}
+      }
+
+      if(s2cmac!=null){
+	s2cmac.update(seqi);
+	s2cmac.update(buf.buffer, 0, buf.index);
+
+        s2cmac.doFinal(s2cmac_result1, 0);
+	io.getByte(s2cmac_result2, 0, s2cmac_result2.length);
+        if(!java.util.Arrays.equals(s2cmac_result1, s2cmac_result2)){
+          if(need > PACKET_MAX_SIZE){
+            throw new IOException("MAC Error");
+          }
+          start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE-need);
+          continue;
+	}
+      }
+
+      seqi++;
+
+      if(inflater!=null){
+        //inflater.uncompress(buf);
+	int pad=buf.buffer[4];
+	uncompress_len[0]=buf.index-5-pad;
+	byte[] foo=inflater.uncompress(buf.buffer, 5, uncompress_len);
+	if(foo!=null){
+	  buf.buffer=foo;
+	  buf.index=5+uncompress_len[0];
+	}
+	else{
+	  System.err.println("fail in inflater");
+	  break;
+	}
+      }
+
+      int type=buf.getCommand()&0xff;
+      //System.err.println("read: "+type);
+      if(type==SSH_MSG_DISCONNECT){
+        buf.rewind();
+        buf.getInt();buf.getShort();
+	int reason_code=buf.getInt();
+	byte[] description=buf.getString();
+	byte[] language_tag=buf.getString();
+	throw new JSchException("SSH_MSG_DISCONNECT: "+
+				    reason_code+
+				" "+Util.byte2str(description)+
+				" "+Util.byte2str(language_tag));
+	//break;
+      }
+      else if(type==SSH_MSG_IGNORE){
+      }
+      else if(type==SSH_MSG_UNIMPLEMENTED){
+        buf.rewind();
+        buf.getInt();buf.getShort();
+	int reason_id=buf.getInt();
+        if(JSch.getLogger().isEnabled(Logger.INFO)){
+          JSch.getLogger().log(Logger.INFO, 
+                               "Received SSH_MSG_UNIMPLEMENTED for "+reason_id);
+        }
+      }
+      else if(type==SSH_MSG_DEBUG){
+        buf.rewind();
+        buf.getInt();buf.getShort();
+/*
+	byte always_display=(byte)buf.getByte();
+	byte[] message=buf.getString();
+	byte[] language_tag=buf.getString();
+	System.err.println("SSH_MSG_DEBUG:"+
+			   " "+Util.byte2str(message)+
+			   " "+Util.byte2str(language_tag));
+*/
+      }
+      else if(type==SSH_MSG_CHANNEL_WINDOW_ADJUST){
+          buf.rewind();
+          buf.getInt();buf.getShort();
+	  Channel c=Channel.getChannel(buf.getInt(), this);
+	  if(c==null){
+	  }
+	  else{
+	    c.addRemoteWindowSize(buf.getInt()); 
+	  }
+      }
+      else if(type==UserAuth.SSH_MSG_USERAUTH_SUCCESS){
+        isAuthed=true;
+        if(inflater==null && deflater==null){
+          String method;
+          method=guess[KeyExchange.PROPOSAL_COMP_ALGS_CTOS];
+          initDeflater(method);
+          method=guess[KeyExchange.PROPOSAL_COMP_ALGS_STOC];
+          initInflater(method);
+        }
+        break;
+      }
+      else{
+        break;
+      }
+    }
+    buf.rewind();
+    return buf;
+  }
+
+  private void start_discard(Buffer buf, Cipher cipher, MAC mac, 
+                             int packet_length, int discard) throws JSchException, IOException{
+    MAC discard_mac = null;
+
+    if(!cipher.isCBC()){
+      throw new JSchException("Packet corrupt");
+    }
+
+    if(packet_length!=PACKET_MAX_SIZE && mac != null){
+      discard_mac = mac;
+    }
+
+    discard -= buf.index;
+
+    while(discard>0){
+      buf.reset();
+      int len = discard>buf.buffer.length ? buf.buffer.length : discard;
+      io.getByte(buf.buffer, 0, len);
+      if(discard_mac!=null){
+        discard_mac.update(buf.buffer, 0, len);
+      }
+      discard -= len;
+    }
+
+    if(discard_mac!=null){
+      discard_mac.doFinal(buf.buffer, 0);
+    }
+
+    throw new JSchException("Packet corrupt");
+  }
+
+  byte[] getSessionId(){
+    return session_id;
+  }
+
+  private void receive_newkeys(Buffer buf, KeyExchange kex) throws Exception {
+    updateKeys(kex);
+    in_kex=false;
+  }
+  private void updateKeys(KeyExchange kex) throws Exception{
+    byte[] K=kex.getK();
+    byte[] H=kex.getH();
+    HASH hash=kex.getHash();
+
+//    String[] guess=kex.guess;
+
+    if(session_id==null){
+      session_id=new byte[H.length];
+      System.arraycopy(H, 0, session_id, 0, H.length);
+    }
+
+    /*
+      Initial IV client to server:     HASH (K || H || "A" || session_id)
+      Initial IV server to client:     HASH (K || H || "B" || session_id)
+      Encryption key client to server: HASH (K || H || "C" || session_id)
+      Encryption key server to client: HASH (K || H || "D" || session_id)
+      Integrity key client to server:  HASH (K || H || "E" || session_id)
+      Integrity key server to client:  HASH (K || H || "F" || session_id)
+    */
+
+    buf.reset();
+    buf.putMPInt(K);
+    buf.putByte(H);
+    buf.putByte((byte)0x41);
+    buf.putByte(session_id);
+    hash.update(buf.buffer, 0, buf.index);
+    IVc2s=hash.digest();
+
+    int j=buf.index-session_id.length-1;
+
+    buf.buffer[j]++;
+    hash.update(buf.buffer, 0, buf.index);
+    IVs2c=hash.digest();
+
+    buf.buffer[j]++;
+    hash.update(buf.buffer, 0, buf.index);
+    Ec2s=hash.digest();
+
+    buf.buffer[j]++;
+    hash.update(buf.buffer, 0, buf.index);
+    Es2c=hash.digest();
+
+    buf.buffer[j]++;
+    hash.update(buf.buffer, 0, buf.index);
+    MACc2s=hash.digest();
+
+    buf.buffer[j]++;
+    hash.update(buf.buffer, 0, buf.index);
+    MACs2c=hash.digest();
+
+    try{
+      Class c;
+      String method;
+  
+      method=guess[KeyExchange.PROPOSAL_ENC_ALGS_STOC];
+      c=Class.forName(getConfig(method));
+      s2ccipher=(Cipher)(c.newInstance());
+      while(s2ccipher.getBlockSize()>Es2c.length){
+        buf.reset();
+        buf.putMPInt(K);
+        buf.putByte(H);
+        buf.putByte(Es2c);
+        hash.update(buf.buffer, 0, buf.index);
+        byte[] foo=hash.digest();
+        byte[] bar=new byte[Es2c.length+foo.length];
+	System.arraycopy(Es2c, 0, bar, 0, Es2c.length);
+	System.arraycopy(foo, 0, bar, Es2c.length, foo.length);
+	Es2c=bar;
+      }
+      s2ccipher.init(Cipher.DECRYPT_MODE, Es2c, IVs2c);
+      s2ccipher_size=s2ccipher.getIVSize();
+
+      method=guess[KeyExchange.PROPOSAL_MAC_ALGS_STOC];
+      c=Class.forName(getConfig(method));
+      s2cmac=(MAC)(c.newInstance());
+      s2cmac.init(MACs2c);
+      //mac_buf=new byte[s2cmac.getBlockSize()];
+      s2cmac_result1=new byte[s2cmac.getBlockSize()];
+      s2cmac_result2=new byte[s2cmac.getBlockSize()];
+
+      method=guess[KeyExchange.PROPOSAL_ENC_ALGS_CTOS];
+      c=Class.forName(getConfig(method));
+      c2scipher=(Cipher)(c.newInstance());
+      while(c2scipher.getBlockSize()>Ec2s.length){
+        buf.reset();
+        buf.putMPInt(K);
+        buf.putByte(H);
+        buf.putByte(Ec2s);
+        hash.update(buf.buffer, 0, buf.index);
+        byte[] foo=hash.digest();
+        byte[] bar=new byte[Ec2s.length+foo.length];
+	System.arraycopy(Ec2s, 0, bar, 0, Ec2s.length);
+	System.arraycopy(foo, 0, bar, Ec2s.length, foo.length);
+	Ec2s=bar;
+      }
+      c2scipher.init(Cipher.ENCRYPT_MODE, Ec2s, IVc2s);
+      c2scipher_size=c2scipher.getIVSize();
+
+      method=guess[KeyExchange.PROPOSAL_MAC_ALGS_CTOS];
+      c=Class.forName(getConfig(method));
+      c2smac=(MAC)(c.newInstance());
+      c2smac.init(MACc2s);
+
+      method=guess[KeyExchange.PROPOSAL_COMP_ALGS_CTOS];
+      initDeflater(method);
+
+      method=guess[KeyExchange.PROPOSAL_COMP_ALGS_STOC];
+      initInflater(method);
+    }
+    catch(Exception e){ 
+      if(e instanceof JSchException)
+        throw e;
+      throw new JSchException(e.toString(), e);
+      //System.err.println("updatekeys: "+e); 
+    }
+  }
+
+  /*public*/ /*synchronized*/ void write(Packet packet, Channel c, int length) throws Exception{
+    long t = getTimeout();
+    while(true){
+      if(in_kex){
+        if(t>0L && (System.currentTimeMillis()-kex_start_time)>t){
+          throw new JSchException("timeout in wating for rekeying process.");
+        }
+        try{Thread.sleep(10);}
+        catch(java.lang.InterruptedException e){};
+        continue;
+      }
+      synchronized(c){
+
+        if(c.rwsize<length){
+          try{ 
+            c.notifyme++;
+            c.wait(100); 
+          }
+          catch(java.lang.InterruptedException e){
+          }
+          finally{
+            c.notifyme--;
+          }
+        }
+
+        if(c.rwsize>=length){
+          c.rwsize-=length;
+          break;
+        }
+
+      }
+      if(c.close || !c.isConnected()){
+	throw new IOException("channel is broken");
+      }
+
+      boolean sendit=false;
+      int s=0;
+      byte command=0;
+      int recipient=-1;
+      synchronized(c){
+	if(c.rwsize>0){
+	  long len=c.rwsize;
+          if(len>length){
+            len=length;
+          }
+          if(len!=length){
+            s=packet.shift((int)len, 
+                           (c2scipher!=null ? c2scipher_size : 8),
+                           (c2smac!=null ? c2smac.getBlockSize() : 0));
+          }
+	  command=packet.buffer.getCommand();
+	  recipient=c.getRecipient();
+	  length-=len;
+	  c.rwsize-=len;
+	  sendit=true;
+	}
+      }
+      if(sendit){
+	_write(packet);
+        if(length==0){
+          return;
+        }
+	packet.unshift(command, recipient, s, length);
+      }
+
+      synchronized(c){
+        if(in_kex){
+          continue;
+        }
+        if(c.rwsize>=length){
+          c.rwsize-=length;
+          break;
+        }
+
+        //try{ 
+        //System.out.println("1wait: "+c.rwsize);
+        //  c.notifyme++;
+        //  c.wait(100); 
+        //}
+        //catch(java.lang.InterruptedException e){
+        //}
+        //finally{
+        //  c.notifyme--;
+        //}
+      }
+    }
+    _write(packet);
+  }
+
+  public void write(Packet packet) throws Exception{
+    // System.err.println("in_kex="+in_kex+" "+(packet.buffer.getCommand()));
+    long t = getTimeout();
+    while(in_kex){
+      if(t>0L && (System.currentTimeMillis()-kex_start_time)>t){
+        throw new JSchException("timeout in wating for rekeying process.");
+      }
+      byte command=packet.buffer.getCommand();
+      //System.err.println("command: "+command);
+      if(command==SSH_MSG_KEXINIT ||
+         command==SSH_MSG_NEWKEYS ||
+         command==SSH_MSG_KEXDH_INIT ||
+         command==SSH_MSG_KEXDH_REPLY ||
+         command==SSH_MSG_KEX_DH_GEX_GROUP ||
+         command==SSH_MSG_KEX_DH_GEX_INIT ||
+         command==SSH_MSG_KEX_DH_GEX_REPLY ||
+         command==SSH_MSG_KEX_DH_GEX_REQUEST ||
+         command==SSH_MSG_DISCONNECT){
+        break;
+      }
+      try{Thread.sleep(10);}
+      catch(java.lang.InterruptedException e){};
+    }
+    _write(packet);
+  }
+
+  private void _write(Packet packet) throws Exception{
+    synchronized(lock){
+      encode(packet);
+      if(io!=null){
+        io.put(packet);
+        seqo++;
+      }
+    }
+  }
+
+  Runnable thread;
+  public void run(){
+    thread=this;
+
+    byte[] foo;
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+    int i=0;
+    Channel channel;
+    int[] start=new int[1];
+    int[] length=new int[1];
+    KeyExchange kex=null;
+
+    int stimeout=0;
+    try{
+      while(isConnected &&
+	    thread!=null){
+        try{
+          buf=read(buf);
+          stimeout=0;
+        }
+        catch(InterruptedIOException/*SocketTimeoutException*/ ee){
+          if(!in_kex && stimeout<serverAliveCountMax){
+            sendKeepAliveMsg();
+            stimeout++;
+            continue;
+          }
+          else if(in_kex && stimeout<serverAliveCountMax){
+            stimeout++;
+            continue;
+          }
+          throw ee;
+        }
+
+	int msgType=buf.getCommand()&0xff;
+
+	if(kex!=null && kex.getState()==msgType){
+          kex_start_time=System.currentTimeMillis();
+	  boolean result=kex.next(buf);
+	  if(!result){
+	    throw new JSchException("verify: "+result);
+	  }
+	  continue;
+	}
+
+        switch(msgType){
+	case SSH_MSG_KEXINIT:
+//System.err.println("KEXINIT");
+	  kex=receive_kexinit(buf);
+	  break;
+
+	case SSH_MSG_NEWKEYS:
+//System.err.println("NEWKEYS");
+          send_newkeys();
+	  receive_newkeys(buf, kex);
+	  kex=null;
+	  break;
+
+	case SSH_MSG_CHANNEL_DATA:
+          buf.getInt(); 
+          buf.getByte(); 
+          buf.getByte(); 
+          i=buf.getInt(); 
+	  channel=Channel.getChannel(i, this);
+	  foo=buf.getString(start, length);
+	  if(channel==null){
+	    break;
+	  }
+
+          if(length[0]==0){
+	    break;
+          }
+
+try{
+	  channel.write(foo, start[0], length[0]);
+}
+catch(Exception e){
+//System.err.println(e);
+  try{channel.disconnect();}catch(Exception ee){}
+break;
+}
+	  int len=length[0];
+	  channel.setLocalWindowSize(channel.lwsize-len);
+ 	  if(channel.lwsize<channel.lwsize_max/2){
+            packet.reset();
+	    buf.putByte((byte)SSH_MSG_CHANNEL_WINDOW_ADJUST);
+	    buf.putInt(channel.getRecipient());
+	    buf.putInt(channel.lwsize_max-channel.lwsize);
+            synchronized(channel){
+              if(!channel.close)
+                write(packet);
+            }
+	    channel.setLocalWindowSize(channel.lwsize_max);
+	  }
+	  break;
+
+        case SSH_MSG_CHANNEL_EXTENDED_DATA:
+          buf.getInt();
+	  buf.getShort();
+	  i=buf.getInt();
+	  channel=Channel.getChannel(i, this);
+	  buf.getInt();                   // data_type_code == 1
+	  foo=buf.getString(start, length);
+	  //System.err.println("stderr: "+new String(foo,start[0],length[0]));
+	  if(channel==null){
+	    break;
+	  }
+
+          if(length[0]==0){
+	    break;
+          }
+
+	  channel.write_ext(foo, start[0], length[0]);
+
+	  len=length[0];
+	  channel.setLocalWindowSize(channel.lwsize-len);
+ 	  if(channel.lwsize<channel.lwsize_max/2){
+            packet.reset();
+	    buf.putByte((byte)SSH_MSG_CHANNEL_WINDOW_ADJUST);
+	    buf.putInt(channel.getRecipient());
+	    buf.putInt(channel.lwsize_max-channel.lwsize);
+            synchronized(channel){
+              if(!channel.close)
+                write(packet);
+            }
+	    channel.setLocalWindowSize(channel.lwsize_max);
+	  }
+	  break;
+
+	case SSH_MSG_CHANNEL_WINDOW_ADJUST:
+          buf.getInt(); 
+	  buf.getShort(); 
+	  i=buf.getInt(); 
+	  channel=Channel.getChannel(i, this);
+	  if(channel==null){
+	    break;
+	  }
+	  channel.addRemoteWindowSize(buf.getInt()); 
+	  break;
+
+	case SSH_MSG_CHANNEL_EOF:
+          buf.getInt(); 
+          buf.getShort(); 
+          i=buf.getInt(); 
+	  channel=Channel.getChannel(i, this);
+	  if(channel!=null){
+	    //channel.eof_remote=true;
+	    //channel.eof();
+	    channel.eof_remote();
+	  }
+	  /*
+	  packet.reset();
+	  buf.putByte((byte)SSH_MSG_CHANNEL_EOF);
+	  buf.putInt(channel.getRecipient());
+	  write(packet);
+	  */
+	  break;
+	case SSH_MSG_CHANNEL_CLOSE:
+          buf.getInt(); 
+	  buf.getShort(); 
+	  i=buf.getInt(); 
+	  channel=Channel.getChannel(i, this);
+	  if(channel!=null){
+//	      channel.close();
+	    channel.disconnect();
+	  }
+	  /*
+          if(Channel.pool.size()==0){
+	    thread=null;
+	  }
+	  */
+	  break;
+	case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
+          buf.getInt(); 
+	  buf.getShort(); 
+	  i=buf.getInt(); 
+	  channel=Channel.getChannel(i, this);
+	  if(channel==null){
+	    //break;
+	  }
+          int r=buf.getInt();
+          long rws=buf.getUInt();
+          int rps=buf.getInt();
+
+          channel.setRemoteWindowSize(rws);
+          channel.setRemotePacketSize(rps);
+          channel.open_confirmation=true;
+          channel.setRecipient(r);
+          break;
+	case SSH_MSG_CHANNEL_OPEN_FAILURE:
+          buf.getInt(); 
+	  buf.getShort(); 
+	  i=buf.getInt(); 
+	  channel=Channel.getChannel(i, this);
+	  if(channel==null){
+	    //break;
+	  }
+	  int reason_code=buf.getInt(); 
+	  //foo=buf.getString();  // additional textual information
+	  //foo=buf.getString();  // language tag 
+          channel.setExitStatus(reason_code);
+          channel.close=true;
+	  channel.eof_remote=true;
+	  channel.setRecipient(0);
+	  break;
+	case SSH_MSG_CHANNEL_REQUEST:
+          buf.getInt(); 
+	  buf.getShort(); 
+	  i=buf.getInt(); 
+	  foo=buf.getString(); 
+          boolean reply=(buf.getByte()!=0);
+	  channel=Channel.getChannel(i, this);
+	  if(channel!=null){
+	    byte reply_type=(byte)SSH_MSG_CHANNEL_FAILURE;
+	    if((Util.byte2str(foo)).equals("exit-status")){
+	      i=buf.getInt();             // exit-status
+	      channel.setExitStatus(i);
+	      reply_type=(byte)SSH_MSG_CHANNEL_SUCCESS;
+	    }
+	    if(reply){
+	      packet.reset();
+	      buf.putByte(reply_type);
+	      buf.putInt(channel.getRecipient());
+	      write(packet);
+	    }
+	  }
+	  else{
+	  }
+	  break;
+	case SSH_MSG_CHANNEL_OPEN:
+          buf.getInt(); 
+	  buf.getShort(); 
+	  foo=buf.getString(); 
+	  String ctyp=Util.byte2str(foo);
+          if(!"forwarded-tcpip".equals(ctyp) &&
+	     !("x11".equals(ctyp) && x11_forwarding) &&
+	     !("auth-agent@openssh.com".equals(ctyp) && agent_forwarding)){
+            //System.err.println("Session.run: CHANNEL OPEN "+ctyp); 
+	    //throw new IOException("Session.run: CHANNEL OPEN "+ctyp);
+	    packet.reset();
+	    buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_FAILURE);
+	    buf.putInt(buf.getInt());
+ 	    buf.putInt(Channel.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED);
+	    buf.putString(Util.empty);
+	    buf.putString(Util.empty);
+	    write(packet);
+	  }
+	  else{
+	    channel=Channel.getChannel(ctyp);
+	    addChannel(channel);
+	    channel.getData(buf);
+	    channel.init();
+
+	    Thread tmp=new Thread(channel);
+	    tmp.setName("Channel "+ctyp+" "+host);
+            if(daemon_thread){
+              tmp.setDaemon(daemon_thread);
+            }
+	    tmp.start();
+	    break;
+	  }
+	case SSH_MSG_CHANNEL_SUCCESS:
+          buf.getInt(); 
+	  buf.getShort(); 
+	  i=buf.getInt(); 
+	  channel=Channel.getChannel(i, this);
+	  if(channel==null){
+	    break;
+	  }
+	  channel.reply=1;
+	  break;
+	case SSH_MSG_CHANNEL_FAILURE:
+	  buf.getInt(); 
+	  buf.getShort(); 
+	  i=buf.getInt(); 
+	  channel=Channel.getChannel(i, this);
+	  if(channel==null){
+	    break;
+	  }
+	  channel.reply=0;
+	  break;
+	case SSH_MSG_GLOBAL_REQUEST:
+	  buf.getInt(); 
+	  buf.getShort(); 
+	  foo=buf.getString();       // request name
+	  reply=(buf.getByte()!=0);
+	  if(reply){
+	    packet.reset();
+	    buf.putByte((byte)SSH_MSG_REQUEST_FAILURE);
+	    write(packet);
+	  }
+	  break;
+	case SSH_MSG_REQUEST_FAILURE:
+	case SSH_MSG_REQUEST_SUCCESS:
+          Thread t=grr.getThread();
+          if(t!=null){
+            grr.setReply(msgType==SSH_MSG_REQUEST_SUCCESS? 1 : 0);
+            t.interrupt();
+          }
+	  break;
+	default:
+          //System.err.println("Session.run: unsupported type "+msgType); 
+	  throw new IOException("Unknown SSH message type "+msgType);
+	}
+      }
+    }
+    catch(Exception e){
+      in_kex=false;
+      if(JSch.getLogger().isEnabled(Logger.INFO)){
+        JSch.getLogger().log(Logger.INFO,
+                             "Caught an exception, leaving main loop due to " + e.getMessage());
+      }
+      //System.err.println("# Session.run");
+      //e.printStackTrace();
+    }
+    try{
+      disconnect();
+    }
+    catch(NullPointerException e){
+      //System.err.println("@1");
+      //e.printStackTrace();
+    }
+    catch(Exception e){
+      //System.err.println("@2");
+      //e.printStackTrace();
+    }
+    isConnected=false;
+  }
+
+  public void disconnect(){
+    if(!isConnected) return;
+    //System.err.println(this+": disconnect");
+    //Thread.dumpStack();
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO,
+                           "Disconnecting from "+host+" port "+port);
+    }
+    /*
+    for(int i=0; i<Channel.pool.size(); i++){
+      try{
+        Channel c=((Channel)(Channel.pool.elementAt(i)));
+	if(c.session==this) c.eof();
+      }
+      catch(Exception e){
+      }
+    } 
+    */
+
+    Channel.disconnect(this);
+
+    isConnected=false;
+
+    PortWatcher.delPort(this);
+    ChannelForwardedTCPIP.delPort(this);
+    ChannelX11.removeFakedCookie(this);
+
+    synchronized(lock){
+      if(connectThread!=null){
+        Thread.yield();
+        connectThread.interrupt();
+        connectThread=null;
+      }
+    }
+    thread=null;
+    try{
+      if(io!=null){
+	if(io.in!=null) io.in.close();
+	if(io.out!=null) io.out.close();
+	if(io.out_ext!=null) io.out_ext.close();
+      }
+      if(proxy==null){
+        if(socket!=null)
+	  socket.close();
+      }
+      else{
+	synchronized(proxy){
+	  proxy.close();	  
+	}
+	proxy=null;
+      }
+    }
+    catch(Exception e){
+//      e.printStackTrace();
+    }
+    io=null;
+    socket=null;
+//    synchronized(jsch.pool){
+//      jsch.pool.removeElement(this);
+//    }
+
+    jsch.removeSession(this);
+
+    //System.gc();
+  }
+
+  public int setPortForwardingL(int lport, String host, int rport) throws JSchException{
+    return setPortForwardingL("127.0.0.1", lport, host, rport);
+  }
+  public int setPortForwardingL(String boundaddress, int lport, String host, int rport) throws JSchException{
+    return setPortForwardingL(boundaddress, lport, host, rport, null);
+  }
+  public int setPortForwardingL(String boundaddress, int lport, String host, int rport, ServerSocketFactory ssf) throws JSchException{
+    PortWatcher pw=PortWatcher.addPort(this, boundaddress, lport, host, rport, ssf);
+    Thread tmp=new Thread(pw);
+    tmp.setName("PortWatcher Thread for "+host);
+    if(daemon_thread){
+      tmp.setDaemon(daemon_thread);
+    }
+    tmp.start();
+    return pw.lport;
+  }
+  public void delPortForwardingL(int lport) throws JSchException{
+    delPortForwardingL("127.0.0.1", lport);
+  }
+  public void delPortForwardingL(String boundaddress, int lport) throws JSchException{
+    PortWatcher.delPort(this, boundaddress, lport);
+  }
+  public String[] getPortForwardingL() throws JSchException{
+    return PortWatcher.getPortForwarding(this);
+  }
+
+  public void setPortForwardingR(int rport, String host, int lport) throws JSchException{
+    setPortForwardingR(null, rport, host, lport, (SocketFactory)null);
+  }
+  public void setPortForwardingR(String bind_address, int rport, String host, int lport) throws JSchException{
+    setPortForwardingR(bind_address, rport, host, lport, (SocketFactory)null);
+  }
+  public void setPortForwardingR(int rport, String host, int lport, SocketFactory sf) throws JSchException{
+    setPortForwardingR(null, rport, host, lport, sf);
+  }
+  public void setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf) throws JSchException{
+    ChannelForwardedTCPIP.addPort(this, bind_address, rport, host, lport, sf);
+    setPortForwarding(bind_address, rport);
+  }
+
+  public void setPortForwardingR(int rport, String daemon) throws JSchException{
+    setPortForwardingR(null, rport, daemon, null);
+  }
+  public void setPortForwardingR(int rport, String daemon, Object[] arg) throws JSchException{
+    setPortForwardingR(null, rport, daemon, arg);
+  }
+  public void setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) throws JSchException{
+    ChannelForwardedTCPIP.addPort(this, bind_address, rport, daemon, arg);
+    setPortForwarding(bind_address, rport);
+  }
+
+  private class GlobalRequestReply{
+    private Thread thread=null;
+    private int reply=-1;
+    void setThread(Thread thread){
+      this.thread=thread;
+      this.reply=-1;
+    }
+    Thread getThread(){ return thread; }
+    void setReply(int reply){ this.reply=reply; }
+    int getReply(){ return this.reply; }
+  }
+  private GlobalRequestReply grr=new GlobalRequestReply();
+  private void setPortForwarding(String bind_address, int rport) throws JSchException{
+    synchronized(grr){
+    Buffer buf=new Buffer(100); // ??
+    Packet packet=new Packet(buf);
+
+    String address_to_bind=ChannelForwardedTCPIP.normalize(bind_address);
+
+    grr.setThread(Thread.currentThread());
+
+    try{
+      // byte SSH_MSG_GLOBAL_REQUEST 80
+      // string "tcpip-forward"
+      // boolean want_reply
+      // string  address_to_bind
+      // uint32  port number to bind
+      packet.reset();
+      buf.putByte((byte) SSH_MSG_GLOBAL_REQUEST);
+      buf.putString(Util.str2byte("tcpip-forward"));
+      buf.putByte((byte)1);
+      buf.putString(Util.str2byte(address_to_bind));
+      buf.putInt(rport);
+      write(packet);
+    }
+    catch(Exception e){
+      grr.setThread(null);
+      if(e instanceof Throwable)
+        throw new JSchException(e.toString(), (Throwable)e);
+      throw new JSchException(e.toString());
+    }
+
+    int count = 0;
+    int reply = grr.getReply();
+    while(count < 10 && reply == -1){
+      try{ Thread.sleep(1000); }
+      catch(Exception e){
+      }
+      count++; 
+      reply = grr.getReply();
+    }
+    grr.setThread(null);
+    if(reply != 1){
+      throw new JSchException("remote port forwarding failed for listen port "+rport);
+    }
+    }
+  }
+  public void delPortForwardingR(int rport) throws JSchException{
+    ChannelForwardedTCPIP.delPort(this, rport);
+  }
+
+  private void initDeflater(String method) throws JSchException{
+    if(method.equals("none")){
+      deflater=null;
+      return;
+    }
+    String foo=getConfig(method);
+    if(foo!=null){
+      if(method.equals("zlib") ||
+         (isAuthed && method.equals("zlib@openssh.com"))){
+        try{
+          Class c=Class.forName(foo);
+          deflater=(Compression)(c.newInstance());
+          int level=6;
+          try{ level=Integer.parseInt(getConfig("compression_level"));}
+          catch(Exception ee){ }
+          deflater.init(Compression.DEFLATER, level);
+        }
+        catch(Exception ee){
+          throw new JSchException(ee.toString(), ee);
+          //System.err.println(foo+" isn't accessible.");
+        }
+      }
+    }
+  }
+  private void initInflater(String method) throws JSchException{
+    if(method.equals("none")){
+      inflater=null;
+      return;
+    }
+    String foo=getConfig(method);
+    if(foo!=null){
+      if(method.equals("zlib") ||
+         (isAuthed && method.equals("zlib@openssh.com"))){
+        try{
+          Class c=Class.forName(foo);
+          inflater=(Compression)(c.newInstance());
+          inflater.init(Compression.INFLATER, 0);
+        }
+        catch(Exception ee){
+          throw new JSchException(ee.toString(), ee);
+	    //System.err.println(foo+" isn't accessible.");
+        }
+      }
+    }
+  }
+
+  void addChannel(Channel channel){
+    channel.setSession(this);
+  }
+
+  public void setProxy(Proxy proxy){ this.proxy=proxy; }
+  public void setHost(String host){ this.host=host; }
+  public void setPort(int port){ this.port=port; }
+  void setUserName(String username){ this.username=username; }
+  public void setUserInfo(UserInfo userinfo){ this.userinfo=userinfo; }
+  public UserInfo getUserInfo(){ return userinfo; }
+  public void setInputStream(InputStream in){ this.in=in; }
+  public void setOutputStream(OutputStream out){ this.out=out; }
+  public void setX11Host(String host){ ChannelX11.setHost(host); }
+  public void setX11Port(int port){ ChannelX11.setPort(port); }
+  public void setX11Cookie(String cookie){ ChannelX11.setCookie(cookie); }
+  public void setPassword(String password){
+    if(password!=null)
+      this.password=Util.str2byte(password);
+  }
+  public void setPassword(byte[] password){ 
+    if(password!=null){
+      this.password=new byte[password.length];
+      System.arraycopy(password, 0, this.password, 0, password.length);
+    }
+  }
+
+  public void setConfig(java.util.Properties newconf){
+    setConfig((java.util.Hashtable)newconf);
+  }
+ 
+  public void setConfig(java.util.Hashtable newconf){
+    synchronized(lock){
+      if(config==null) 
+        config=new java.util.Hashtable();
+      for(java.util.Enumeration e=newconf.keys() ; e.hasMoreElements() ;) {
+        String key=(String)(e.nextElement());
+        config.put(key, (String)(newconf.get(key)));
+      }
+    }
+  }
+
+  public void setConfig(String key, String value){
+    synchronized(lock){ 
+      if(config==null){
+        config=new java.util.Hashtable();
+      }
+      config.put(key, value);
+    }
+  }
+
+  public String getConfig(String key){
+    Object foo=null;
+    if(config!=null){
+      foo=config.get(key);
+      if(foo instanceof String) return (String)foo;
+    }
+    foo=jsch.getConfig(key);
+    if(foo instanceof String) return (String)foo;
+    return null;
+  }
+
+  public void setSocketFactory(SocketFactory sfactory){ 
+    socket_factory=sfactory;
+  }
+  public boolean isConnected(){ return isConnected; }
+  public int getTimeout(){ return timeout; }
+  public void setTimeout(int timeout) throws JSchException {
+    if(socket==null){
+      if(timeout<0){
+        throw new JSchException("invalid timeout value");
+      }
+      this.timeout=timeout;
+      return;
+    }
+    try{
+      socket.setSoTimeout(timeout);
+      this.timeout=timeout;
+    }
+    catch(Exception e){
+      if(e instanceof Throwable)
+        throw new JSchException(e.toString(), (Throwable)e);
+      throw new JSchException(e.toString());
+    }
+  }
+  public String getServerVersion(){
+    return Util.byte2str(V_S);
+  }
+  public String getClientVersion(){
+    return Util.byte2str(V_C);
+  }
+  public void setClientVersion(String cv){
+    V_C=Util.str2byte(cv);
+  }
+
+  public void sendIgnore() throws Exception{
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+    packet.reset();
+    buf.putByte((byte)SSH_MSG_IGNORE);
+    write(packet);
+  }
+
+  private static final byte[] keepalivemsg=Util.str2byte("keepalive@jcraft.com");
+  public void sendKeepAliveMsg() throws Exception{
+    Buffer buf=new Buffer();
+    Packet packet=new Packet(buf);
+    packet.reset();
+    buf.putByte((byte)SSH_MSG_GLOBAL_REQUEST);
+    buf.putString(keepalivemsg);
+    buf.putByte((byte)1);
+    write(packet);
+  }
+  
+  private HostKey hostkey=null;
+  public HostKey getHostKey(){ return hostkey; }
+  public String getHost(){return host;}
+  public String getUserName(){return username;}
+  public int getPort(){return port;}
+  public void setHostKeyAlias(String hostKeyAlias){
+    this.hostKeyAlias=hostKeyAlias;
+  }
+  public String getHostKeyAlias(){
+    return hostKeyAlias;
+  }
+
+  public void setServerAliveInterval(int interval) throws JSchException {
+    setTimeout(interval);
+    this.serverAliveInterval=interval;
+  }
+  public void setServerAliveCountMax(int count){
+    this.serverAliveCountMax=count;
+  }
+
+  public int getServerAliveInterval(){
+    return this.serverAliveInterval;
+  }
+  public int getServerAliveCountMax(){
+    return this.serverAliveCountMax;
+  }
+
+  public void setDaemonThread(boolean enable){
+    this.daemon_thread=enable;
+  }
+
+  private String[] checkCiphers(String ciphers){
+    if(ciphers==null || ciphers.length()==0)
+      return null;
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "CheckCiphers: "+ciphers);
+    }
+
+    java.util.Vector result=new java.util.Vector();
+    String[] _ciphers=Util.split(ciphers, ",");
+    for(int i=0; i<_ciphers.length; i++){
+      if(!checkCipher(getConfig(_ciphers[i]))){
+        result.addElement(_ciphers[i]);
+      }
+    }
+    if(result.size()==0)
+      return null;
+    String[] foo=new String[result.size()];
+    System.arraycopy(result.toArray(), 0, foo, 0, result.size());
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      for(int i=0; i<foo.length; i++){
+        JSch.getLogger().log(Logger.INFO, 
+                             foo[i]+" is not available.");
+      }
+    }
+
+    return foo;
+  }
+
+  static boolean checkCipher(String cipher){
+    try{
+      Class c=Class.forName(cipher);
+      Cipher _c=(Cipher)(c.newInstance());
+      _c.init(Cipher.ENCRYPT_MODE,
+              new byte[_c.getBlockSize()],
+              new byte[_c.getIVSize()]);
+      return true;
+    }
+    catch(Exception e){
+      return false;
+    }
+  }
+
+  private String[] checkKexes(String kexes){
+    if(kexes==null || kexes.length()==0)
+      return null;
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "CheckKexes: "+kexes);
+    }
+
+    java.util.Vector result=new java.util.Vector();
+    String[] _kexes=Util.split(kexes, ",");
+    for(int i=0; i<_kexes.length; i++){
+      if(!checkKex(this, getConfig(_kexes[i]))){
+        result.addElement(_kexes[i]);
+      }
+    }
+    if(result.size()==0)
+      return null;
+    String[] foo=new String[result.size()];
+    System.arraycopy(result.toArray(), 0, foo, 0, result.size());
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      for(int i=0; i<foo.length; i++){
+        JSch.getLogger().log(Logger.INFO, 
+                             foo[i]+" is not available.");
+      }
+    }
+
+    return foo;
+  }
+
+  static boolean checkKex(Session s, String kex){
+    try{
+      Class c=Class.forName(kex);
+      KeyExchange _c=(KeyExchange)(c.newInstance());
+      _c.init(s ,null, null, null, null);
+      return true;
+    }
+    catch(Exception e){ return false; }
+  }
+}
diff --git a/java/com/jcraft/jsch/SftpATTRS.java b/java/com/jcraft/jsch/SftpATTRS.java
new file mode 100644
index 0000000..e89b243
--- /dev/null
+++ b/java/com/jcraft/jsch/SftpATTRS.java
@@ -0,0 +1,263 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/*
+  uint32   flags
+  uint64   size           present only if flag SSH_FILEXFER_ATTR_SIZE
+  uint32   uid            present only if flag SSH_FILEXFER_ATTR_UIDGID
+  uint32   gid            present only if flag SSH_FILEXFER_ATTR_UIDGID
+  uint32   permissions    present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
+  uint32   atime          present only if flag SSH_FILEXFER_ACMODTIME
+  uint32   mtime          present only if flag SSH_FILEXFER_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
+*/
+public class SftpATTRS {
+
+  static final int S_ISUID = 04000; // set user ID on execution
+  static final int S_ISGID = 02000; // set group ID on execution
+  static final int S_ISVTX = 01000; // sticky bit   ****** NOT DOCUMENTED *****
+
+  static final int S_IRUSR = 00400; // read by owner
+  static final int S_IWUSR = 00200; // write by owner
+  static final int S_IXUSR = 00100; // execute/search by owner
+  static final int S_IREAD = 00400; // read by owner
+  static final int S_IWRITE= 00200; // write by owner
+  static final int S_IEXEC = 00100; // execute/search by owner
+
+  static final int S_IRGRP = 00040; // read by group
+  static final int S_IWGRP = 00020; // write by group
+  static final int S_IXGRP = 00010; // execute/search by group
+
+  static final int S_IROTH = 00004; // read by others
+  static final int S_IWOTH = 00002; // write by others
+  static final int S_IXOTH = 00001; // execute/search by others
+
+  private static final int pmask = 0xFFF;
+
+  public String getPermissionsString() {
+    StringBuffer buf = new StringBuffer(10);
+
+    if(isDir()) buf.append('d');
+    else if(isLink()) buf.append('l');
+    else buf.append('-');
+
+    if((permissions & S_IRUSR)!=0) buf.append('r');
+    else buf.append('-');
+
+    if((permissions & S_IWUSR)!=0) buf.append('w');
+    else buf.append('-');
+
+    if((permissions & S_ISUID)!=0) buf.append('s');
+    else if ((permissions & S_IXUSR)!=0) buf.append('x');
+    else buf.append('-');
+
+    if((permissions & S_IRGRP)!=0) buf.append('r');
+    else buf.append('-');
+
+    if((permissions & S_IWGRP)!=0) buf.append('w');
+    else buf.append('-');
+
+    if((permissions & S_ISGID)!=0) buf.append('s');
+    else if((permissions & S_IXGRP)!=0) buf.append('x');
+    else buf.append('-');
+
+    if((permissions & S_IROTH) != 0) buf.append('r');
+    else buf.append('-');
+
+    if((permissions & S_IWOTH) != 0) buf.append('w');
+    else buf.append('-');
+
+    if((permissions & S_IXOTH) != 0) buf.append('x');
+    else buf.append('-');
+    return (buf.toString());
+  }
+
+  public String  getAtimeString(){
+    SimpleDateFormat locale=new SimpleDateFormat();
+    return (locale.format(new Date(atime)));
+  }
+
+  public String  getMtimeString(){
+    Date date= new Date(((long)mtime)*1000);
+    return (date.toString());
+  }
+
+  public static final int SSH_FILEXFER_ATTR_SIZE=         0x00000001;
+  public static final int SSH_FILEXFER_ATTR_UIDGID=       0x00000002;
+  public static final int SSH_FILEXFER_ATTR_PERMISSIONS=  0x00000004;
+  public static final int SSH_FILEXFER_ATTR_ACMODTIME=    0x00000008;
+  public static final int SSH_FILEXFER_ATTR_EXTENDED=     0x80000000;
+
+  static final int S_IFDIR=0x4000;
+  static final int S_IFLNK=0xa000;
+
+  int flags=0;
+  long size;
+  int uid;
+  int gid;
+  int permissions;
+  int atime;
+  int mtime;
+  String[] extended=null;
+
+  private SftpATTRS(){
+  }
+
+  static SftpATTRS getATTR(Buffer buf){
+    SftpATTRS attr=new SftpATTRS();	
+    attr.flags=buf.getInt();
+    if((attr.flags&SSH_FILEXFER_ATTR_SIZE)!=0){ attr.size=buf.getLong(); }
+    if((attr.flags&SSH_FILEXFER_ATTR_UIDGID)!=0){
+      attr.uid=buf.getInt(); attr.gid=buf.getInt();
+    }
+    if((attr.flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0){ 
+      attr.permissions=buf.getInt();
+    }
+    if((attr.flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ 
+      attr.atime=buf.getInt();
+    }
+    if((attr.flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ 
+      attr.mtime=buf.getInt(); 
+    }
+    if((attr.flags&SSH_FILEXFER_ATTR_EXTENDED)!=0){
+      int count=buf.getInt();
+      if(count>0){
+	attr.extended=new String[count*2];
+	for(int i=0; i<count; i++){
+	  attr.extended[i*2]=Util.byte2str(buf.getString());
+	  attr.extended[i*2+1]=Util.byte2str(buf.getString());
+	}
+      }
+    }
+    return attr;
+  } 
+
+  int length(){
+    int len=4;
+
+    if((flags&SSH_FILEXFER_ATTR_SIZE)!=0){ len+=8; }
+    if((flags&SSH_FILEXFER_ATTR_UIDGID)!=0){ len+=8; }
+    if((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0){ len+=4; }
+    if((flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ len+=8; }
+    if((flags&SSH_FILEXFER_ATTR_EXTENDED)!=0){
+      len+=4;
+      int count=extended.length/2;
+      if(count>0){
+	for(int i=0; i<count; i++){
+	  len+=4; len+=extended[i*2].length();
+	  len+=4; len+=extended[i*2+1].length();
+	}
+      }
+    }
+    return len;
+  }
+
+  void dump(Buffer buf){
+    buf.putInt(flags);
+    if((flags&SSH_FILEXFER_ATTR_SIZE)!=0){ buf.putLong(size); }
+    if((flags&SSH_FILEXFER_ATTR_UIDGID)!=0){
+      buf.putInt(uid); buf.putInt(gid);
+    }
+    if((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0){ 
+      buf.putInt(permissions);
+    }
+    if((flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ buf.putInt(atime); }
+    if((flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ buf.putInt(mtime); }
+    if((flags&SSH_FILEXFER_ATTR_EXTENDED)!=0){
+      int count=extended.length/2;
+      if(count>0){
+	for(int i=0; i<count; i++){
+	  buf.putString(Util.str2byte(extended[i*2]));
+	  buf.putString(Util.str2byte(extended[i*2+1]));
+	}
+      }
+    }
+  }
+  void setFLAGS(int flags){
+    this.flags=flags;
+  }
+  public void setSIZE(long size){
+    flags|=SSH_FILEXFER_ATTR_SIZE;
+    this.size=size;
+  }
+  public void setUIDGID(int uid, int gid){
+    flags|=SSH_FILEXFER_ATTR_UIDGID;
+    this.uid=uid;
+    this.gid=gid;
+  }
+  public void setACMODTIME(int atime, int mtime){
+    flags|=SSH_FILEXFER_ATTR_ACMODTIME;
+    this.atime=atime;
+    this.mtime=mtime;
+  }
+  public void setPERMISSIONS(int permissions){
+    flags|=SSH_FILEXFER_ATTR_PERMISSIONS;
+    permissions=(this.permissions&~pmask)|(permissions&pmask);
+    this.permissions=permissions;
+  }
+
+  public boolean isDir(){
+    return ((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0 && 
+	    ((permissions&S_IFDIR)==S_IFDIR));
+  }      
+  public boolean isLink(){
+    return ((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0 && 
+	    ((permissions&S_IFLNK)==S_IFLNK));
+  }      
+  public int getFlags() { return flags; }
+  public long getSize() { return size; }
+  public int getUId() { return uid; }
+  public int getGId() { return gid; }
+  public int getPermissions() { return permissions; }
+  public int getATime() { return atime; }
+  public int getMTime() { return mtime; }
+  public String[] getExtended() { return extended; }
+
+  public String toString() {
+    return (getPermissionsString()+" "+getUId()+" "+getGId()+" "+getSize()+" "+getMtimeString());
+  }
+  /*
+  public String toString(){
+    return (((flags&SSH_FILEXFER_ATTR_SIZE)!=0) ? ("size:"+size+" ") : "")+
+           (((flags&SSH_FILEXFER_ATTR_UIDGID)!=0) ? ("uid:"+uid+",gid:"+gid+" ") : "")+
+           (((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0) ? ("permissions:0x"+Integer.toHexString(permissions)+" ") : "")+
+           (((flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0) ? ("atime:"+atime+",mtime:"+mtime+" ") : "")+
+           (((flags&SSH_FILEXFER_ATTR_EXTENDED)!=0) ? ("extended:?"+" ") : "");
+  }
+  */
+}
diff --git a/java/com/jcraft/jsch/SftpException.java b/java/com/jcraft/jsch/SftpException.java
new file mode 100644
index 0000000..6a6c1ff
--- /dev/null
+++ b/java/com/jcraft/jsch/SftpException.java
@@ -0,0 +1,51 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class SftpException extends Exception{
+  //private static final long serialVersionUID=-5616888495583253811L;
+  public int id;
+  private Throwable cause=null;
+  public SftpException (int id, String message) {
+    super(message);
+    this.id=id;
+  }
+  public SftpException (int id, String message, Throwable e) {
+    super(message);
+    this.id=id;
+    this.cause=e;
+  }
+  public String toString(){
+    return id+": "+getMessage();
+  }
+  public Throwable getCause(){
+    return this.cause;
+  }
+}
diff --git a/java/com/jcraft/jsch/SftpProgressMonitor.java b/java/com/jcraft/jsch/SftpProgressMonitor.java
new file mode 100644
index 0000000..bd91688
--- /dev/null
+++ b/java/com/jcraft/jsch/SftpProgressMonitor.java
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface SftpProgressMonitor{
+  public static final int PUT=0;
+  public static final int GET=1;
+  public static final long UNKNOWN_SIZE = -1L;
+  void init(int op, String src, String dest, long max);
+  boolean count(long count);
+  void end();
+}
diff --git a/java/com/jcraft/jsch/SignatureDSA.java b/java/com/jcraft/jsch/SignatureDSA.java
new file mode 100644
index 0000000..6f44b7f
--- /dev/null
+++ b/java/com/jcraft/jsch/SignatureDSA.java
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface SignatureDSA{
+  void init() throws Exception;
+  void setPubKey(byte[] y, byte[] p, byte[] q, byte[] g) throws Exception;
+  void setPrvKey(byte[] x, byte[] p, byte[] q, byte[] g) throws Exception;
+  void update(byte[] H) throws Exception;
+  boolean verify(byte[] sig) throws Exception;
+  byte[] sign() throws Exception;
+}
diff --git a/java/com/jcraft/jsch/SignatureRSA.java b/java/com/jcraft/jsch/SignatureRSA.java
new file mode 100644
index 0000000..e8e6105
--- /dev/null
+++ b/java/com/jcraft/jsch/SignatureRSA.java
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface SignatureRSA{
+  void init() throws Exception;
+  void setPubKey(byte[] e, byte[] n) throws Exception;
+  void setPrvKey(byte[] d, byte[] n) throws Exception;
+  void update(byte[] H) throws Exception;
+  boolean verify(byte[] sig) throws Exception;
+  byte[] sign() throws Exception;
+}
diff --git a/java/com/jcraft/jsch/SocketFactory.java b/java/com/jcraft/jsch/SocketFactory.java
new file mode 100644
index 0000000..aaac0dc
--- /dev/null
+++ b/java/com/jcraft/jsch/SocketFactory.java
@@ -0,0 +1,40 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.net.*;
+import java.io.*;
+
+public interface SocketFactory{
+  public Socket createSocket(String host, int port)throws IOException,
+							  UnknownHostException;
+  public InputStream getInputStream(Socket socket)throws IOException;
+  public OutputStream getOutputStream(Socket socket)throws IOException;
+}
diff --git a/java/com/jcraft/jsch/UIKeyboardInteractive.java b/java/com/jcraft/jsch/UIKeyboardInteractive.java
new file mode 100644
index 0000000..23af9c3
--- /dev/null
+++ b/java/com/jcraft/jsch/UIKeyboardInteractive.java
@@ -0,0 +1,38 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface UIKeyboardInteractive{
+  String[] promptKeyboardInteractive(String destination,
+				     String name,
+				     String instruction,
+				     String[] prompt,
+				     boolean[] echo);
+}
diff --git a/java/com/jcraft/jsch/UserAuth.java b/java/com/jcraft/jsch/UserAuth.java
new file mode 100644
index 0000000..085a950
--- /dev/null
+++ b/java/com/jcraft/jsch/UserAuth.java
@@ -0,0 +1,53 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public abstract class UserAuth{
+  protected static final int SSH_MSG_USERAUTH_REQUEST=               50;
+  protected static final int SSH_MSG_USERAUTH_FAILURE=               51;
+  protected static final int SSH_MSG_USERAUTH_SUCCESS=               52;
+  protected static final int SSH_MSG_USERAUTH_BANNER=                53;
+  protected static final int SSH_MSG_USERAUTH_INFO_REQUEST=          60;
+  protected static final int SSH_MSG_USERAUTH_INFO_RESPONSE=         61;
+  protected static final int SSH_MSG_USERAUTH_PK_OK=                 60;
+
+  protected UserInfo userinfo;
+  protected Packet packet;
+  protected Buffer buf;
+  protected String username;
+
+  public boolean start(Session session) throws Exception{
+    this.userinfo=session.getUserInfo();
+    this.packet=session.packet;
+    this.buf=packet.getBuffer();
+    this.username=session.getUserName();
+    return true;
+  }
+}
diff --git a/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java b/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java
new file mode 100644
index 0000000..15856cb
--- /dev/null
+++ b/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java
@@ -0,0 +1,227 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING
+NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class UserAuthGSSAPIWithMIC extends UserAuth {
+  private static final int SSH_MSG_USERAUTH_GSSAPI_RESPONSE=         60;
+  private static final int SSH_MSG_USERAUTH_GSSAPI_TOKEN=            61;
+  private static final int SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE=63;
+  private static final int SSH_MSG_USERAUTH_GSSAPI_ERROR=            64;
+  private static final int SSH_MSG_USERAUTH_GSSAPI_ERRTOK=           65;
+  private static final int SSH_MSG_USERAUTH_GSSAPI_MIC=              66;
+
+  private static final byte[][] supported_oid={
+    // OID 1.2.840.113554.1.2.2 in DER
+    {(byte)0x6,(byte)0x9,(byte)0x2a,(byte)0x86,(byte)0x48,
+     (byte)0x86,(byte)0xf7,(byte)0x12,(byte)0x1,(byte)0x2,
+     (byte)0x2}
+  };
+
+  private static final String[] supported_method={
+    "gssapi-with-mic.krb5"
+  };
+
+  public boolean start(Session session)throws Exception{
+    super.start(session);
+
+    byte[] _username=Util.str2byte(username);
+
+    packet.reset();
+
+    // byte            SSH_MSG_USERAUTH_REQUEST(50)
+    // string          user name(in ISO-10646 UTF-8 encoding)
+    // string          service name(in US-ASCII)
+    // string          "gssapi"(US-ASCII)
+    // uint32          n, the number of OIDs client supports
+    // string[n]       mechanism OIDS
+    buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST);
+    buf.putString(_username);
+    buf.putString(Util.str2byte("ssh-connection"));
+    buf.putString(Util.str2byte("gssapi-with-mic"));
+    buf.putInt(supported_oid.length);
+    for(int i=0; i<supported_oid.length; i++){
+      buf.putString(supported_oid[i]);
+    }
+    session.write(packet);
+
+    String method=null;
+    int command;
+    while(true){
+      buf=session.read(buf);
+      command=buf.getCommand()&0xff;
+
+      if(command==SSH_MSG_USERAUTH_FAILURE){
+        return false;
+      }
+      
+      if(command==SSH_MSG_USERAUTH_GSSAPI_RESPONSE){
+        buf.getInt(); buf.getByte(); buf.getByte();
+        byte[] message=buf.getString();
+
+        for(int i=0; i<supported_oid.length; i++){
+          if(Util.array_equals(message, supported_oid[i])){
+            method=supported_method[i];
+            break;
+          }
+        }
+
+        if(method==null){
+          return false;
+        }
+
+        break; // success
+      }
+
+      if(command==SSH_MSG_USERAUTH_BANNER){
+        buf.getInt(); buf.getByte(); buf.getByte();
+        byte[] _message=buf.getString();
+        byte[] lang=buf.getString();
+        String message=Util.byte2str(_message);
+        if(userinfo!=null){
+          userinfo.showMessage(message);
+        }
+        continue;
+      }
+      return false;
+    }
+
+    GSSContext context=null;
+    try{
+      Class c=Class.forName(session.getConfig(method));
+      context=(GSSContext)(c.newInstance());
+    }
+    catch(Exception e){ 
+      return false;
+    }
+
+    try{
+      context.create(username, session.host);
+    }
+    catch(JSchException e){
+      return false;
+    }
+
+    byte[] token=new byte[0];
+
+    while(!context.isEstablished()){
+      try{
+        token=context.init(token, 0, token.length);
+      }
+      catch(JSchException e){
+        // TODO
+        // ERRTOK should be sent?
+        // byte        SSH_MSG_USERAUTH_GSSAPI_ERRTOK
+        // string      error token
+        return false;
+      }
+
+      if(token!=null){
+        packet.reset();
+        buf.putByte((byte)SSH_MSG_USERAUTH_GSSAPI_TOKEN);
+        buf.putString(token);
+        session.write(packet);
+      }
+
+      if(!context.isEstablished()){
+        buf=session.read(buf);
+        command=buf.getCommand()&0xff;
+        if(command==SSH_MSG_USERAUTH_GSSAPI_ERROR){
+          // uint32    major_status
+          // uint32    minor_status
+          // string    message
+          // string    language tag
+
+          buf=session.read(buf);
+          command=buf.getCommand()&0xff;
+          //return false;
+        }
+        else if(command==SSH_MSG_USERAUTH_GSSAPI_ERRTOK){
+          // string error token
+
+          buf=session.read(buf);
+          command=buf.getCommand()&0xff;
+          //return false;
+        }
+
+        if(command==SSH_MSG_USERAUTH_FAILURE){
+          return false;
+        }
+
+        buf.getInt(); buf.getByte(); buf.getByte();
+        token=buf.getString();
+      }
+    }
+
+    Buffer mbuf=new Buffer();
+    // string    session identifier
+    // byte      SSH_MSG_USERAUTH_REQUEST
+    // string    user name
+    // string    service
+    // string    "gssapi-with-mic"
+    mbuf.putString(session.getSessionId());
+    mbuf.putByte((byte)SSH_MSG_USERAUTH_REQUEST);
+    mbuf.putString(_username);
+    mbuf.putString(Util.str2byte("ssh-connection"));
+    mbuf.putString(Util.str2byte("gssapi-with-mic"));
+
+    byte[] mic=context.getMIC(mbuf.buffer, 0, mbuf.getLength());
+
+    if(mic==null){
+      return false;
+    }
+
+    packet.reset();
+    buf.putByte((byte)SSH_MSG_USERAUTH_GSSAPI_MIC);
+    buf.putString(mic);
+    session.write(packet);
+
+    context.dispose();
+
+    buf=session.read(buf);
+    command=buf.getCommand()&0xff;
+
+    if(command==SSH_MSG_USERAUTH_SUCCESS){
+      return true;
+    }
+    else if(command==SSH_MSG_USERAUTH_FAILURE){
+      buf.getInt(); buf.getByte(); buf.getByte(); 
+      byte[] foo=buf.getString();
+      int partial_success=buf.getByte();
+      //System.err.println(new String(foo)+
+      //		 " partial_success:"+(partial_success!=0));
+      if(partial_success!=0){
+        throw new JSchPartialAuthException(Util.byte2str(foo));
+      }
+    }
+    return false;
+  }
+}
+
+
diff --git a/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java b/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java
new file mode 100644
index 0000000..2994796
--- /dev/null
+++ b/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java
@@ -0,0 +1,203 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class UserAuthKeyboardInteractive extends UserAuth{
+  public boolean start(Session session) throws Exception{
+    super.start(session);
+
+    if(userinfo!=null && !(userinfo instanceof UIKeyboardInteractive)){
+      return false;
+    }
+
+    String dest=username+"@"+session.host;
+    if(session.port!=22){
+      dest+=(":"+session.port);
+    }
+    byte[] password=session.password;
+
+    boolean cancel=false;
+
+    byte[] _username=null;
+    _username=Util.str2byte(username);
+
+    while(true){
+
+      if(session.auth_failures >= session.max_auth_tries){
+	return false;
+      }
+
+      // send
+      // byte      SSH_MSG_USERAUTH_REQUEST(50)
+      // string    user name (ISO-10646 UTF-8, as defined in [RFC-2279])
+      // string    service name (US-ASCII) "ssh-userauth" ? "ssh-connection"
+      // string    "keyboard-interactive" (US-ASCII)
+      // string    language tag (as defined in [RFC-3066])
+      // string    submethods (ISO-10646 UTF-8)
+      packet.reset();
+      buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST);
+      buf.putString(_username);
+      buf.putString(Util.str2byte("ssh-connection"));
+      //buf.putString("ssh-userauth".getBytes());
+      buf.putString(Util.str2byte("keyboard-interactive"));
+      buf.putString(Util.empty);
+      buf.putString(Util.empty);
+      session.write(packet);
+
+      boolean firsttime=true;
+      loop:
+      while(true){
+        buf=session.read(buf);
+        int command=buf.getCommand()&0xff;
+
+	if(command==SSH_MSG_USERAUTH_SUCCESS){
+	  return true;
+	}
+	if(command==SSH_MSG_USERAUTH_BANNER){
+	  buf.getInt(); buf.getByte(); buf.getByte();
+	  byte[] _message=buf.getString();
+	  byte[] lang=buf.getString();
+	  String message=Util.byte2str(_message);
+	  if(userinfo!=null){
+	    userinfo.showMessage(message);
+	  }
+	  continue loop;
+	}
+	if(command==SSH_MSG_USERAUTH_FAILURE){
+	  buf.getInt(); buf.getByte(); buf.getByte(); 
+	  byte[] foo=buf.getString();
+	  int partial_success=buf.getByte();
+//	  System.err.println(new String(foo)+
+//			     " partial_success:"+(partial_success!=0));
+
+	  if(partial_success!=0){
+	    throw new JSchPartialAuthException(Util.byte2str(foo));
+	  }
+
+	  if(firsttime){
+	    return false;
+	    //throw new JSchException("USERAUTH KI is not supported");
+	    //cancel=true;  // ??
+	  }
+          session.auth_failures++;
+	  break;
+	}
+	if(command==SSH_MSG_USERAUTH_INFO_REQUEST){
+	  firsttime=false;
+	  buf.getInt(); buf.getByte(); buf.getByte();
+	  String name=Util.byte2str(buf.getString());
+	  String instruction=Util.byte2str(buf.getString());
+	  String languate_tag=Util.byte2str(buf.getString());
+	  int num=buf.getInt();
+	  String[] prompt=new String[num];
+	  boolean[] echo=new boolean[num];
+	  for(int i=0; i<num; i++){
+	    prompt[i]=Util.byte2str(buf.getString());
+	    echo[i]=(buf.getByte()!=0);
+	  }
+
+	  byte[][] response=null;
+
+          if(password!=null && 
+             prompt.length==1 &&
+             !echo[0] &&
+             prompt[0].toLowerCase().startsWith("password:")){
+            response=new byte[1][];
+            response[0]=password;
+            password=null;
+          }
+          else if(num>0
+	     ||(name.length()>0 || instruction.length()>0)
+	     ){
+	    if(userinfo!=null){
+              UIKeyboardInteractive kbi=(UIKeyboardInteractive)userinfo;
+              String[] _response=kbi.promptKeyboardInteractive(dest,
+                                                               name,
+                                                               instruction,
+                                                               prompt,
+                                                               echo);
+              if(_response!=null){
+                response=new byte[_response.length][];
+                for(int i=0; i<_response.length; i++){
+                  response[i]=Util.str2byte(_response[i]);
+                }
+              }
+	    }
+	  }
+
+	  // byte      SSH_MSG_USERAUTH_INFO_RESPONSE(61)
+	  // int       num-responses
+	  // string    response[1] (ISO-10646 UTF-8)
+	  // ...
+	  // string    response[num-responses] (ISO-10646 UTF-8)
+	  packet.reset();
+	  buf.putByte((byte)SSH_MSG_USERAUTH_INFO_RESPONSE);
+	  if(num>0 &&
+	     (response==null ||  // cancel
+	      num!=response.length)){
+
+            if(response==null){  
+              // working around the bug in OpenSSH ;-<
+              buf.putInt(num);
+              for(int i=0; i<num; i++){
+                buf.putString(Util.empty);
+              }
+            }
+            else{
+              buf.putInt(0);
+            }
+
+	    if(response==null)
+	      cancel=true;
+	  }
+	  else{
+	    buf.putInt(num);
+	    for(int i=0; i<num; i++){
+	      buf.putString(response[i]);
+	    }
+	  }
+	  session.write(packet);
+          /*
+	  if(cancel)
+	    break;
+          */
+	  continue loop;
+	}
+	//throw new JSchException("USERAUTH fail ("+command+")");
+	return false;
+      }
+      if(cancel){
+	throw new JSchAuthCancelException("keyboard-interactive");
+	//break;
+      }
+    }
+    //return false;
+  }
+}
diff --git a/java/com/jcraft/jsch/UserAuthNone.java b/java/com/jcraft/jsch/UserAuthNone.java
new file mode 100644
index 0000000..b3b0b07
--- /dev/null
+++ b/java/com/jcraft/jsch/UserAuthNone.java
@@ -0,0 +1,129 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class UserAuthNone extends UserAuth{
+  private static final int SSH_MSG_SERVICE_ACCEPT=                  6;
+  private String methods=null;
+
+  public boolean start(Session session) throws Exception{
+    super.start(session);
+
+
+    // send
+    // byte      SSH_MSG_SERVICE_REQUEST(5)
+    // string    service name "ssh-userauth"
+    packet.reset();
+    buf.putByte((byte)Session.SSH_MSG_SERVICE_REQUEST);
+    buf.putString(Util.str2byte("ssh-userauth"));
+    session.write(packet);
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "SSH_MSG_SERVICE_REQUEST sent");
+    }
+
+    // receive
+    // byte      SSH_MSG_SERVICE_ACCEPT(6)
+    // string    service name
+    buf=session.read(buf);
+    int command=buf.getCommand();
+
+    boolean result=(command==SSH_MSG_SERVICE_ACCEPT);
+
+    if(JSch.getLogger().isEnabled(Logger.INFO)){
+      JSch.getLogger().log(Logger.INFO, 
+                           "SSH_MSG_SERVICE_ACCEPT received");
+    }
+    if(!result)
+      return false;
+
+    byte[] _username=null;
+    _username=Util.str2byte(username);
+
+    // send
+    // byte      SSH_MSG_USERAUTH_REQUEST(50)
+    // string    user name
+    // string    service name ("ssh-connection")
+    // string    "none"
+    packet.reset();
+    buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST);
+    buf.putString(_username);
+    buf.putString(Util.str2byte("ssh-connection"));
+    buf.putString(Util.str2byte("none"));
+    session.write(packet);
+
+    loop:
+    while(true){
+      buf=session.read(buf);
+      command=buf.getCommand()&0xff;
+
+      if(command==SSH_MSG_USERAUTH_SUCCESS){
+	return true;
+      }
+      if(command==SSH_MSG_USERAUTH_BANNER){
+	buf.getInt(); buf.getByte(); buf.getByte();
+	byte[] _message=buf.getString();
+	byte[] lang=buf.getString();
+	String message=Util.byte2str(_message);
+	if(userinfo!=null){
+          try{
+            userinfo.showMessage(message);
+          }
+          catch(RuntimeException ee){
+          }
+	}
+	continue loop;
+      }
+      if(command==SSH_MSG_USERAUTH_FAILURE){
+	buf.getInt(); buf.getByte(); buf.getByte(); 
+	byte[] foo=buf.getString();
+	int partial_success=buf.getByte();
+	methods=Util.byte2str(foo);
+//System.err.println("UserAuthNONE: "+methods+
+//		   " partial_success:"+(partial_success!=0));
+//	if(partial_success!=0){
+//	  throw new JSchPartialAuthException(new String(foo));
+//	}
+
+        break;
+      }
+      else{
+//      System.err.println("USERAUTH fail ("+command+")");
+	throw new JSchException("USERAUTH fail ("+command+")");
+      }
+    }
+   //throw new JSchException("USERAUTH fail");
+    return false;
+  }
+  String getMethods(){
+    return methods;
+  }
+}
diff --git a/java/com/jcraft/jsch/UserAuthPassword.java b/java/com/jcraft/jsch/UserAuthPassword.java
new file mode 100644
index 0000000..9b5837f
--- /dev/null
+++ b/java/com/jcraft/jsch/UserAuthPassword.java
@@ -0,0 +1,193 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class UserAuthPassword extends UserAuth{
+  private final int SSH_MSG_USERAUTH_PASSWD_CHANGEREQ=60;
+
+  public boolean start(Session session) throws Exception{
+    super.start(session);
+
+    byte[] password=session.password;
+    String dest=username+"@"+session.host;
+    if(session.port!=22){
+      dest+=(":"+session.port);
+    }
+
+    try{
+
+    while(true){
+
+      if(session.auth_failures >= session.max_auth_tries){
+        return false;
+      }
+
+      if(password==null){
+	if(userinfo==null){
+	  //throw new JSchException("USERAUTH fail");
+	  return false;
+	}
+	if(!userinfo.promptPassword("Password for "+dest)){
+	  throw new JSchAuthCancelException("password");
+	  //break;
+	}
+
+	String _password=userinfo.getPassword();
+	if(_password==null){
+	  throw new JSchAuthCancelException("password");
+	  //break;
+	}
+        password=Util.str2byte(_password);
+      }
+
+      byte[] _username=null;
+      _username=Util.str2byte(username);
+
+      // send
+      // byte      SSH_MSG_USERAUTH_REQUEST(50)
+      // string    user name
+      // string    service name ("ssh-connection")
+      // string    "password"
+      // boolen    FALSE
+      // string    plaintext password (ISO-10646 UTF-8)
+      packet.reset();
+      buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST);
+      buf.putString(_username);
+      buf.putString(Util.str2byte("ssh-connection"));
+      buf.putString(Util.str2byte("password"));
+      buf.putByte((byte)0);
+      buf.putString(password);
+      session.write(packet);
+
+      loop:
+      while(true){
+	buf=session.read(buf);
+        int command=buf.getCommand()&0xff;
+
+	if(command==SSH_MSG_USERAUTH_SUCCESS){
+	  return true;
+	}
+	if(command==SSH_MSG_USERAUTH_BANNER){
+	  buf.getInt(); buf.getByte(); buf.getByte();
+	  byte[] _message=buf.getString();
+	  byte[] lang=buf.getString();
+          String message=Util.byte2str(_message);
+	  if(userinfo!=null){
+	    userinfo.showMessage(message);
+	  }
+	  continue loop;
+	}
+	if(command==SSH_MSG_USERAUTH_PASSWD_CHANGEREQ){
+	  buf.getInt(); buf.getByte(); buf.getByte(); 
+	  byte[] instruction=buf.getString();
+	  byte[] tag=buf.getString();
+	  if(userinfo==null || 
+             !(userinfo instanceof UIKeyboardInteractive)){
+            if(userinfo!=null){
+              userinfo.showMessage("Password must be changed.");
+            }
+            return false;
+          }
+
+          UIKeyboardInteractive kbi=(UIKeyboardInteractive)userinfo;
+          String[] response;
+          String name="Password Change Required";
+          String[] prompt={"New Password: "};
+          boolean[] echo={false};
+          response=kbi.promptKeyboardInteractive(dest,
+                                                 name,
+                                                 Util.byte2str(instruction),
+                                                 prompt,
+                                                 echo);
+          if(response==null){
+            throw new JSchAuthCancelException("password");
+          }
+
+          byte[] newpassword=Util.str2byte(response[0]);
+
+          // send
+          // byte      SSH_MSG_USERAUTH_REQUEST(50)
+          // string    user name
+          // string    service name ("ssh-connection")
+          // string    "password"
+          // boolen    TRUE
+          // string    plaintext old password (ISO-10646 UTF-8)
+          // string    plaintext new password (ISO-10646 UTF-8)
+          packet.reset();
+          buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST);
+          buf.putString(_username);
+          buf.putString(Util.str2byte("ssh-connection"));
+          buf.putString(Util.str2byte("password"));
+          buf.putByte((byte)1);
+          buf.putString(password);
+          buf.putString(newpassword);
+          Util.bzero(newpassword);
+          response=null;
+          session.write(packet);
+	  continue loop;
+        }
+	if(command==SSH_MSG_USERAUTH_FAILURE){
+	  buf.getInt(); buf.getByte(); buf.getByte(); 
+	  byte[] foo=buf.getString();
+	  int partial_success=buf.getByte();
+	  //System.err.println(new String(foo)+
+	  //		 " partial_success:"+(partial_success!=0));
+	  if(partial_success!=0){
+	    throw new JSchPartialAuthException(Util.byte2str(foo));
+	  }
+          session.auth_failures++;
+	  break;
+	}
+	else{
+          //System.err.println("USERAUTH fail ("+buf.getCommand()+")");
+//	  throw new JSchException("USERAUTH fail ("+buf.getCommand()+")");
+	  return false;
+	}
+      }
+
+      if(password!=null){
+        Util.bzero(password);
+        password=null;
+      }
+
+    }
+
+    }
+    finally{
+      if(password!=null){
+        Util.bzero(password);
+        password=null;
+      }
+    }
+
+    //throw new JSchException("USERAUTH fail");
+    //return false;
+  }
+}
diff --git a/java/com/jcraft/jsch/UserAuthPublicKey.java b/java/com/jcraft/jsch/UserAuthPublicKey.java
new file mode 100644
index 0000000..64e8e72
--- /dev/null
+++ b/java/com/jcraft/jsch/UserAuthPublicKey.java
@@ -0,0 +1,228 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.util.Vector;
+
+class UserAuthPublicKey extends UserAuth{
+
+  public boolean start(Session session) throws Exception{
+    super.start(session);
+
+    Vector identities=session.jsch.getIdentityRepository().getIdentities();
+
+    byte[] passphrase=null;
+    byte[] _username=null;
+
+    int command;
+
+    synchronized(identities){
+      if(identities.size()<=0){
+        return false;
+      }
+
+      _username=Util.str2byte(username);
+
+      for(int i=0; i<identities.size(); i++){
+
+        if(session.auth_failures >= session.max_auth_tries){
+          return false;
+        }
+
+        Identity identity=(Identity)(identities.elementAt(i));
+        byte[] pubkeyblob=identity.getPublicKeyBlob();
+
+//System.err.println("UserAuthPublicKey: "+identity+" "+pubkeyblob);
+
+        if(pubkeyblob!=null){
+          // send
+          // byte      SSH_MSG_USERAUTH_REQUEST(50)
+          // string    user name
+          // string    service name ("ssh-connection")
+          // string    "publickey"
+          // boolen    FALSE
+          // string    plaintext password (ISO-10646 UTF-8)
+          packet.reset();
+          buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST);
+          buf.putString(_username);
+          buf.putString(Util.str2byte("ssh-connection"));
+          buf.putString(Util.str2byte("publickey"));
+          buf.putByte((byte)0);
+          buf.putString(Util.str2byte(identity.getAlgName()));
+          buf.putString(pubkeyblob);
+          session.write(packet);
+
+          loop1:
+          while(true){
+            buf=session.read(buf);
+            command=buf.getCommand()&0xff;
+
+            if(command==SSH_MSG_USERAUTH_PK_OK){
+              break;
+            }
+            else if(command==SSH_MSG_USERAUTH_FAILURE){
+              break;
+            }
+            else if(command==SSH_MSG_USERAUTH_BANNER){
+              buf.getInt(); buf.getByte(); buf.getByte();
+              byte[] _message=buf.getString();
+              byte[] lang=buf.getString();
+              String message=Util.byte2str(_message);
+              if(userinfo!=null){
+                userinfo.showMessage(message);
+              }
+              continue loop1;
+            }
+            else{
+	    //System.err.println("USERAUTH fail ("+command+")");
+	    //throw new JSchException("USERAUTH fail ("+command+")");
+              break;
+            }
+          }
+
+          if(command!=SSH_MSG_USERAUTH_PK_OK){
+            continue;
+          }
+        }
+
+//System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted());
+
+        int count=5;
+        while(true){
+          if((identity.isEncrypted() && passphrase==null)){
+            if(userinfo==null) throw new JSchException("USERAUTH fail");
+            if(identity.isEncrypted() &&
+               !userinfo.promptPassphrase("Passphrase for "+identity.getName())){
+              throw new JSchAuthCancelException("publickey");
+              //throw new JSchException("USERAUTH cancel");
+              //break;
+            }
+            String _passphrase=userinfo.getPassphrase();
+            if(_passphrase!=null){
+              passphrase=Util.str2byte(_passphrase);
+            }
+          }
+
+          if(!identity.isEncrypted() || passphrase!=null){
+            if(identity.setPassphrase(passphrase))
+              break;
+          }
+          Util.bzero(passphrase);
+          passphrase=null;
+          count--;
+          if(count==0)break;
+        }
+
+        Util.bzero(passphrase);
+        passphrase=null;
+//System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted());
+
+        if(identity.isEncrypted()) continue;
+        if(pubkeyblob==null) pubkeyblob=identity.getPublicKeyBlob();
+
+//System.err.println("UserAuthPublicKey: pubkeyblob="+pubkeyblob);
+
+        if(pubkeyblob==null) continue;
+
+      // send
+      // byte      SSH_MSG_USERAUTH_REQUEST(50)
+      // string    user name
+      // string    service name ("ssh-connection")
+      // string    "publickey"
+      // boolen    TRUE
+      // string    plaintext password (ISO-10646 UTF-8)
+        packet.reset();
+        buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST);
+        buf.putString(_username);
+        buf.putString(Util.str2byte("ssh-connection"));
+        buf.putString(Util.str2byte("publickey"));
+        buf.putByte((byte)1);
+        buf.putString(Util.str2byte(identity.getAlgName()));
+        buf.putString(pubkeyblob);
+
+//      byte[] tmp=new byte[buf.index-5];
+//      System.arraycopy(buf.buffer, 5, tmp, 0, tmp.length);
+//      buf.putString(signature);
+
+        byte[] sid=session.getSessionId();
+        int sidlen=sid.length;
+        byte[] tmp=new byte[4+sidlen+buf.index-5];
+        tmp[0]=(byte)(sidlen>>>24);
+        tmp[1]=(byte)(sidlen>>>16);
+        tmp[2]=(byte)(sidlen>>>8);
+        tmp[3]=(byte)(sidlen);
+        System.arraycopy(sid, 0, tmp, 4, sidlen);
+        System.arraycopy(buf.buffer, 5, tmp, 4+sidlen, buf.index-5);
+        byte[] signature=identity.getSignature(tmp);
+        if(signature==null){  // for example, too long key length.
+          break;
+        }
+        buf.putString(signature);
+        session.write(packet);
+
+        loop2:
+        while(true){
+          buf=session.read(buf);
+          command=buf.getCommand()&0xff;
+
+          if(command==SSH_MSG_USERAUTH_SUCCESS){
+            return true;
+          }
+          else if(command==SSH_MSG_USERAUTH_BANNER){
+            buf.getInt(); buf.getByte(); buf.getByte();
+            byte[] _message=buf.getString();
+            byte[] lang=buf.getString();
+            String message=Util.byte2str(_message);
+            if(userinfo!=null){
+              userinfo.showMessage(message);
+            }
+            continue loop2;
+          }
+          else if(command==SSH_MSG_USERAUTH_FAILURE){
+            buf.getInt(); buf.getByte(); buf.getByte(); 
+            byte[] foo=buf.getString();
+            int partial_success=buf.getByte();
+	  //System.err.println(new String(foo)+
+	  //                   " partial_success:"+(partial_success!=0));
+            if(partial_success!=0){
+              throw new JSchPartialAuthException(Util.byte2str(foo));
+            }
+            session.auth_failures++;
+            break;
+          }
+          //System.err.println("USERAUTH fail ("+command+")");
+          //throw new JSchException("USERAUTH fail ("+command+")");
+          break;
+        }
+      }
+    }
+    return false;
+  }
+}
diff --git a/java/com/jcraft/jsch/UserInfo.java b/java/com/jcraft/jsch/UserInfo.java
new file mode 100644
index 0000000..22552ed
--- /dev/null
+++ b/java/com/jcraft/jsch/UserInfo.java
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface UserInfo{
+  String getPassphrase();
+  String getPassword();
+  boolean promptPassword(String message);
+  boolean promptPassphrase(String message);
+  boolean promptYesNo(String message);
+  void showMessage(String message);
+}
diff --git a/java/com/jcraft/jsch/Util.java b/java/com/jcraft/jsch/Util.java
new file mode 100644
index 0000000..df51b2d
--- /dev/null
+++ b/java/com/jcraft/jsch/Util.java
@@ -0,0 +1,474 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+import java.net.Socket;
+
+class Util{
+
+  private static final byte[] b64 =Util.str2byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
+  private static byte val(byte foo){
+    if(foo == '=') return 0;
+    for(int j=0; j<b64.length; j++){
+      if(foo==b64[j]) return (byte)j;
+    }
+    return 0;
+  }
+  static byte[] fromBase64(byte[] buf, int start, int length){
+    byte[] foo=new byte[length];
+    int j=0;
+    for (int i=start;i<start+length;i+=4){
+      foo[j]=(byte)((val(buf[i])<<2)|((val(buf[i+1])&0x30)>>>4));
+      if(buf[i+2]==(byte)'='){ j++; break;}
+      foo[j+1]=(byte)(((val(buf[i+1])&0x0f)<<4)|((val(buf[i+2])&0x3c)>>>2));
+      if(buf[i+3]==(byte)'='){ j+=2; break;}
+      foo[j+2]=(byte)(((val(buf[i+2])&0x03)<<6)|(val(buf[i+3])&0x3f));
+      j+=3;
+    }
+    byte[] bar=new byte[j];
+    System.arraycopy(foo, 0, bar, 0, j);
+    return bar;
+  }
+  static byte[] toBase64(byte[] buf, int start, int length){
+
+    byte[] tmp=new byte[length*2];
+    int i,j,k;
+    
+    int foo=(length/3)*3+start;
+    i=0;
+    for(j=start; j<foo; j+=3){
+      k=(buf[j]>>>2)&0x3f;
+      tmp[i++]=b64[k];
+      k=(buf[j]&0x03)<<4|(buf[j+1]>>>4)&0x0f;
+      tmp[i++]=b64[k];
+      k=(buf[j+1]&0x0f)<<2|(buf[j+2]>>>6)&0x03;
+      tmp[i++]=b64[k];
+      k=buf[j+2]&0x3f;
+      tmp[i++]=b64[k];
+    }
+
+    foo=(start+length)-foo;
+    if(foo==1){
+      k=(buf[j]>>>2)&0x3f;
+      tmp[i++]=b64[k];
+      k=((buf[j]&0x03)<<4)&0x3f;
+      tmp[i++]=b64[k];
+      tmp[i++]=(byte)'=';
+      tmp[i++]=(byte)'=';
+    }
+    else if(foo==2){
+      k=(buf[j]>>>2)&0x3f;
+      tmp[i++]=b64[k];
+      k=(buf[j]&0x03)<<4|(buf[j+1]>>>4)&0x0f;
+      tmp[i++]=b64[k];
+      k=((buf[j+1]&0x0f)<<2)&0x3f;
+      tmp[i++]=b64[k];
+      tmp[i++]=(byte)'=';
+    }
+    byte[] bar=new byte[i];
+    System.arraycopy(tmp, 0, bar, 0, i);
+    return bar;
+
+//    return sun.misc.BASE64Encoder().encode(buf);
+  }
+
+  static String[] split(String foo, String split){
+    if(foo==null)
+      return null;
+    byte[] buf=Util.str2byte(foo);
+    java.util.Vector bar=new java.util.Vector();
+    int start=0;
+    int index;
+    while(true){
+      index=foo.indexOf(split, start);
+      if(index>=0){
+	bar.addElement(Util.byte2str(buf, start, index-start));
+	start=index+1;
+	continue;
+      }
+      bar.addElement(Util.byte2str(buf, start, buf.length-start));
+      break;
+    }
+    String[] result=new String[bar.size()];
+    for(int i=0; i<result.length; i++){
+      result[i]=(String)(bar.elementAt(i));
+    }
+    return result;
+  }
+  static boolean glob(byte[] pattern, byte[] name){
+    return glob0(pattern, 0, name, 0);
+  }
+  static private boolean glob0(byte[] pattern, int pattern_index,
+			      byte[] name, int name_index){
+    if(name.length>0 && name[0]=='.'){
+      if(pattern.length>0 && pattern[0]=='.'){
+        if(pattern.length==2 && pattern[1]=='*') return true;
+        return glob(pattern, pattern_index+1, name, name_index+1);
+      }
+      return false;
+    }
+    return glob(pattern, pattern_index, name, name_index);
+  }
+  static private boolean glob(byte[] pattern, int pattern_index,
+			      byte[] name, int name_index){
+    //System.err.println("glob: "+new String(pattern)+", "+pattern_index+" "+new String(name)+", "+name_index);
+
+    int patternlen=pattern.length;
+    if(patternlen==0)
+      return false;
+
+    int namelen=name.length;
+    int i=pattern_index;
+    int j=name_index;
+
+    while(i<patternlen && j<namelen){
+      if(pattern[i]=='\\'){
+	if(i+1==patternlen)
+	  return false;
+	i++;
+	if(pattern[i]!=name[j]) 
+          return false;
+        i+=skipUTF8Char(pattern[i]);
+        j+=skipUTF8Char(name[j]);
+	continue;
+      }
+
+      if(pattern[i]=='*'){
+        while(i<patternlen){
+          if(pattern[i]=='*'){
+            i++;
+            continue;
+          }
+          break;
+        }
+	if(patternlen==i)
+          return true;
+
+	byte foo=pattern[i];
+        if(foo=='?'){
+          while(j<namelen){
+	    if(glob(pattern, i, name, j)){
+	      return true;
+            }
+            j+=skipUTF8Char(name[j]);
+          }
+          return false;
+        }
+        else if(foo=='\\'){
+          if(i+1==patternlen)
+            return false;
+          i++;
+          foo=pattern[i];
+          while(j<namelen){
+            if(foo==name[j]){
+              if(glob(pattern, i+skipUTF8Char(foo),
+                      name, j+skipUTF8Char(name[j]))){
+                return true;
+              }
+            }
+            j+=skipUTF8Char(name[j]);
+          }
+          return false;
+        }
+
+	while(j<namelen){
+	  if(foo==name[j]){
+	    if(glob(pattern, i, name, j)){
+	      return true;
+	    }
+	  }
+          j+=skipUTF8Char(name[j]);
+	}
+	return false;
+      }
+
+      if(pattern[i]=='?'){
+        i++;
+        j+=skipUTF8Char(name[j]);
+	continue;
+      }
+
+      if(pattern[i]!=name[j])
+        return false;
+
+      i+=skipUTF8Char(pattern[i]);
+      j+=skipUTF8Char(name[j]);
+
+      if(!(j<namelen)){         // name is end
+        if(!(i<patternlen)){    // pattern is end
+	  return true;
+	}
+	if(pattern[i]=='*'){    
+          break;
+	}
+      }
+      continue;
+    }
+
+    if(i==patternlen && j==namelen) 
+      return true;
+
+    if(!(j<namelen) &&  // name is end
+       pattern[i]=='*'){
+      boolean ok=true;
+      while(i<patternlen){
+        if(pattern[i++]!='*'){
+          ok=false;
+          break;
+        }
+      }
+      return ok;
+    }
+
+    return false;
+  }
+
+  static String quote(String path){
+    byte[] _path=str2byte(path);
+    int count=0;
+    for(int i=0;i<_path.length; i++){
+      byte b=_path[i];
+      if(b=='\\' || b=='?' || b=='*')
+        count++;
+    }
+    if(count==0)
+      return path;
+    byte[] _path2=new byte[_path.length+count];
+    for(int i=0, j=0; i<_path.length; i++){
+      byte b=_path[i];
+      if(b=='\\' || b=='?' || b=='*'){
+        _path2[j++]='\\';
+      }
+      _path2[j++]=b;
+    }
+    return byte2str(_path2);
+  }
+
+  static String unquote(String path){
+    byte[] foo=str2byte(path);
+    byte[] bar=unquote(foo);
+    if(foo.length==bar.length)
+      return path;
+    return byte2str(bar);
+  }
+  static byte[] unquote(byte[] path){
+    int pathlen=path.length;
+    int i=0;
+    while(i<pathlen){
+      if(path[i]=='\\'){
+        if(i+1==pathlen)
+          break;
+        System.arraycopy(path, i+1, path, i, path.length-(i+1));
+        pathlen--;
+        i++;
+        continue;
+      }
+      i++;
+    }
+    if(pathlen==path.length)
+      return path;
+    byte[] foo=new byte[pathlen];
+    System.arraycopy(path, 0, foo, 0, pathlen);
+    return foo;
+  }
+
+  private static String[] chars={
+    "0","1","2","3","4","5","6","7","8","9", "a","b","c","d","e","f"
+  };
+  static String getFingerPrint(HASH hash, byte[] data){
+    try{
+      hash.init();
+      hash.update(data, 0, data.length);
+      byte[] foo=hash.digest();
+      StringBuffer sb=new StringBuffer();
+      int bar;
+      for(int i=0; i<foo.length;i++){
+        bar=foo[i]&0xff;
+        sb.append(chars[(bar>>>4)&0xf]);
+        sb.append(chars[(bar)&0xf]);
+        if(i+1<foo.length)
+          sb.append(":");
+      }
+      return sb.toString();
+    }
+    catch(Exception e){
+      return "???";
+    }
+  }
+  static boolean array_equals(byte[] foo, byte bar[]){
+    int i=foo.length;
+    if(i!=bar.length) return false;
+    for(int j=0; j<i; j++){ if(foo[j]!=bar[j]) return false; }
+    //try{while(true){i--; if(foo[i]!=bar[i])return false;}}catch(Exception e){}
+    return true;
+  }
+  static Socket createSocket(String host, int port, int timeout) throws JSchException{
+    Socket socket=null;
+    if(timeout==0){
+      try{
+        socket=new Socket(host, port);
+        return socket;
+      }
+      catch(Exception e){
+        String message=e.toString();
+        if(e instanceof Throwable)
+          throw new JSchException(message, (Throwable)e);
+        throw new JSchException(message);
+      }
+    }
+    final String _host=host;
+    final int _port=port;
+    final Socket[] sockp=new Socket[1];
+    final Exception[] ee=new Exception[1];
+    String message="";
+    Thread tmp=new Thread(new Runnable(){
+        public void run(){
+          sockp[0]=null;
+          try{
+            sockp[0]=new Socket(_host, _port);
+          }
+          catch(Exception e){
+            ee[0]=e;
+            if(sockp[0]!=null && sockp[0].isConnected()){
+              try{
+                sockp[0].close();
+              }
+              catch(Exception eee){}
+            }
+            sockp[0]=null;
+          }
+        }
+      });
+    tmp.setName("Opening Socket "+host);
+    tmp.start();
+    try{ 
+      tmp.join(timeout);
+      message="timeout: ";
+    }
+    catch(java.lang.InterruptedException eee){
+    }
+    if(sockp[0]!=null && sockp[0].isConnected()){
+      socket=sockp[0];
+    }
+    else{
+      message+="socket is not established";
+      if(ee[0]!=null){
+        message=ee[0].toString();
+      }
+      tmp.interrupt();
+      tmp=null;
+      throw new JSchException(message);
+    }
+    return socket;
+  } 
+
+  static byte[] str2byte(String str, String encoding){
+    if(str==null) 
+      return null;
+    try{ return str.getBytes(encoding); }
+    catch(java.io.UnsupportedEncodingException e){
+      return str.getBytes();
+    }
+  }
+
+  static byte[] str2byte(String str){
+    return str2byte(str, "UTF-8");
+  }
+
+  static String byte2str(byte[] str, String encoding){
+    return byte2str(str, 0, str.length, encoding);
+  }
+
+  static String byte2str(byte[] str, int s, int l, String encoding){
+    try{ return new String(str, s, l, encoding); }
+    catch(java.io.UnsupportedEncodingException e){
+      return new String(str, s, l);
+    }
+  }
+
+  static String byte2str(byte[] str){
+    return byte2str(str, 0, str.length, "UTF-8");
+  }
+
+  static String byte2str(byte[] str, int s, int l){
+    return byte2str(str, s, l, "UTF-8");
+  }
+
+  static final byte[] empty = str2byte("");
+
+  /*
+  static byte[] char2byte(char[] foo){
+    int len=0;
+    for(int i=0; i<foo.length; i++){
+      if((foo[i]&0xff00)==0) len++;
+      else len+=2;
+    }
+    byte[] bar=new byte[len];
+    for(int i=0, j=0; i<foo.length; i++){
+      if((foo[i]&0xff00)==0){
+        bar[j++]=(byte)foo[i];
+      }
+      else{
+        bar[j++]=(byte)(foo[i]>>>8);
+        bar[j++]=(byte)foo[i];
+      }
+    }
+    return bar;
+  }
+  */
+  static void bzero(byte[] foo){
+    if(foo==null)
+      return;
+    for(int i=0; i<foo.length; i++)
+      foo[i]=0;
+  }
+
+  static String diffString(String str, String[] not_available){
+    String[] stra=Util.split(str, ",");
+    String result=null;
+    loop:
+    for(int i=0; i<stra.length; i++){
+      for(int j=0; j<not_available.length; j++){
+        if(stra[i].equals(not_available[j])){
+          continue loop;
+        }
+      }
+      if(result==null){ result=stra[i]; }
+      else{ result=result+","+stra[i]; }
+    }
+    return result;
+  }
+
+  private static int skipUTF8Char(byte b){
+    if((byte)(b&0x80)==0) return 1;
+    if((byte)(b&0xe0)==(byte)0xc0) return 2;
+    if((byte)(b&0xf0)==(byte)0xe0) return 3;
+    return 1;
+  }
+}
diff --git a/java/com/jcraft/jsch/jce/AES128CBC.java b/java/com/jcraft/jsch/jce/AES128CBC.java
new file mode 100644
index 0000000..8c2b43b
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/AES128CBC.java
@@ -0,0 +1,73 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.spec.*;
+
+public class AES128CBC implements Cipher{
+  private static final int ivsize=16;
+  private static final int bsize=16;
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    String pad="NoPadding";      
+    byte[] tmp;
+    if(iv.length>ivsize){
+      tmp=new byte[ivsize];
+      System.arraycopy(iv, 0, tmp, 0, tmp.length);
+      iv=tmp;
+    }
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+
+    try{
+      SecretKeySpec keyspec=new SecretKeySpec(key, "AES");
+      cipher=javax.crypto.Cipher.getInstance("AES/CBC/"+pad);
+      cipher.init((mode==ENCRYPT_MODE?
+                   javax.crypto.Cipher.ENCRYPT_MODE:
+                   javax.crypto.Cipher.DECRYPT_MODE),
+                  keyspec, new IvParameterSpec(iv));
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+
+  public boolean isCBC(){return true; }
+}
diff --git a/java/com/jcraft/jsch/jce/AES128CTR.java b/java/com/jcraft/jsch/jce/AES128CTR.java
new file mode 100644
index 0000000..470a896
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/AES128CTR.java
@@ -0,0 +1,73 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.spec.*;
+
+public class AES128CTR implements Cipher{
+  private static final int ivsize=16;
+  private static final int bsize=16;
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    String pad="NoPadding";      
+    byte[] tmp;
+    if(iv.length>ivsize){
+      tmp=new byte[ivsize];
+      System.arraycopy(iv, 0, tmp, 0, tmp.length);
+      iv=tmp;
+    }
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+
+    try{
+      SecretKeySpec keyspec=new SecretKeySpec(key, "AES");
+      cipher=javax.crypto.Cipher.getInstance("AES/CTR/"+pad);
+      cipher.init((mode==ENCRYPT_MODE?
+                   javax.crypto.Cipher.ENCRYPT_MODE:
+                   javax.crypto.Cipher.DECRYPT_MODE),
+                  keyspec, new IvParameterSpec(iv));
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+
+  public boolean isCBC(){return false; }
+}
diff --git a/java/com/jcraft/jsch/jce/AES192CBC.java b/java/com/jcraft/jsch/jce/AES192CBC.java
new file mode 100644
index 0000000..615d494
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/AES192CBC.java
@@ -0,0 +1,71 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.spec.*;
+
+public class AES192CBC implements Cipher{
+  private static final int ivsize=16;
+  private static final int bsize=24;
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    String pad="NoPadding";      
+    byte[] tmp;
+    if(iv.length>ivsize){
+      tmp=new byte[ivsize];
+      System.arraycopy(iv, 0, tmp, 0, tmp.length);
+      iv=tmp;
+    }
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+    try{
+      SecretKeySpec keyspec=new SecretKeySpec(key, "AES");
+      cipher=javax.crypto.Cipher.getInstance("AES/CBC/"+pad);
+      cipher.init((mode==ENCRYPT_MODE?
+                   javax.crypto.Cipher.ENCRYPT_MODE:
+                   javax.crypto.Cipher.DECRYPT_MODE),
+                  keyspec, new IvParameterSpec(iv));
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+  public boolean isCBC(){return true; }
+}
diff --git a/java/com/jcraft/jsch/jce/AES192CTR.java b/java/com/jcraft/jsch/jce/AES192CTR.java
new file mode 100644
index 0000000..74090bf
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/AES192CTR.java
@@ -0,0 +1,71 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.spec.*;
+
+public class AES192CTR implements Cipher{
+  private static final int ivsize=16;
+  private static final int bsize=24;
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    String pad="NoPadding";      
+    byte[] tmp;
+    if(iv.length>ivsize){
+      tmp=new byte[ivsize];
+      System.arraycopy(iv, 0, tmp, 0, tmp.length);
+      iv=tmp;
+    }
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+    try{
+      SecretKeySpec keyspec=new SecretKeySpec(key, "AES");
+      cipher=javax.crypto.Cipher.getInstance("AES/CTR/"+pad);
+      cipher.init((mode==ENCRYPT_MODE?
+                   javax.crypto.Cipher.ENCRYPT_MODE:
+                   javax.crypto.Cipher.DECRYPT_MODE),
+                  keyspec, new IvParameterSpec(iv));
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+  public boolean isCBC(){return false; }
+}
diff --git a/java/com/jcraft/jsch/jce/AES256CBC.java b/java/com/jcraft/jsch/jce/AES256CBC.java
new file mode 100644
index 0000000..9018a20
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/AES256CBC.java
@@ -0,0 +1,71 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.spec.*;
+
+public class AES256CBC implements Cipher{
+  private static final int ivsize=16;
+  private static final int bsize=32;
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    String pad="NoPadding";      
+    byte[] tmp;
+    if(iv.length>ivsize){
+      tmp=new byte[ivsize];
+      System.arraycopy(iv, 0, tmp, 0, tmp.length);
+      iv=tmp;
+    }
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+    try{
+      SecretKeySpec keyspec=new SecretKeySpec(key, "AES");
+      cipher=javax.crypto.Cipher.getInstance("AES/CBC/"+pad);
+      cipher.init((mode==ENCRYPT_MODE?
+                   javax.crypto.Cipher.ENCRYPT_MODE:
+                   javax.crypto.Cipher.DECRYPT_MODE),
+                  keyspec, new IvParameterSpec(iv));
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+  public boolean isCBC(){return true; }
+}
diff --git a/java/com/jcraft/jsch/jce/AES256CTR.java b/java/com/jcraft/jsch/jce/AES256CTR.java
new file mode 100644
index 0000000..b7472e9
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/AES256CTR.java
@@ -0,0 +1,71 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.spec.*;
+
+public class AES256CTR implements Cipher{
+  private static final int ivsize=16;
+  private static final int bsize=32;
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    String pad="NoPadding";      
+    byte[] tmp;
+    if(iv.length>ivsize){
+      tmp=new byte[ivsize];
+      System.arraycopy(iv, 0, tmp, 0, tmp.length);
+      iv=tmp;
+    }
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+    try{
+      SecretKeySpec keyspec=new SecretKeySpec(key, "AES");
+      cipher=javax.crypto.Cipher.getInstance("AES/CTR/"+pad);
+      cipher.init((mode==ENCRYPT_MODE?
+                   javax.crypto.Cipher.ENCRYPT_MODE:
+                   javax.crypto.Cipher.DECRYPT_MODE),
+                  keyspec, new IvParameterSpec(iv));
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+  public boolean isCBC(){return false; }
+}
diff --git a/java/com/jcraft/jsch/jce/ARCFOUR.java b/java/com/jcraft/jsch/jce/ARCFOUR.java
new file mode 100644
index 0000000..9f6537c
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/ARCFOUR.java
@@ -0,0 +1,68 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class ARCFOUR implements Cipher{
+  private static final int ivsize=8;
+  private static final int bsize=16;
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    String pad="NoPadding";      
+    byte[] tmp;
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+
+    try{
+      cipher=javax.crypto.Cipher.getInstance("RC4");
+      SecretKeySpec _key = new SecretKeySpec(key, "RC4");
+      cipher.init((mode==ENCRYPT_MODE?
+		   javax.crypto.Cipher.ENCRYPT_MODE:
+		   javax.crypto.Cipher.DECRYPT_MODE),
+		  _key);
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+  public boolean isCBC(){return false; }
+}
diff --git a/java/com/jcraft/jsch/jce/ARCFOUR128.java b/java/com/jcraft/jsch/jce/ARCFOUR128.java
new file mode 100644
index 0000000..d8b4613
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/ARCFOUR128.java
@@ -0,0 +1,71 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class ARCFOUR128 implements Cipher{
+  private static final int ivsize=8;
+  private static final int bsize=16;
+  private static final int skip=1536; 
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    byte[] tmp;
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+    try{
+      cipher=javax.crypto.Cipher.getInstance("RC4");
+      SecretKeySpec _key = new SecretKeySpec(key, "RC4");
+      cipher.init((mode==ENCRYPT_MODE?
+		   javax.crypto.Cipher.ENCRYPT_MODE:
+		   javax.crypto.Cipher.DECRYPT_MODE),
+		  _key);
+      byte[] foo=new byte[1];
+      for(int i=0; i<skip; i++){
+        cipher.update(foo, 0, 1, foo, 0);
+      }
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+  public boolean isCBC(){return false; }
+}
diff --git a/java/com/jcraft/jsch/jce/ARCFOUR256.java b/java/com/jcraft/jsch/jce/ARCFOUR256.java
new file mode 100644
index 0000000..a4a9f69
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/ARCFOUR256.java
@@ -0,0 +1,71 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class ARCFOUR256 implements Cipher{
+  private static final int ivsize=8;
+  private static final int bsize=32;
+  private static final int skip=1536; 
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    byte[] tmp;
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+    try{
+      cipher=javax.crypto.Cipher.getInstance("RC4");
+      SecretKeySpec _key = new SecretKeySpec(key, "RC4");
+      cipher.init((mode==ENCRYPT_MODE?
+		   javax.crypto.Cipher.ENCRYPT_MODE:
+		   javax.crypto.Cipher.DECRYPT_MODE),
+		  _key);
+      byte[] foo=new byte[1];
+      for(int i=0; i<skip; i++){
+        cipher.update(foo, 0, 1, foo, 0);
+      }
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+  public boolean isCBC(){return false; }
+}
diff --git a/java/com/jcraft/jsch/jce/BlowfishCBC.java b/java/com/jcraft/jsch/jce/BlowfishCBC.java
new file mode 100644
index 0000000..3853ab7
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/BlowfishCBC.java
@@ -0,0 +1,71 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.spec.*;
+
+public class BlowfishCBC implements Cipher{
+  private static final int ivsize=8;
+  private static final int bsize=16;
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    String pad="NoPadding";      
+//  if(padding) pad="PKCS5Padding";
+    byte[] tmp;
+    if(iv.length>ivsize){
+      tmp=new byte[ivsize];
+      System.arraycopy(iv, 0, tmp, 0, tmp.length);
+      iv=tmp;
+    }
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+    try{
+      SecretKeySpec skeySpec = new SecretKeySpec(key, "Blowfish");
+      cipher=javax.crypto.Cipher.getInstance("Blowfish/CBC/"+pad);
+      cipher.init((mode==ENCRYPT_MODE?
+		   javax.crypto.Cipher.ENCRYPT_MODE:
+		   javax.crypto.Cipher.DECRYPT_MODE),
+		  skeySpec, new IvParameterSpec(iv));
+    }
+    catch(Exception e){
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+  public boolean isCBC(){return true; }
+}
diff --git a/java/com/jcraft/jsch/jce/DH.java b/java/com/jcraft/jsch/jce/DH.java
new file mode 100644
index 0000000..da03c1d
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/DH.java
@@ -0,0 +1,91 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import java.math.BigInteger;
+import java.security.*;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class DH implements com.jcraft.jsch.DH{
+  BigInteger p;
+  BigInteger g;
+  BigInteger e;  // my public key
+  byte[] e_array;
+  BigInteger f;  // your public key
+  BigInteger K;  // shared secret key
+  byte[] K_array;
+
+  private KeyPairGenerator myKpairGen;
+  private KeyAgreement myKeyAgree;
+  public void init() throws Exception{
+    myKpairGen=KeyPairGenerator.getInstance("DH");
+//    myKpairGen=KeyPairGenerator.getInstance("DiffieHellman");
+    myKeyAgree=KeyAgreement.getInstance("DH");
+//    myKeyAgree=KeyAgreement.getInstance("DiffieHellman");
+  }
+  public byte[] getE() throws Exception{
+    if(e==null){
+      DHParameterSpec dhSkipParamSpec=new DHParameterSpec(p, g);
+      myKpairGen.initialize(dhSkipParamSpec);
+      KeyPair myKpair=myKpairGen.generateKeyPair();
+      myKeyAgree.init(myKpair.getPrivate());
+//    BigInteger x=((javax.crypto.interfaces.DHPrivateKey)(myKpair.getPrivate())).getX();
+      byte[] myPubKeyEnc=myKpair.getPublic().getEncoded();
+      e=((javax.crypto.interfaces.DHPublicKey)(myKpair.getPublic())).getY();
+      e_array=e.toByteArray();
+    }
+    return e_array;
+  }
+  public byte[] getK() throws Exception{
+    if(K==null){
+      KeyFactory myKeyFac=KeyFactory.getInstance("DH");
+      DHPublicKeySpec keySpec=new DHPublicKeySpec(f, p, g);
+      PublicKey yourPubKey=myKeyFac.generatePublic(keySpec);
+      myKeyAgree.doPhase(yourPubKey, true);
+      byte[] mySharedSecret=myKeyAgree.generateSecret();
+      K=new BigInteger(mySharedSecret);
+      K_array=K.toByteArray();
+
+//System.err.println("K.signum(): "+K.signum()+
+//		   " "+Integer.toHexString(mySharedSecret[0]&0xff)+
+//		   " "+Integer.toHexString(K_array[0]&0xff));
+
+      K_array=mySharedSecret;
+    }
+    return K_array;
+  }
+  public void setP(byte[] p){ setP(new BigInteger(p)); }
+  public void setG(byte[] g){ setG(new BigInteger(g)); }
+  public void setF(byte[] f){ setF(new BigInteger(f)); }
+  void setP(BigInteger p){this.p=p;}
+  void setG(BigInteger g){this.g=g;}
+  void setF(BigInteger f){this.f=f;}
+}
diff --git a/java/com/jcraft/jsch/jce/HMACMD5.java b/java/com/jcraft/jsch/jce/HMACMD5.java
new file mode 100644
index 0000000..def3747
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/HMACMD5.java
@@ -0,0 +1,75 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.MAC;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class HMACMD5 implements MAC{
+  private static final String name="hmac-md5";
+  private static final int BSIZE=16;
+  private Mac mac;
+  public int getBlockSize(){return BSIZE;};
+  public void init(byte[] key) throws Exception{
+    if(key.length>BSIZE){
+      byte[] tmp=new byte[BSIZE];
+      System.arraycopy(key, 0, tmp, 0, BSIZE);	  
+      key=tmp;
+    }
+
+    SecretKeySpec skey=new SecretKeySpec(key, "HmacMD5");
+    mac=Mac.getInstance("HmacMD5");
+    mac.init(skey);
+  } 
+
+  private final byte[] tmp=new byte[4];
+  public void update(int i){
+    tmp[0]=(byte)(i>>>24);
+    tmp[1]=(byte)(i>>>16);
+    tmp[2]=(byte)(i>>>8);
+    tmp[3]=(byte)i;
+    update(tmp, 0, 4);
+  }
+  public void update(byte foo[], int s, int l){
+    mac.update(foo, s, l);      
+  }
+  public void doFinal(byte[] buf, int offset){
+    try{
+      mac.doFinal(buf, offset);
+    }
+    catch(ShortBufferException e){
+    }
+  }
+
+  public String getName(){
+    return name;
+  }
+}
diff --git a/java/com/jcraft/jsch/jce/HMACMD596.java b/java/com/jcraft/jsch/jce/HMACMD596.java
new file mode 100644
index 0000000..a296a5d
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/HMACMD596.java
@@ -0,0 +1,77 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.MAC;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class HMACMD596 implements MAC{
+  private static final String name="hmac-md5-96";
+  private static final int bsize=12;
+  private Mac mac;
+  public int getBlockSize(){return bsize;};
+  public void init(byte[] key) throws Exception{
+    if(key.length>16){
+      byte[] tmp=new byte[16];
+      System.arraycopy(key, 0, tmp, 0, 16);	  
+      key=tmp;
+    }
+    SecretKeySpec skey=new SecretKeySpec(key, "HmacMD5");
+    mac=Mac.getInstance("HmacMD5");
+    mac.init(skey);
+  } 
+  private final byte[] tmp=new byte[4];
+  public void update(int i){
+    tmp[0]=(byte)(i>>>24);
+    tmp[1]=(byte)(i>>>16);
+    tmp[2]=(byte)(i>>>8);
+    tmp[3]=(byte)i;
+    update(tmp, 0, 4);
+  }
+
+  public void update(byte foo[], int s, int l){
+    mac.update(foo, s, l);      
+  }
+
+  private final byte[] _buf16=new byte[16];
+  public void doFinal(byte[] buf, int offset){
+    try{
+      mac.doFinal(_buf16, 0);
+    }
+    catch(ShortBufferException e){
+    }
+    System.arraycopy(_buf16, 0, buf, offset, 12);
+  }
+
+  public String getName(){
+    return name;
+  }
+}
diff --git a/java/com/jcraft/jsch/jce/HMACSHA1.java b/java/com/jcraft/jsch/jce/HMACSHA1.java
new file mode 100644
index 0000000..13feaab
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/HMACSHA1.java
@@ -0,0 +1,75 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.MAC;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class HMACSHA1 implements MAC{
+  private static final String name="hmac-sha1";
+  private static final int bsize=20;
+  private Mac mac;
+  public int getBlockSize(){return bsize;};
+  public void init(byte[] key) throws Exception{
+    if(key.length>bsize){
+      byte[] tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, bsize);	  
+      key=tmp;
+    }
+    SecretKeySpec skey=new SecretKeySpec(key, "HmacSHA1");
+    mac=Mac.getInstance("HmacSHA1");
+    mac.init(skey);
+  } 
+  private final byte[] tmp=new byte[4];
+  public void update(int i){
+    tmp[0]=(byte)(i>>>24);
+    tmp[1]=(byte)(i>>>16);
+    tmp[2]=(byte)(i>>>8);
+    tmp[3]=(byte)i;
+    update(tmp, 0, 4);
+  }
+
+  public void update(byte foo[], int s, int l){
+    mac.update(foo, s, l);      
+  }
+
+  public void doFinal(byte[] buf, int offset){
+    try{
+      mac.doFinal(buf, offset);
+    }
+    catch(ShortBufferException e){
+    }
+  }
+
+  public String getName(){
+    return name;
+  }
+}
diff --git a/java/com/jcraft/jsch/jce/HMACSHA196.java b/java/com/jcraft/jsch/jce/HMACSHA196.java
new file mode 100644
index 0000000..e879176
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/HMACSHA196.java
@@ -0,0 +1,76 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.MAC;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class HMACSHA196 implements MAC{
+  private static final String name="hmac-sha1-96";
+  private static final int bsize=12;
+  private Mac mac;
+  public int getBlockSize(){return bsize;};
+  public void init(byte[] key) throws Exception{
+    if(key.length>20){
+      byte[] tmp=new byte[20];
+      System.arraycopy(key, 0, tmp, 0, 20);	  
+      key=tmp;
+    }
+    SecretKeySpec skey=new SecretKeySpec(key, "HmacSHA1");
+    mac=Mac.getInstance("HmacSHA1");
+    mac.init(skey);
+  } 
+  private final byte[] tmp=new byte[4];
+  public void update(int i){
+    tmp[0]=(byte)(i>>>24);
+    tmp[1]=(byte)(i>>>16);
+    tmp[2]=(byte)(i>>>8);
+    tmp[3]=(byte)i;
+    update(tmp, 0, 4);
+  }
+  public void update(byte foo[], int s, int l){
+    mac.update(foo, s, l);      
+  }
+
+  private final byte[] _buf20=new byte[20];
+  public void doFinal(byte[] buf, int offset){
+    try{
+      mac.doFinal(_buf20, 0);
+    }
+    catch(ShortBufferException e){
+    }
+    System.arraycopy(_buf20, 0, buf, offset, 12);
+  }
+
+  public String getName(){
+    return name;
+  }
+}
diff --git a/java/com/jcraft/jsch/jce/KeyPairGenDSA.java b/java/com/jcraft/jsch/jce/KeyPairGenDSA.java
new file mode 100644
index 0000000..67ad54e
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/KeyPairGenDSA.java
@@ -0,0 +1,62 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import java.security.*;
+import java.security.interfaces.*;
+
+public class KeyPairGenDSA implements com.jcraft.jsch.KeyPairGenDSA{
+  byte[] x;  // private
+  byte[] y;  // public
+  byte[] p;
+  byte[] q;
+  byte[] g;
+
+  public void init(int key_size) throws Exception{
+    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
+    keyGen.initialize(key_size, new SecureRandom());
+    KeyPair pair = keyGen.generateKeyPair();
+    PublicKey pubKey=pair.getPublic();
+    PrivateKey prvKey=pair.getPrivate();
+
+    x=((DSAPrivateKey)prvKey).getX().toByteArray();
+    y=((DSAPublicKey)pubKey).getY().toByteArray();
+
+    DSAParams params=((DSAKey)prvKey).getParams();
+    p=params.getP().toByteArray();
+    q=params.getQ().toByteArray();
+    g=params.getG().toByteArray();
+  }
+  public byte[] getX(){return x;}
+  public byte[] getY(){return y;}
+  public byte[] getP(){return p;}
+  public byte[] getQ(){return q;}
+  public byte[] getG(){return g;}
+}
diff --git a/java/com/jcraft/jsch/jce/KeyPairGenRSA.java b/java/com/jcraft/jsch/jce/KeyPairGenRSA.java
new file mode 100644
index 0000000..543f0f7
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/KeyPairGenRSA.java
@@ -0,0 +1,72 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import java.security.*;
+import java.security.interfaces.*;
+
+public class KeyPairGenRSA implements com.jcraft.jsch.KeyPairGenRSA{
+  byte[] d;  // private
+  byte[] e;  // public
+  byte[] n;
+
+  byte[] c; //  coefficient
+  byte[] ep; // exponent p
+  byte[] eq; // exponent q
+  byte[] p;  // prime p
+  byte[] q;  // prime q
+
+  public void init(int key_size) throws Exception{
+    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+    keyGen.initialize(key_size, new SecureRandom());
+    KeyPair pair = keyGen.generateKeyPair();
+
+    PublicKey pubKey=pair.getPublic();
+    PrivateKey prvKey=pair.getPrivate();
+
+    d=((RSAPrivateKey)prvKey).getPrivateExponent().toByteArray();
+    e=((RSAPublicKey)pubKey).getPublicExponent().toByteArray();
+    n=((RSAPrivateKey)prvKey).getModulus().toByteArray();
+
+    c=((RSAPrivateCrtKey)prvKey).getCrtCoefficient().toByteArray();
+    ep=((RSAPrivateCrtKey)prvKey).getPrimeExponentP().toByteArray();
+    eq=((RSAPrivateCrtKey)prvKey).getPrimeExponentQ().toByteArray();
+    p=((RSAPrivateCrtKey)prvKey).getPrimeP().toByteArray();
+    q=((RSAPrivateCrtKey)prvKey).getPrimeQ().toByteArray();
+  }
+  public byte[] getD(){return d;}
+  public byte[] getE(){return e;}
+  public byte[] getN(){return n;}
+  public byte[] getC(){return c;}
+  public byte[] getEP(){return ep;}
+  public byte[] getEQ(){return eq;}
+  public byte[] getP(){return p;}
+  public byte[] getQ(){return q;}
+}
diff --git a/java/com/jcraft/jsch/jce/MD5.java b/java/com/jcraft/jsch/jce/MD5.java
new file mode 100644
index 0000000..538ae34
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/MD5.java
@@ -0,0 +1,51 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.HASH;
+
+import java.security.*;
+
+public class MD5 implements HASH{
+  MessageDigest md;
+  public int getBlockSize(){return 16;}
+  public void init() throws Exception{
+    try{ md=MessageDigest.getInstance("MD5"); }
+    catch(Exception e){
+      System.err.println(e);
+    }
+  }
+  public void update(byte[] foo, int start, int len) throws Exception{
+    md.update(foo, start, len);
+  }
+  public byte[] digest() throws Exception{
+    return md.digest();
+  }
+}
diff --git a/java/com/jcraft/jsch/jce/Random.java b/java/com/jcraft/jsch/jce/Random.java
new file mode 100644
index 0000000..8668a01
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/Random.java
@@ -0,0 +1,81 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import java.security.SecureRandom;
+
+public class Random implements com.jcraft.jsch.Random{
+  private byte[] tmp=new byte[16];
+  private SecureRandom random=null;
+  public Random(){
+
+    // We hope that 'new SecureRandom()' will use NativePRNG algorithm
+    // on Sun's Java5 for GNU/Linux and Solaris.
+    // It seems NativePRNG refers to /dev/urandom and it must not be blocked,
+    // but NativePRNG is slower than SHA1PRNG ;-<
+    // TIPS: By adding option '-Djava.security.egd=file:/dev/./urandom'
+    //       SHA1PRNG will be used instead of NativePRNG.
+    // On MacOSX, 'new SecureRandom()' will use NativePRNG algorithm and
+    // it is also slower than SHA1PRNG.
+    // On Windows, 'new SecureRandom()' will use SHA1PRNG algorithm.
+    random=new SecureRandom();
+
+    /*
+    try{ 
+      random=SecureRandom.getInstance("SHA1PRNG"); 
+      return;
+    }
+    catch(java.security.NoSuchAlgorithmException e){ 
+      // System.err.println(e); 
+    }
+
+    // The following code is for IBM's JCE
+    try{ 
+      random=SecureRandom.getInstance("IBMSecureRandom"); 
+      return;
+    }
+    catch(java.security.NoSuchAlgorithmException ee){ 
+      //System.err.println(ee); 
+    }
+    */
+  }
+  public void fill(byte[] foo, int start, int len){
+    /*
+    // This case will not become true in our usage.
+    if(start==0 && foo.length==len){
+      random.nextBytes(foo);
+      return;
+    }
+    */
+    if(len>tmp.length){ tmp=new byte[len]; }
+    random.nextBytes(tmp);
+    System.arraycopy(tmp, 0, foo, start, len);
+  }
+}
diff --git a/java/com/jcraft/jsch/jce/SHA1.java b/java/com/jcraft/jsch/jce/SHA1.java
new file mode 100644
index 0000000..08fce4b
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/SHA1.java
@@ -0,0 +1,51 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.HASH;
+
+import java.security.*;
+
+public class SHA1 implements HASH{
+  MessageDigest md;
+  public int getBlockSize(){return 20;}
+  public void init() throws Exception{
+    try{ md=MessageDigest.getInstance("SHA-1"); }
+    catch(Exception e){
+      System.err.println(e);
+    }
+  }
+  public void update(byte[] foo, int start, int len) throws Exception{
+    md.update(foo, start, len);
+  }
+  public byte[] digest() throws Exception{
+    return md.digest();
+  }
+}
diff --git a/java/com/jcraft/jsch/jce/SignatureDSA.java b/java/com/jcraft/jsch/jce/SignatureDSA.java
new file mode 100644
index 0000000..262dfc5
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/SignatureDSA.java
@@ -0,0 +1,147 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import java.math.BigInteger;
+import java.security.*;
+import java.security.spec.*;
+
+public class SignatureDSA implements com.jcraft.jsch.SignatureDSA{
+
+  java.security.Signature signature;
+  KeyFactory keyFactory;
+
+  public void init() throws Exception{
+    signature=java.security.Signature.getInstance("SHA1withDSA");
+    keyFactory=KeyFactory.getInstance("DSA");
+  }     
+  public void setPubKey(byte[] y, byte[] p, byte[] q, byte[] g) throws Exception{
+    DSAPublicKeySpec dsaPubKeySpec = 
+	new DSAPublicKeySpec(new BigInteger(y),
+			     new BigInteger(p),
+			     new BigInteger(q),
+			     new BigInteger(g));
+    PublicKey pubKey=keyFactory.generatePublic(dsaPubKeySpec);
+    signature.initVerify(pubKey);
+  }
+  public void setPrvKey(byte[] x, byte[] p, byte[] q, byte[] g) throws Exception{
+    DSAPrivateKeySpec dsaPrivKeySpec = 
+	new DSAPrivateKeySpec(new BigInteger(x),
+			      new BigInteger(p),
+			      new BigInteger(q),
+			      new BigInteger(g));
+    PrivateKey prvKey = keyFactory.generatePrivate(dsaPrivKeySpec);
+    signature.initSign(prvKey);
+  }
+  public byte[] sign() throws Exception{
+    byte[] sig=signature.sign();      
+/*
+System.err.print("sign["+sig.length+"] ");
+for(int i=0; i<sig.length;i++){
+System.err.print(Integer.toHexString(sig[i]&0xff)+":");
+}
+System.err.println("");
+*/
+    // sig is in ASN.1
+    // SEQUENCE::={ r INTEGER, s INTEGER }
+    int len=0;	
+    int index=3;
+    len=sig[index++]&0xff;
+//System.err.println("! len="+len);
+    byte[] r=new byte[len];
+    System.arraycopy(sig, index, r, 0, r.length);
+    index=index+len+1;
+    len=sig[index++]&0xff;
+//System.err.println("!! len="+len);
+    byte[] s=new byte[len];
+    System.arraycopy(sig, index, s, 0, s.length);
+
+    byte[] result=new byte[40];
+
+    // result must be 40 bytes, but length of r and s may not be 20 bytes  
+
+    System.arraycopy(r, (r.length>20)?1:0,
+		     result, (r.length>20)?0:20-r.length,
+		     (r.length>20)?20:r.length);
+    System.arraycopy(s, (s.length>20)?1:0,
+		     result, (s.length>20)?20:40-s.length,
+		     (s.length>20)?20:s.length);
+ 
+//  System.arraycopy(sig, (sig[3]==20?4:5), result, 0, 20);
+//  System.arraycopy(sig, sig.length-20, result, 20, 20);
+
+    return result;
+  }
+  public void update(byte[] foo) throws Exception{
+   signature.update(foo);
+  }
+  public boolean verify(byte[] sig) throws Exception{
+    int i=0;
+    int j=0;
+    byte[] tmp;
+
+    if(sig[0]==0 && sig[1]==0 && sig[2]==0){
+    j=((sig[i++]<<24)&0xff000000)|((sig[i++]<<16)&0x00ff0000)|
+	((sig[i++]<<8)&0x0000ff00)|((sig[i++])&0x000000ff);
+    i+=j;
+    j=((sig[i++]<<24)&0xff000000)|((sig[i++]<<16)&0x00ff0000)|
+	((sig[i++]<<8)&0x0000ff00)|((sig[i++])&0x000000ff);
+    tmp=new byte[j]; 
+    System.arraycopy(sig, i, tmp, 0, j); sig=tmp;
+    }
+
+    // ASN.1
+    int frst=((sig[0]&0x80)!=0?1:0);
+    int scnd=((sig[20]&0x80)!=0?1:0);
+    //System.err.println("frst: "+frst+", scnd: "+scnd);
+
+    int length=sig.length+6+frst+scnd;
+    tmp=new byte[length];
+    tmp[0]=(byte)0x30; tmp[1]=(byte)0x2c; 
+    tmp[1]+=frst; tmp[1]+=scnd;
+    tmp[2]=(byte)0x02; tmp[3]=(byte)0x14;
+    tmp[3]+=frst;
+    System.arraycopy(sig, 0, tmp, 4+frst, 20);
+    tmp[4+tmp[3]]=(byte)0x02; tmp[5+tmp[3]]=(byte)0x14;
+    tmp[5+tmp[3]]+=scnd;
+    System.arraycopy(sig, 20, tmp, 6+tmp[3]+scnd, 20);
+    sig=tmp;
+
+/*
+    tmp=new byte[sig.length+6];
+    tmp[0]=(byte)0x30; tmp[1]=(byte)0x2c; 
+    tmp[2]=(byte)0x02; tmp[3]=(byte)0x14;
+    System.arraycopy(sig, 0, tmp, 4, 20);
+    tmp[24]=(byte)0x02; tmp[25]=(byte)0x14;
+    System.arraycopy(sig, 20, tmp, 26, 20); sig=tmp;
+*/  
+    return signature.verify(sig);
+  }
+}
diff --git a/java/com/jcraft/jsch/jce/SignatureRSA.java b/java/com/jcraft/jsch/jce/SignatureRSA.java
new file mode 100644
index 0000000..50d8b5a
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/SignatureRSA.java
@@ -0,0 +1,83 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import java.math.BigInteger;
+import java.security.*;
+import java.security.spec.*;
+
+public class SignatureRSA implements com.jcraft.jsch.SignatureRSA{
+
+  java.security.Signature signature;
+  KeyFactory keyFactory;
+
+  public void init() throws Exception{
+    signature=java.security.Signature.getInstance("SHA1withRSA");
+    keyFactory=KeyFactory.getInstance("RSA");
+  }     
+  public void setPubKey(byte[] e, byte[] n) throws Exception{
+    RSAPublicKeySpec rsaPubKeySpec = 
+	new RSAPublicKeySpec(new BigInteger(n),
+			     new BigInteger(e));
+    PublicKey pubKey=keyFactory.generatePublic(rsaPubKeySpec);
+    signature.initVerify(pubKey);
+  }
+  public void setPrvKey(byte[] d, byte[] n) throws Exception{
+    RSAPrivateKeySpec rsaPrivKeySpec = 
+	new RSAPrivateKeySpec(new BigInteger(n),
+			      new BigInteger(d));
+    PrivateKey prvKey = keyFactory.generatePrivate(rsaPrivKeySpec);
+    signature.initSign(prvKey);
+  }
+  public byte[] sign() throws Exception{
+    byte[] sig=signature.sign();      
+    return sig;
+  }
+  public void update(byte[] foo) throws Exception{
+   signature.update(foo);
+  }
+  public boolean verify(byte[] sig) throws Exception{
+    int i=0;
+    int j=0;
+    byte[] tmp;
+
+    if(sig[0]==0 && sig[1]==0 && sig[2]==0){
+    j=((sig[i++]<<24)&0xff000000)|((sig[i++]<<16)&0x00ff0000)|
+	((sig[i++]<<8)&0x0000ff00)|((sig[i++])&0x000000ff);
+    i+=j;
+    j=((sig[i++]<<24)&0xff000000)|((sig[i++]<<16)&0x00ff0000)|
+	((sig[i++]<<8)&0x0000ff00)|((sig[i++])&0x000000ff);
+    tmp=new byte[j]; 
+    System.arraycopy(sig, i, tmp, 0, j); sig=tmp;
+    }
+//System.err.println("j="+j+" "+Integer.toHexString(sig[0]&0xff));
+    return signature.verify(sig);
+  }
+}
diff --git a/java/com/jcraft/jsch/jce/TripleDESCBC.java b/java/com/jcraft/jsch/jce/TripleDESCBC.java
new file mode 100644
index 0000000..2cbf9b7
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/TripleDESCBC.java
@@ -0,0 +1,84 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class TripleDESCBC implements Cipher{
+  private static final int ivsize=8;
+  private static final int bsize=24;
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    String pad="NoPadding";      
+    //if(padding) pad="PKCS5Padding";
+    byte[] tmp;
+    if(iv.length>ivsize){
+      tmp=new byte[ivsize];
+      System.arraycopy(iv, 0, tmp, 0, tmp.length);
+      iv=tmp;
+    }
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+
+    try{
+      cipher=javax.crypto.Cipher.getInstance("DESede/CBC/"+pad);
+/*
+      // The following code does not work on IBM's JDK 1.4.1
+      SecretKeySpec skeySpec = new SecretKeySpec(key, "DESede");
+      cipher.init((mode==ENCRYPT_MODE?
+		   javax.crypto.Cipher.ENCRYPT_MODE:
+		   javax.crypto.Cipher.DECRYPT_MODE),
+		  skeySpec, new IvParameterSpec(iv));
+*/
+      DESedeKeySpec keyspec=new DESedeKeySpec(key);
+      SecretKeyFactory keyfactory=SecretKeyFactory.getInstance("DESede");
+      SecretKey _key=keyfactory.generateSecret(keyspec);
+      cipher.init((mode==ENCRYPT_MODE?
+		   javax.crypto.Cipher.ENCRYPT_MODE:
+		   javax.crypto.Cipher.DECRYPT_MODE),
+		  _key, new IvParameterSpec(iv));
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+  public boolean isCBC(){return true; }
+}
diff --git a/java/com/jcraft/jsch/jce/TripleDESCTR.java b/java/com/jcraft/jsch/jce/TripleDESCTR.java
new file mode 100644
index 0000000..899f03b
--- /dev/null
+++ b/java/com/jcraft/jsch/jce/TripleDESCTR.java
@@ -0,0 +1,84 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2008-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jce;
+
+import com.jcraft.jsch.Cipher;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class TripleDESCTR implements Cipher{
+  private static final int ivsize=8;
+  private static final int bsize=24;
+  private javax.crypto.Cipher cipher;    
+  public int getIVSize(){return ivsize;} 
+  public int getBlockSize(){return bsize;}
+  public void init(int mode, byte[] key, byte[] iv) throws Exception{
+    String pad="NoPadding";      
+    //if(padding) pad="PKCS5Padding";
+    byte[] tmp;
+    if(iv.length>ivsize){
+      tmp=new byte[ivsize];
+      System.arraycopy(iv, 0, tmp, 0, tmp.length);
+      iv=tmp;
+    }
+    if(key.length>bsize){
+      tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, tmp.length);
+      key=tmp;
+    }
+
+    try{
+      cipher=javax.crypto.Cipher.getInstance("DESede/CTR/"+pad);
+/*
+      // The following code does not work on IBM's JDK 1.4.1
+      SecretKeySpec skeySpec = new SecretKeySpec(key, "DESede");
+      cipher.init((mode==ENCRYPT_MODE?
+		   javax.crypto.Cipher.ENCRYPT_MODE:
+		   javax.crypto.Cipher.DECRYPT_MODE),
+		  skeySpec, new IvParameterSpec(iv));
+*/
+      DESedeKeySpec keyspec=new DESedeKeySpec(key);
+      SecretKeyFactory keyfactory=SecretKeyFactory.getInstance("DESede");
+      SecretKey _key=keyfactory.generateSecret(keyspec);
+      cipher.init((mode==ENCRYPT_MODE?
+		   javax.crypto.Cipher.ENCRYPT_MODE:
+		   javax.crypto.Cipher.DECRYPT_MODE),
+		  _key, new IvParameterSpec(iv));
+    }
+    catch(Exception e){
+      cipher=null;
+      throw e;
+    }
+  }
+  public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
+    cipher.update(foo, s1, len, bar, s2);
+  }
+  public boolean isCBC(){return false; }
+}
diff --git a/java/com/jcraft/jsch/jcraft/Compression.java b/java/com/jcraft/jsch/jcraft/Compression.java
new file mode 100644
index 0000000..dddcaed
--- /dev/null
+++ b/java/com/jcraft/jsch/jcraft/Compression.java
@@ -0,0 +1,140 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jcraft;
+import com.jcraft.jzlib.*;
+import com.jcraft.jsch.*;
+
+public class Compression implements com.jcraft.jsch.Compression {
+  static private final int BUF_SIZE=4096;
+  private final int buffer_margin=32+20; // AES256 + HMACSHA1
+  private int type;
+  private ZStream stream;
+  private byte[] tmpbuf=new byte[BUF_SIZE];
+
+  public Compression(){
+    stream=new ZStream();
+  }
+
+  public void init(int type, int level){
+    if(type==DEFLATER){
+      stream.deflateInit(level);
+      this.type=DEFLATER;
+    }
+    else if(type==INFLATER){
+      stream.inflateInit();
+      inflated_buf=new byte[BUF_SIZE];
+      this.type=INFLATER;
+    }
+  }
+
+  private byte[] inflated_buf;
+
+  public byte[] compress(byte[] buf, int start, int[] len){
+    stream.next_in=buf;
+    stream.next_in_index=start;
+    stream.avail_in=len[0]-start;
+    int status;
+    int outputlen=start;
+    byte[] outputbuf=buf;
+    int tmp=0;
+
+    do{
+      stream.next_out=tmpbuf;
+      stream.next_out_index=0;
+      stream.avail_out=BUF_SIZE;
+      status=stream.deflate(JZlib.Z_PARTIAL_FLUSH);
+      switch(status){
+        case JZlib.Z_OK:
+          tmp=BUF_SIZE-stream.avail_out;
+          if(outputbuf.length<outputlen+tmp+buffer_margin){
+            byte[] foo=new byte[(outputlen+tmp+buffer_margin)*2];
+            System.arraycopy(outputbuf, 0, foo, 0, outputbuf.length);
+            outputbuf=foo;
+          }
+          System.arraycopy(tmpbuf, 0, outputbuf, outputlen, tmp);
+          outputlen+=tmp;
+          break;
+        default:
+	    System.err.println("compress: deflate returnd "+status);
+      }
+    }
+    while(stream.avail_out==0);
+
+    len[0]=outputlen;
+    return outputbuf;
+  }
+
+  public byte[] uncompress(byte[] buffer, int start, int[] length){
+    int inflated_end=0;
+
+    stream.next_in=buffer;
+    stream.next_in_index=start;
+    stream.avail_in=length[0];
+
+    while(true){
+      stream.next_out=tmpbuf;
+      stream.next_out_index=0;
+      stream.avail_out=BUF_SIZE;
+      int status=stream.inflate(JZlib.Z_PARTIAL_FLUSH);
+      switch(status){
+        case JZlib.Z_OK:
+	  if(inflated_buf.length<inflated_end+BUF_SIZE-stream.avail_out){
+            int len=inflated_buf.length*2;
+            if(len<inflated_end+BUF_SIZE-stream.avail_out)
+              len=inflated_end+BUF_SIZE-stream.avail_out;
+            byte[] foo=new byte[len];
+	    System.arraycopy(inflated_buf, 0, foo, 0, inflated_end);
+	    inflated_buf=foo;
+	  }
+	  System.arraycopy(tmpbuf, 0,
+			   inflated_buf, inflated_end,
+			   BUF_SIZE-stream.avail_out);
+	  inflated_end+=(BUF_SIZE-stream.avail_out);
+          length[0]=inflated_end;
+	  break;
+        case JZlib.Z_BUF_ERROR:
+          if(inflated_end>buffer.length-start){
+            byte[] foo=new byte[inflated_end+start];
+            System.arraycopy(buffer, 0, foo, 0, start);
+            System.arraycopy(inflated_buf, 0, foo, start, inflated_end);
+	    buffer=foo;
+	  }
+	  else{
+            System.arraycopy(inflated_buf, 0, buffer, start, inflated_end);
+	  }
+          length[0]=inflated_end;
+	  return buffer;
+	default:
+	  System.err.println("uncompress: inflate returnd "+status);
+          return null;
+      }
+    }
+  }
+}
diff --git a/java/com/jcraft/jsch/jcraft/HMAC.java b/java/com/jcraft/jsch/jcraft/HMAC.java
new file mode 100644
index 0000000..9aedc27
--- /dev/null
+++ b/java/com/jcraft/jsch/jcraft/HMAC.java
@@ -0,0 +1,107 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jcraft;
+
+import java.security.*;
+
+class HMAC{
+
+  /*
+   * Refer to RFC2104.
+   *
+   * H(K XOR opad, H(K XOR ipad, text))
+   *
+   * where K is an n byte key
+   * ipad is the byte 0x36 repeated 64 times
+   * opad is the byte 0x5c repeated 64 times
+   * and text is the data being protected
+   */
+  private static final int B=64;
+  private byte[] k_ipad=null;
+  private byte[] k_opad=null;
+
+  private MessageDigest md=null;
+
+  private int bsize=0;
+
+  protected void setH(MessageDigest md){
+    this.md=md;
+    bsize=md.getDigestLength();
+  }
+
+  public int getBlockSize(){return bsize;};
+  public void init(byte[] key) throws Exception{
+    if(key.length>bsize){
+      byte[] tmp=new byte[bsize];
+      System.arraycopy(key, 0, tmp, 0, bsize);	  
+      key=tmp;
+    }
+
+    /* if key is longer than B bytes reset it to key=MD5(key) */
+    if(key.length>B){
+      md.update(key, 0, key.length);
+      key=md.digest();
+    }
+
+    k_ipad=new byte[B];
+    System.arraycopy(key, 0, k_ipad, 0, key.length);
+    k_opad=new byte[B];
+    System.arraycopy(key, 0, k_opad, 0, key.length);
+
+    /* XOR key with ipad and opad values */
+    for(int i=0; i<B; i++) {
+      k_ipad[i]^=(byte)0x36;
+      k_opad[i]^=(byte)0x5c;
+    }
+
+    md.update(k_ipad, 0, B);
+  }
+
+  private final byte[] tmp=new byte[4];
+  public void update(int i){
+    tmp[0]=(byte)(i>>>24);
+    tmp[1]=(byte)(i>>>16);
+    tmp[2]=(byte)(i>>>8);
+    tmp[3]=(byte)i;
+    update(tmp, 0, 4);
+  }
+
+  public void update(byte foo[], int s, int l){
+    md.update(foo, s, l);
+  }
+
+  public void doFinal(byte[] buf, int offset){
+    byte[] result=md.digest();
+    md.update(k_opad, 0, B);
+    md.update(result, 0, bsize);
+    try{md.digest(buf, offset, bsize);}catch(Exception e){}
+    md.update(k_ipad, 0, B);
+  }
+}
diff --git a/java/com/jcraft/jsch/jcraft/HMACMD5.java b/java/com/jcraft/jsch/jcraft/HMACMD5.java
new file mode 100644
index 0000000..9096011
--- /dev/null
+++ b/java/com/jcraft/jsch/jcraft/HMACMD5.java
@@ -0,0 +1,51 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jcraft;
+
+import com.jcraft.jsch.MAC;
+import java.security.*;
+
+public class HMACMD5 extends HMAC implements MAC{
+  private static final String name="hmac-md5";
+
+  public HMACMD5(){
+    super();
+    MessageDigest md=null;
+    try{ md=MessageDigest.getInstance("MD5"); }
+    catch(Exception e){
+      System.err.println(e);
+    }
+    setH(md);
+  }
+
+  public String getName(){
+    return name;
+  }
+}
diff --git a/java/com/jcraft/jsch/jcraft/HMACMD596.java b/java/com/jcraft/jsch/jcraft/HMACMD596.java
new file mode 100644
index 0000000..95c6f60
--- /dev/null
+++ b/java/com/jcraft/jsch/jcraft/HMACMD596.java
@@ -0,0 +1,50 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jcraft;
+
+import com.jcraft.jsch.MAC;
+
+public class HMACMD596 extends HMACMD5{
+
+  private static final String name="hmac-md5-96";
+  private static final int BSIZE=12;
+
+  public int getBlockSize(){return BSIZE;};
+
+  private final byte[] _buf16=new byte[16];
+  public void doFinal(byte[] buf, int offset){
+    super.doFinal(_buf16, 0);
+    System.arraycopy(_buf16, 0, buf, offset, BSIZE);
+  }
+
+  public String getName(){
+    return name;
+  }
+}
diff --git a/java/com/jcraft/jsch/jcraft/HMACSHA1.java b/java/com/jcraft/jsch/jcraft/HMACSHA1.java
new file mode 100644
index 0000000..ea9eccf
--- /dev/null
+++ b/java/com/jcraft/jsch/jcraft/HMACSHA1.java
@@ -0,0 +1,51 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jcraft;
+
+import com.jcraft.jsch.MAC;
+import java.security.*;
+
+public class HMACSHA1 extends HMAC implements MAC{
+  private static final String name="hmac-sha1";
+
+  public HMACSHA1(){
+    super();
+    MessageDigest md=null;
+    try{ md=MessageDigest.getInstance("SHA-1"); }
+    catch(Exception e){
+      System.err.println(e);
+    }
+    setH(md);
+  }
+
+  public String getName(){
+    return name;
+  }
+}
diff --git a/java/com/jcraft/jsch/jcraft/HMACSHA196.java b/java/com/jcraft/jsch/jcraft/HMACSHA196.java
new file mode 100644
index 0000000..86a81b5
--- /dev/null
+++ b/java/com/jcraft/jsch/jcraft/HMACSHA196.java
@@ -0,0 +1,50 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jcraft;
+
+import com.jcraft.jsch.MAC;
+
+public class HMACSHA196 extends HMACSHA1{
+
+  private static final String name="hmac-sha1-96";
+  private static final int BSIZE=12;
+
+  public int getBlockSize(){return BSIZE;};
+
+  private final byte[] _buf16=new byte[20];
+  public void doFinal(byte[] buf, int offset){
+    super.doFinal(_buf16, 0);
+    System.arraycopy(_buf16, 0, buf, offset, BSIZE);
+  }
+
+  public String getName(){
+    return name;
+  }
+}
diff --git a/java/com/jcraft/jsch/jgss/GSSContextKrb5.java b/java/com/jcraft/jsch/jgss/GSSContextKrb5.java
new file mode 100644
index 0000000..9ee1560
--- /dev/null
+++ b/java/com/jcraft/jsch/jgss/GSSContextKrb5.java
@@ -0,0 +1,177 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright 
+     notice, this list of conditions and the following disclaimer in 
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch.jgss;
+
+import com.jcraft.jsch.JSchException;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.MessageProp;
+import org.ietf.jgss.Oid;
+
+public class GSSContextKrb5 implements com.jcraft.jsch.GSSContext{
+
+  private static final String pUseSubjectCredsOnly = 
+    "javax.security.auth.useSubjectCredsOnly";
+  private static String useSubjectCredsOnly = 
+    getSystemProperty(pUseSubjectCredsOnly);
+
+  private GSSContext context=null;
+  public void create(String user, String host) throws JSchException{
+    try{
+      // RFC 1964
+      Oid krb5=new Oid("1.2.840.113554.1.2.2");
+      // Kerberos Principal Name Form
+      Oid principalName=new Oid("1.2.840.113554.1.2.2.1");
+
+      GSSManager mgr=GSSManager.getInstance();
+
+      GSSCredential crd=null;
+      /*
+      try{
+        GSSName _user=mgr.createName(user, principalName);
+        crd=mgr.createCredential(_user,
+                                 GSSCredential.DEFAULT_LIFETIME,
+                                 krb5,
+                                 GSSCredential.INITIATE_ONLY);
+      }
+      catch(GSSException crdex){
+      }
+      */
+
+      String cname=host;
+      try{
+        cname=InetAddress.getByName(cname).getCanonicalHostName();
+      }
+      catch(UnknownHostException e){
+      }
+      GSSName _host=mgr.createName("host/"+cname, principalName);
+
+      context=mgr.createContext(_host,
+                                krb5,
+                                crd,
+                                GSSContext.DEFAULT_LIFETIME);
+
+      // RFC4462  3.4.  GSS-API Session
+      //
+      // When calling GSS_Init_sec_context(), the client MUST set
+      // integ_req_flag to "true" to request that per-message integrity
+      // protection be supported for this context.  In addition,
+      // deleg_req_flag MAY be set to "true" to request access delegation, if
+      // requested by the user.
+      //
+      // Since the user authentication process by its nature authenticates
+      // only the client, the setting of mutual_req_flag is not needed for
+      // this process.  This flag SHOULD be set to "false".
+
+      // TODO: OpenSSH's sshd does accepts 'false' for mutual_req_flag
+      //context.requestMutualAuth(false);
+      context.requestMutualAuth(true);
+      context.requestConf(true);
+      context.requestInteg(true);             // for MIC
+      context.requestCredDeleg(true);
+      context.requestAnonymity(false);
+
+      return;
+    }
+    catch(GSSException ex){
+      throw new JSchException(ex.toString());
+    }
+  }
+
+  public boolean isEstablished(){
+    return context.isEstablished();
+  }
+
+  public byte[] init(byte[] token, int s, int l) throws JSchException {
+    try{
+      // Without setting "javax.security.auth.useSubjectCredsOnly" to "false",
+      // Sun's JVM for Un*x will show messages to stderr in
+      // processing context.initSecContext().
+      // This hack is not thread safe ;-<.
+      // If that property is explicitly given as "true" or "false",
+      // this hack must not be invoked.
+      if(useSubjectCredsOnly==null){
+        setSystemProperty(pUseSubjectCredsOnly, "false");
+      }
+      return context.initSecContext(token, 0, l);
+    }
+    catch(GSSException ex){
+      throw new JSchException(ex.toString());
+    }
+    catch(java.lang.SecurityException ex){
+      throw new JSchException(ex.toString());
+    }
+    finally{
+      if(useSubjectCredsOnly==null){
+        // By the default, it must be "true".
+        setSystemProperty(pUseSubjectCredsOnly, "true");
+      }
+    }
+  }
+
+  public byte[] getMIC(byte[] message, int s, int l){
+    try{
+      MessageProp prop =  new MessageProp(0, true);
+      return context.getMIC(message, s, l, prop);
+    }
+    catch(GSSException ex){
+      return null;
+    }
+  }
+
+  public void dispose(){
+    try{
+      context.dispose();
+    }
+    catch(GSSException ex){
+    }
+  }
+
+  private static String getSystemProperty(String key){
+    try{ return System.getProperty(key); }
+    catch(Exception e){ 
+      // We are not allowed to get the System properties.
+      return null; 
+    } 
+  }
+
+  private static void setSystemProperty(String key, String value){
+    try{ System.setProperty(key, value); }
+    catch(Exception e){ 
+      // We are not allowed to set the System properties.
+    }
+  }
+}
diff --git a/java/com/tigervnc/network/TcpListener.java b/java/com/tigervnc/network/TcpListener.java
index 4cc8c0d..cb0a69a 100644
--- a/java/com/tigervnc/network/TcpListener.java
+++ b/java/com/tigervnc/network/TcpListener.java
@@ -100,7 +100,7 @@
 
     // Accept an incoming connection
     try {
-      if (selector.select() > 0) {
+      if (selector.select(0) > 0) {
         Set keys = selector.selectedKeys();
         Iterator iter = keys.iterator();
         while (iter.hasNext()) {
diff --git a/java/com/tigervnc/network/TcpSocket.java b/java/com/tigervnc/network/TcpSocket.java
index 9277dd1..1d127f5 100644
--- a/java/com/tigervnc/network/TcpSocket.java
+++ b/java/com/tigervnc/network/TcpSocket.java
@@ -171,29 +171,23 @@
     return ((InetSocketAddress)((SocketDescriptor)getFd()).socket().getRemoteSocketAddress()).getPort();
   }
 
+  /* Tunnelling support. */
+  public static int findFreeTcpPort() {
+    java.net.ServerSocket sock;
+    int port;
+    try {
+      sock = new java.net.ServerSocket(0);
+      port = sock.getLocalPort();
+      sock.close();
+    } catch (java.io.IOException e) {
+      throw new SocketException("unable to create socket: "+e.toString());
+    }
+    return port;
+  }
+
   private boolean closeFd;
   static LogWriter vlog = new LogWriter("TcpSocket");
 
 }
 
-/* Tunnelling support. */
-/*
-public int findFreeTcpPort() {
-  int sock;
-
-  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
-    throw SocketException("unable to create socket", errorNumber);
-
-  int port = 0;
-  if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
-    throw SocketException("unable to find free port", errorNumber);
-
-  socklen_t n = sizeof(addr);
-  if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
-    throw SocketException("unable to get port number", errorNumber);
-
-  closesocket(sock);
-  return ntohs(addr.sin_port);
-}
-*/
 
diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java
index a87ddae..053bef2 100644
--- a/java/com/tigervnc/vncviewer/CConn.java
+++ b/java/com/tigervnc/vncviewer/CConn.java
@@ -314,7 +314,7 @@
       }
     }
     if (passwd != null)
-      passwd.append(dlg.passwdEntry.getText());
+      passwd.append(new String(dlg.passwdEntry.getPassword()));
     return true;
   }
 
diff --git a/java/com/tigervnc/vncviewer/PasswdDialog.java b/java/com/tigervnc/vncviewer/PasswdDialog.java
index d947828..51c268f 100644
--- a/java/com/tigervnc/vncviewer/PasswdDialog.java
+++ b/java/com/tigervnc/vncviewer/PasswdDialog.java
@@ -18,10 +18,14 @@
 
 package com.tigervnc.vncviewer;
 
+import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
+import com.jcraft.jsch.*;
 
-class PasswdDialog extends Dialog implements KeyListener{
+class PasswdDialog extends Dialog implements KeyListener,
+                                             UserInfo,
+                                             UIKeyboardInteractive {
 
   public PasswdDialog(String title, boolean userDisabled, boolean passwdDisabled) {
     super(true);
@@ -79,8 +83,90 @@
     }
   }
 
+  public String getPassword(){ 
+    return new String(passwdEntry.getPassword());
+  }
+  public String getPassphrase(){ return null; }
+  public boolean promptPassphrase(String message){ return false; }
+  public boolean promptPassword(String message){
+    setTitle(message);
+    showDialog();
+    if (passwdEntry != null)
+      return true;
+    return false;
+  }
+  public void showMessage(String message){
+    JOptionPane.showMessageDialog(null, message);
+  }
+  public boolean promptYesNo(String str){
+    Object[] options={ "yes", "no" };
+    int foo=JOptionPane.showOptionDialog(null, 
+           str,
+           "Warning", 
+           JOptionPane.DEFAULT_OPTION, 
+           JOptionPane.WARNING_MESSAGE,
+           null, options, options[0]);
+     return foo==0;
+  }
+  public String[] promptKeyboardInteractive(String destination,
+                                            String name,
+                                            String instruction,
+                                            String[] prompt,
+                                            boolean[] echo){
+    Container panel = new JPanel();
+    panel.setLayout(new GridBagLayout());
+
+    GridBagConstraints gbc = 
+      new GridBagConstraints(0,0,1,1,1,1,
+                             GridBagConstraints.NORTHWEST,
+                             GridBagConstraints.NONE,
+                             new Insets(0,0,0,0),0,0);
+    gbc.weightx = 1.0;
+    gbc.gridwidth = GridBagConstraints.REMAINDER;
+    gbc.gridx = 0;
+    panel.add(new JLabel(instruction), gbc);
+    gbc.gridy++;
+
+    gbc.gridwidth = GridBagConstraints.RELATIVE;
+
+    JTextField[] texts=new JTextField[prompt.length];
+    for(int i=0; i<prompt.length; i++){
+      gbc.fill = GridBagConstraints.NONE;
+      gbc.gridx = 0;
+      gbc.weightx = 1;
+      panel.add(new JLabel(prompt[i]),gbc);
+
+      gbc.gridx = 1;
+      gbc.fill = GridBagConstraints.HORIZONTAL;
+      gbc.weighty = 1;
+      if(echo[i]){
+        texts[i]=new JTextField(20);
+      }
+      else{
+        texts[i]=new JPasswordField(20);
+      }
+      panel.add(texts[i], gbc);
+      gbc.gridy++;
+    }
+
+    if(JOptionPane.showConfirmDialog(null, panel, 
+                                     destination+": "+name,
+                                     JOptionPane.OK_CANCEL_OPTION,
+                                     JOptionPane.QUESTION_MESSAGE)
+       ==JOptionPane.OK_OPTION){
+      String[] response=new String[prompt.length];
+      for(int i=0; i<prompt.length; i++){
+        response[i]=texts[i].getText();
+      }
+	return response;
+    }
+    else{
+      return null;  // cancel
+    }
+  }
+
   JLabel userLabel;
   JTextField userEntry;
   JLabel passwdLabel;
-  JTextField passwdEntry;
+  JPasswordField passwdEntry;
 }
diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java
index 91504d2..f7d1f62 100644
--- a/java/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/com/tigervnc/vncviewer/VncViewer.java
@@ -33,15 +33,21 @@
 import java.awt.Image;
 import java.io.InputStream;
 import java.io.IOException;
+import java.io.File;
 import java.lang.Character;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
+import java.util.ArrayList;
+import java.util.Iterator;
 import javax.swing.*;
 
 import com.tigervnc.rdr.*;
 import com.tigervnc.rfb.*;
 import com.tigervnc.network.*;
 
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.Session;
+
 public class VncViewer extends java.applet.Applet implements Runnable
 {
   public static final String about1 = "TigerVNC Viewer for Java";
@@ -130,6 +136,82 @@
     System.exit(1);
   }
 
+  /* Tunnelling support. */
+  private void interpretViaParam(StringParameter gatewayHost,
+    StringParameter remoteHost, IntParameter remotePort, 
+    StringParameter vncServerName, IntParameter localPort)
+  {
+    final int SERVER_PORT_OFFSET = 5900;;
+    int pos = vncServerName.getValueStr().indexOf(":");
+    if (pos == -1)
+      remotePort.setParam(""+SERVER_PORT_OFFSET+"");
+    else {
+      int portOffset = SERVER_PORT_OFFSET;
+      int len;
+      pos++;
+      len =  vncServerName.getValueStr().substring(pos).length();
+      if (vncServerName.getValueStr().substring(pos, pos).equals(":")) {
+        /* Two colons is an absolute port number, not an offset. */
+        pos++;
+        len--;
+        portOffset = 0;
+      }
+      try {
+        if (len <= 0 || !vncServerName.getValueStr().substring(pos).matches("[0-9]+"))
+          usage();
+        portOffset += Integer.parseInt(vncServerName.getValueStr().substring(pos));
+        remotePort.setParam(""+portOffset+"");
+      } catch (java.lang.NumberFormatException e) {
+        usage();
+      }
+    }
+  
+    if (vncServerName != null)
+      remoteHost.setParam(vncServerName.getValueStr().split(":")[0]);
+  
+    gatewayHost.setParam(via.getValueStr());
+    vncServerName.setParam("localhost::"+localPort.getValue());
+  }
+
+  private void
+  createTunnel(String gatewayHost, String remoteHost,
+          int remotePort, int localPort)
+  {
+    try{
+      JSch jsch=new JSch();
+      String homeDir = new String("");
+      try {
+        homeDir = System.getProperty("user.home");
+      } catch(java.security.AccessControlException e) {
+        System.out.println("Cannot access user.home system property");
+      }
+      // NOTE: jsch does not support all ciphers.  User may be
+      //       prompted to accept host key authenticy even if
+      //       the key is in the known_hosts file.
+      File knownHosts = new File(homeDir+"/.ssh/known_hosts");
+      if (knownHosts.exists() && knownHosts.canRead())
+	      jsch.setKnownHosts(knownHosts.getAbsolutePath());
+      ArrayList<File> privateKeys = new ArrayList<File>();
+      privateKeys.add(new File(homeDir+"/.ssh/id_rsa"));
+      privateKeys.add(new File(homeDir+"/.ssh/id_dsa"));
+      for (Iterator i = privateKeys.iterator(); i.hasNext();) {
+        File privateKey = (File)i.next();
+        if (privateKey.exists() && privateKey.canRead())
+	        jsch.addIdentity(privateKey.getAbsolutePath());
+      }
+      // username and passphrase will be given via UserInfo interface.
+      PasswdDialog dlg = new PasswdDialog(new String("SSH Authentication"), false, false);
+      dlg.userEntry.setText((String)System.getProperties().get("user.name"));
+      Session session=jsch.getSession(dlg.userEntry.getText(), gatewayHost, 22);
+      session.setUserInfo(dlg);
+      session.connect();
+
+      session.setPortForwardingL(localPort, remoteHost, remotePort);
+    } catch (java.lang.Exception e) {
+      System.out.println(e);
+    }
+  }
+
   public VncViewer() {
     applet = true;
     firstApplet = true;
@@ -193,6 +275,21 @@
     CConn cc = null;
     Socket sock = null;
 
+    /* Tunnelling support. */
+    if (via.getValueStr() != null) {
+      StringParameter gatewayHost = new StringParameter("", "", "");
+      StringParameter remoteHost = new StringParameter("", "", "localhost");
+      IntParameter localPort = 
+        new IntParameter("", "", TcpSocket.findFreeTcpPort());
+      IntParameter remotePort = new IntParameter("", "", 5900);
+      if (vncServerName.getValueStr() == null)
+        usage();
+      interpretViaParam(gatewayHost, remoteHost, remotePort, 
+        vncServerName, localPort);
+      createTunnel(gatewayHost.getValueStr(), remoteHost.getValueStr(), 
+        remotePort.getValue(), localPort.getValue());
+    }
+
     if (listenMode.getValue()) {
       int port = 5500;
 
@@ -322,6 +419,9 @@
   = new BoolParameter("AcceptBell",
                       "Produce a system beep when requested to by the server.", 
                       true);
+  StringParameter via
+  = new StringParameter("via", "Gateway to tunnel via", null);
+
   BoolParameter customCompressLevel
   = new BoolParameter("CustomCompressLevel",
                           "Use custom compression level. "+