Initial commit.
authorJoshua Wise <jwise@andrew.cmu.edu>
Sat, 7 Aug 2010 06:11:29 +0000 (02:11 -0400)
committerJoshua Wise <jwise@andrew.cmu.edu>
Sat, 7 Aug 2010 06:11:29 +0000 (02:11 -0400)
131 files changed:
.classpath [new file with mode: 0644]
.project [new file with mode: 0644]
AndroidManifest.xml [new file with mode: 0644]
build.xml [new file with mode: 0644]
default.properties [new file with mode: 0644]
local.properties [new file with mode: 0644]
res/drawable-hdpi/icon.png [new file with mode: 0644]
res/drawable-ldpi/icon.png [new file with mode: 0644]
res/drawable-mdpi/icon.png [new file with mode: 0644]
res/layout/main.xml [new file with mode: 0644]
res/layout/passwd.xml [new file with mode: 0644]
res/values/strings.xml [new file with mode: 0644]
src/com/jcraft/jsch/Buffer.java [new file with mode: 0644]
src/com/jcraft/jsch/Channel.java [new file with mode: 0644]
src/com/jcraft/jsch/ChannelAgentForwarding.java [new file with mode: 0644]
src/com/jcraft/jsch/ChannelDirectTCPIP.java [new file with mode: 0644]
src/com/jcraft/jsch/ChannelExec.java [new file with mode: 0644]
src/com/jcraft/jsch/ChannelForwardedTCPIP.java [new file with mode: 0644]
src/com/jcraft/jsch/ChannelSession.java [new file with mode: 0644]
src/com/jcraft/jsch/ChannelSftp.java [new file with mode: 0644]
src/com/jcraft/jsch/ChannelShell.java [new file with mode: 0644]
src/com/jcraft/jsch/ChannelSubsystem.java [new file with mode: 0644]
src/com/jcraft/jsch/ChannelX11.java [new file with mode: 0644]
src/com/jcraft/jsch/Cipher.java [new file with mode: 0644]
src/com/jcraft/jsch/CipherNone.java [new file with mode: 0644]
src/com/jcraft/jsch/Compression.java [new file with mode: 0644]
src/com/jcraft/jsch/DH.java [new file with mode: 0644]
src/com/jcraft/jsch/DHG1.java [new file with mode: 0644]
src/com/jcraft/jsch/DHGEX.java [new file with mode: 0644]
src/com/jcraft/jsch/ForwardedTCPIPDaemon.java [new file with mode: 0644]
src/com/jcraft/jsch/GSSContext.java [new file with mode: 0644]
src/com/jcraft/jsch/HASH.java [new file with mode: 0644]
src/com/jcraft/jsch/HostKey.java [new file with mode: 0644]
src/com/jcraft/jsch/HostKeyRepository.java [new file with mode: 0644]
src/com/jcraft/jsch/IO.java [new file with mode: 0644]
src/com/jcraft/jsch/Identity.java [new file with mode: 0644]
src/com/jcraft/jsch/IdentityFile.java [new file with mode: 0644]
src/com/jcraft/jsch/JSch.java [new file with mode: 0644]
src/com/jcraft/jsch/JSchAuthCancelException.java [new file with mode: 0644]
src/com/jcraft/jsch/JSchException.java [new file with mode: 0644]
src/com/jcraft/jsch/JSchPartialAuthException.java [new file with mode: 0644]
src/com/jcraft/jsch/KeyExchange.java [new file with mode: 0644]
src/com/jcraft/jsch/KeyPair.java [new file with mode: 0644]
src/com/jcraft/jsch/KeyPairDSA.java [new file with mode: 0644]
src/com/jcraft/jsch/KeyPairGenDSA.java [new file with mode: 0644]
src/com/jcraft/jsch/KeyPairGenRSA.java [new file with mode: 0644]
src/com/jcraft/jsch/KeyPairRSA.java [new file with mode: 0644]
src/com/jcraft/jsch/KnownHosts.java [new file with mode: 0644]
src/com/jcraft/jsch/Logger.java [new file with mode: 0644]
src/com/jcraft/jsch/MAC.java [new file with mode: 0644]
src/com/jcraft/jsch/Packet.java [new file with mode: 0644]
src/com/jcraft/jsch/PortWatcher.java [new file with mode: 0644]
src/com/jcraft/jsch/Proxy.java [new file with mode: 0644]
src/com/jcraft/jsch/ProxyHTTP.java [new file with mode: 0644]
src/com/jcraft/jsch/ProxySOCKS4.java [new file with mode: 0644]
src/com/jcraft/jsch/ProxySOCKS5.java [new file with mode: 0644]
src/com/jcraft/jsch/Random.java [new file with mode: 0644]
src/com/jcraft/jsch/Request.java [new file with mode: 0644]
src/com/jcraft/jsch/RequestAgentForwarding.java [new file with mode: 0644]
src/com/jcraft/jsch/RequestEnv.java [new file with mode: 0644]
src/com/jcraft/jsch/RequestExec.java [new file with mode: 0644]
src/com/jcraft/jsch/RequestPtyReq.java [new file with mode: 0644]
src/com/jcraft/jsch/RequestSftp.java [new file with mode: 0644]
src/com/jcraft/jsch/RequestShell.java [new file with mode: 0644]
src/com/jcraft/jsch/RequestSignal.java [new file with mode: 0644]
src/com/jcraft/jsch/RequestSubsystem.java [new file with mode: 0644]
src/com/jcraft/jsch/RequestWindowChange.java [new file with mode: 0644]
src/com/jcraft/jsch/RequestX11.java [new file with mode: 0644]
src/com/jcraft/jsch/ServerSocketFactory.java [new file with mode: 0644]
src/com/jcraft/jsch/Session.java [new file with mode: 0644]
src/com/jcraft/jsch/SftpATTRS.java [new file with mode: 0644]
src/com/jcraft/jsch/SftpException.java [new file with mode: 0644]
src/com/jcraft/jsch/SftpProgressMonitor.java [new file with mode: 0644]
src/com/jcraft/jsch/SignatureDSA.java [new file with mode: 0644]
src/com/jcraft/jsch/SignatureRSA.java [new file with mode: 0644]
src/com/jcraft/jsch/SocketFactory.java [new file with mode: 0644]
src/com/jcraft/jsch/UIKeyboardInteractive.java [new file with mode: 0644]
src/com/jcraft/jsch/UserAuth.java [new file with mode: 0644]
src/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java [new file with mode: 0644]
src/com/jcraft/jsch/UserAuthKeyboardInteractive.java [new file with mode: 0644]
src/com/jcraft/jsch/UserAuthNone.java [new file with mode: 0644]
src/com/jcraft/jsch/UserAuthPassword.java [new file with mode: 0644]
src/com/jcraft/jsch/UserAuthPublicKey.java [new file with mode: 0644]
src/com/jcraft/jsch/UserInfo.java [new file with mode: 0644]
src/com/jcraft/jsch/Util.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/AES128CBC.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/AES128CTR.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/AES192CBC.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/AES192CTR.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/AES256CBC.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/AES256CTR.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/ARCFOUR.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/ARCFOUR128.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/ARCFOUR256.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/BlowfishCBC.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/DH.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/HMACMD5.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/HMACMD596.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/HMACSHA1.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/HMACSHA196.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/KeyPairGenDSA.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/KeyPairGenRSA.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/MD5.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/Random.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/SHA1.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/SignatureDSA.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/SignatureRSA.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/TripleDESCBC.java [new file with mode: 0644]
src/com/jcraft/jsch/jce/TripleDESCTR.java [new file with mode: 0644]
src/com/jcraft/jsch/jcraft/Compression.java [new file with mode: 0644]
src/com/jcraft/jsch/jcraft/HMAC.java [new file with mode: 0644]
src/com/jcraft/jsch/jcraft/HMACMD5.java [new file with mode: 0644]
src/com/jcraft/jsch/jcraft/HMACMD596.java [new file with mode: 0644]
src/com/jcraft/jsch/jcraft/HMACSHA1.java [new file with mode: 0644]
src/com/jcraft/jsch/jcraft/HMACSHA196.java [new file with mode: 0644]
src/com/jcraft/jzlib/Adler32.java [new file with mode: 0644]
src/com/jcraft/jzlib/Deflate.java [new file with mode: 0644]
src/com/jcraft/jzlib/InfBlocks.java [new file with mode: 0644]
src/com/jcraft/jzlib/InfCodes.java [new file with mode: 0644]
src/com/jcraft/jzlib/InfTree.java [new file with mode: 0644]
src/com/jcraft/jzlib/Inflate.java [new file with mode: 0644]
src/com/jcraft/jzlib/JZlib.java [new file with mode: 0644]
src/com/jcraft/jzlib/StaticTree.java [new file with mode: 0644]
src/com/jcraft/jzlib/Tree.java [new file with mode: 0644]
src/com/jcraft/jzlib/ZInputStream.java [new file with mode: 0644]
src/com/jcraft/jzlib/ZOutputStream.java [new file with mode: 0644]
src/com/jcraft/jzlib/ZStream.java [new file with mode: 0644]
src/com/jcraft/jzlib/ZStreamException.java [new file with mode: 0644]
src/com/joshuawise/dumload/Dumload.java [new file with mode: 0644]
src/com/joshuawise/dumload/NotifSlave.java [new file with mode: 0644]
src/com/joshuawise/dumload/Uploader.java [new file with mode: 0644]

diff --git a/.classpath b/.classpath
new file mode 100644 (file)
index 0000000..609aa00
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="src" path="gen"/>
+       <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/.project b/.project
new file mode 100644 (file)
index 0000000..f1d11b4
--- /dev/null
+++ b/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>dumload</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..b24610e
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.joshuawise.dumload"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <application android:icon="@drawable/icon" android:label="Dumload">
+        <activity android:name=".Dumload"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            
+
+</intent-filter>
+        <intent-filter><action android:name="android.intent.action.SEND"></action>
+        <category android:name="android.intent.category.DEFAULT" />
+<data android:mimeType="image/jpeg"></data>
+</intent-filter>
+</activity>
+
+<service android:name=".Uploader"><intent-filter><action android:name="com.joshuawise.dumload.Uploader" /></intent-filter></service>
+<activity android:name=".NotifSlave"><intent-filter><action android:name="com.joshuawise.dumload.NotifSlave" /></intent-filter></activity>
+    </application>
+
+
+
+<uses-permission android:name="android.permission.INTERNET"></uses-permission>
+<uses-permission android:name="android.permission.STATUS_BAR"></uses-permission>
+<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
+</manifest> 
\ No newline at end of file
diff --git a/build.xml b/build.xml
new file mode 100644 (file)
index 0000000..7ea4a86
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="Dumload" default="help">
+
+    <!-- The local.properties file is created and updated by the 'android' tool.
+         It contains the path to the SDK. It should *NOT* be checked in in Version
+         Control Systems. -->
+    <property file="local.properties" />
+
+    <!-- The build.properties file can be created by you and is never touched
+         by the 'android' tool. This is the place to change some of the default property values
+         used by the Ant rules.
+         Here are some properties you may want to change/update:
+
+         application.package
+             the name of your application package as defined in the manifest. Used by the
+             'uninstall' rule.
+         source.dir
+             the name of the source directory. Default is 'src'.
+         out.dir
+             the name of the output directory. Default is 'bin'.
+
+         Properties related to the SDK location or the project target should be updated
+          using the 'android' tool with the 'update' action.
+
+         This file is an integral part of the build system for your application and
+         should be checked in in Version Control Systems.
+
+         -->
+    <property file="build.properties" />
+
+    <!-- The default.properties file is created and updated by the 'android' tool, as well
+         as ADT.
+         This file is an integral part of the build system for your application and
+         should be checked in in Version Control Systems. -->
+    <property file="default.properties" />
+
+    <!-- Custom Android task to deal with the project target, and import the proper rules.
+         This requires ant 1.6.0 or above. -->
+    <path id="android.antlibs">
+        <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
+        <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
+        <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
+        <pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
+        <pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
+    </path>
+
+    <taskdef name="setup"
+        classname="com.android.ant.SetupTask"
+        classpathref="android.antlibs" />
+
+    <!-- Execute the Android Setup task that will setup some properties specific to the target,
+         and import the build rules files.
+
+         The rules file is imported from
+            <SDK>/platforms/<target_platform>/templates/android_rules.xml
+
+         To customize some build steps for your project:
+         - copy the content of the main node <project> from android_rules.xml
+         - paste it in this build.xml below the <setup /> task.
+         - disable the import by changing the setup task below to <setup import="false" />
+
+         This will ensure that the properties are setup correctly but that your customized
+         build steps are used.
+    -->
+    <setup />
+
+</project>
diff --git a/default.properties b/default.properties
new file mode 100644 (file)
index 0000000..9d135cb
--- /dev/null
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+# 
+# This file must be checked in Version Control Systems.
+# 
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-7
diff --git a/local.properties b/local.properties
new file mode 100644 (file)
index 0000000..57f202f
--- /dev/null
@@ -0,0 +1,10 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+# 
+# This file must *NOT* be checked in Version Control Systems,
+# as it contains information specific to your local configuration.
+
+# location of the SDK. This is only used by Ant
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=/Users/joshua/android/android-sdk-mac_86
diff --git a/res/drawable-hdpi/icon.png b/res/drawable-hdpi/icon.png
new file mode 100644 (file)
index 0000000..8074c4c
Binary files /dev/null and b/res/drawable-hdpi/icon.png differ
diff --git a/res/drawable-ldpi/icon.png b/res/drawable-ldpi/icon.png
new file mode 100644 (file)
index 0000000..1095584
Binary files /dev/null and b/res/drawable-ldpi/icon.png differ
diff --git a/res/drawable-mdpi/icon.png b/res/drawable-mdpi/icon.png
new file mode 100644 (file)
index 0000000..a07c69f
Binary files /dev/null and b/res/drawable-mdpi/icon.png differ
diff --git a/res/layout/main.xml b/res/layout/main.xml
new file mode 100644 (file)
index 0000000..da4f82e
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView  
+    android:id="@+id/suckit"
+    android:layout_width="fill_parent" 
+    android:layout_height="wrap_content" 
+    android:text="@string/hello"
+    />
+</LinearLayout>
diff --git a/res/layout/passwd.xml b/res/layout/passwd.xml
new file mode 100644 (file)
index 0000000..f475e8d
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:padding="10dp"
+    >
+<TextView  
+    android:id="@+id/prompt"
+    android:layout_width="fill_parent" 
+    android:layout_height="wrap_content" 
+    />
+<EditText
+    android:id="@+id/entry"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:background="@android:drawable/editbox_background"
+    android:password="true"
+    />
+<LinearLayout
+    android:orientation="horizontal"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    >
+<Button
+    android:id="@+id/ok"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:text="OK"
+    />
+<Button
+    android:id="@+id/cancel"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:text="Cancel"
+    />
+</LinearLayout>
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644 (file)
index 0000000..4606407
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="hello">Hello World, Dumload!</string>
+    <string name="app_name">dumload</string>
+</resources>
diff --git a/src/com/jcraft/jsch/Buffer.java b/src/com/jcraft/jsch/Buffer.java
new file mode 100644 (file)
index 0000000..1393a12
--- /dev/null
@@ -0,0 +1,243 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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();
+    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
+       s+i>index){
+      //throw new java.io.IOException("invalid string length: "+(((long)i)&0xffffffffL));
+      i = index-s; // 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];
+  }
+
+/*
+  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/src/com/jcraft/jsch/Channel.java b/src/com/jcraft/jsch/Channel.java
new file mode 100644 (file)
index 0000000..0e58bcf
--- /dev/null
@@ -0,0 +1,641 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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;
+  int recipient=-1;
+  byte[] type=Util.str2byte("foo");
+  int lwsize_max=0x100000;
+//int lwsize_max=0x20000;  // 32*1024*4
+  int lwsize=lwsize_max;  // local initial window size
+  int lmpsize=0x4000;     // local maximum packet size
+//int lmpsize=0x8000;     // local maximum packet size
+
+  long rwsize=0;         // remote initial window size
+  int rmpsize=0;        // remote maximum packet size
+
+  IO io=null;    
+  Thread thread=null;
+
+  boolean eof_local=false;
+  boolean eof_remote=false;
+
+  boolean close=false;
+  boolean connected=false;
+
+  int exitstatus=-1;
+
+  int reply=0; 
+  int connectTimeout=0;
+
+  private Session session;
+
+  int notifyme=0; 
+
+  Channel(){
+    synchronized(pool){
+      id=index++;
+      pool.addElement(this);
+    }
+  }
+  void setRecipient(int foo){
+    this.recipient=foo;
+  }
+  int getRecipient(){
+    return recipient;
+  }
+
+  void init() throws JSchException {
+  }
+
+  public void connect() throws JSchException{
+    connect(0);
+  }
+
+  public void connect(int connectTimeout) throws JSchException{
+    Session _session=getSession();
+    if(!_session.isConnected()){
+      throw new JSchException("session is down");
+    }
+    this.connectTimeout=connectTimeout;
+    try{
+      Buffer buf=new Buffer(100);
+      Packet packet=new Packet(buf);
+      // send
+      // 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);
+      _session.write(packet);
+      int retry=1000;
+      long start=System.currentTimeMillis();
+      long timeout=connectTimeout;
+      while(this.getRecipient()==-1 &&
+           _session.isConnected() &&
+           retry>0){
+        if(timeout>0L){
+          if((System.currentTimeMillis()-start)>timeout){
+            retry=0;
+            continue;
+          }
+        }
+       try{Thread.sleep(50);}catch(Exception ee){}
+       retry--;
+      }
+      if(!_session.isConnected()){
+       throw new JSchException("session is down");
+      }
+      if(retry==0){
+        throw new JSchException("channel is not opened.");
+      }
+
+      /*
+       * At the failure in opening the channel on the sshd, 
+       * 'SSH_MSG_CHANNEL_OPEN_FAILURE' will be sent from sshd and it will
+       * be processed in Session#run().
+       */
+      if(this.isClosed()){
+        throw new JSchException("channel is not opened.");
+      }
+      connected=true;
+      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)-32-20<=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)-32-20){
+              _l=_bufl-(14+dataLen)-32-20;
+            }
+
+            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;
+            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];
+    }
+  }
+  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){
+    }
+  }
+}
diff --git a/src/com/jcraft/jsch/ChannelAgentForwarding.java b/src/com/jcraft/jsch/ChannelAgentForwarding.java
new file mode 100644 (file)
index 0000000..9fb8cb0
--- /dev/null
@@ -0,0 +1,227 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2010 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 int SSH2_AGENTC_REQUEST_IDENTITIES=11;
+  private final int SSH2_AGENT_IDENTITIES_ANSWER=12;
+  private final int SSH2_AGENTC_SIGN_REQUEST=13;
+  private final int SSH2_AGENT_SIGN_RESPONSE=14;
+  private final int SSH2_AGENTC_ADD_IDENTITY=17;
+  private final int SSH2_AGENTC_REMOVE_IDENTITY=18;
+  private final int SSH2_AGENTC_REMOVE_ALL_IDENTITIES=19;
+  private final int 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());
+    }
+
+    Vector identities=_session.jsch.identities;
+    UserInfo userinfo=_session.getUserInfo();
+
+    if(typ==SSH2_AGENTC_REQUEST_IDENTITIES){ 
+      mbuf.reset();
+      mbuf.putByte((byte)SSH2_AGENT_IDENTITIES_ANSWER);
+      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);
+        }
+      }
+      byte[] bar=new byte[mbuf.getLength()];
+      mbuf.getByte(bar);
+
+      send(bar);
+    }
+    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;
+//      }
+
+      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);
+      }
+
+      mbuf.reset();
+      if(signature==null){
+        mbuf.putByte((byte)SSH2_AGENT_FAILURE);
+      }
+      else{
+        mbuf.putByte((byte)SSH2_AGENT_SIGN_RESPONSE);
+        mbuf.putString(signature);
+      }
+
+      byte[] bar=new byte[mbuf.getLength()];
+      mbuf.getByte(bar);
+
+      send(bar);
+    }
+  }
+
+  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){
+    }
+  }
+}
diff --git a/src/com/jcraft/jsch/ChannelDirectTCPIP.java b/src/com/jcraft/jsch/ChannelDirectTCPIP.java
new file mode 100644 (file)
index 0000000..12ad8d5
--- /dev/null
@@ -0,0 +1,183 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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;
+
+  String host;
+  int port;
+
+  String originator_IP_address="127.0.0.1";
+  int originator_port=0;
+
+  ChannelDirectTCPIP(){
+    super();
+    setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
+    setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
+    setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
+  }
+
+  void init (){
+    try{ 
+      io=new IO();
+    }
+    catch(Exception e){
+      System.err.println(e);
+    }
+  }
+
+  public void connect() throws JSchException{
+    try{
+      Session _session=getSession();
+      if(!_session.isConnected()){
+        throw new JSchException("session is down");
+      }
+      Buffer buf=new Buffer(150);
+      Packet packet=new Packet(buf);
+      // send
+      // 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(Util.str2byte("direct-tcpip"));
+      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);
+      _session.write(packet);
+
+      int retry=1000;
+      try{
+        while(this.getRecipient()==-1 && 
+              _session.isConnected() &&
+              retry>0 &&
+              !eof_remote){
+          //Thread.sleep(500);
+          Thread.sleep(50);
+          retry--;
+        }
+      }
+      catch(Exception ee){
+      }
+      if(!_session.isConnected()){
+       throw new JSchException("session is down");
+      }
+      if(retry==0 || this.eof_remote){
+        throw new JSchException("channel is not opened.");
+      }
+      /*
+      if(this.eof_remote){      // failed to open
+        disconnect();
+        return;
+      }
+      */
+
+      connected=true;
+
+      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(){
+
+    Buffer buf=new Buffer(rmpsize);
+    Packet packet=new Packet(buf);
+    int i=0;
+
+    try{
+      Session _session=getSession();
+      while(isConnected() &&
+            thread!=null && 
+            io!=null && 
+            io.in!=null){
+        i=io.in.read(buf.buffer, 
+                     14, 
+                     buf.buffer.length-14
+                     -32 -20 // padding and mac
+                     );
+
+        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);
+        _session.write(packet, this, i);
+      }
+    }
+    catch(Exception e){
+    }
+    disconnect();
+    //System.err.println("connect end");
+  }
+
+  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;}
+}
diff --git a/src/com/jcraft/jsch/ChannelExec.java b/src/com/jcraft/jsch/ChannelExec.java
new file mode 100644 (file)
index 0000000..df8869c
--- /dev/null
@@ -0,0 +1,83 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/ChannelForwardedTCPIP.java b/src/com/jcraft/jsch/ChannelForwardedTCPIP.java
new file mode 100644 (file)
index 0000000..faafa17
--- /dev/null
@@ -0,0 +1,311 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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{
+      while(thread!=null && 
+            io!=null && 
+            io.in!=null){
+        i=io.in.read(buf.buffer, 
+                     14, 
+                     buf.buffer.length-14
+                     -32 -20 // padding and mac
+                     );
+        if(i<=0){
+          eof();
+          break;
+        }
+        packet.reset();
+        if(close)break;
+        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);
+    }
+    //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/src/com/jcraft/jsch/ChannelSession.java b/src/com/jcraft/jsch/ChannelSession.java
new file mode 100644 (file)
index 0000000..17b7ef2
--- /dev/null
@@ -0,0 +1,276 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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
+                     -32 -20 // padding and mac
+                    );
+       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/src/com/jcraft/jsch/ChannelSftp.java b/src/com/jcraft/jsch/ChannelSftp.java
new file mode 100644 (file)
index 0000000..cea923e
--- /dev/null
@@ -0,0 +1,2447 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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{
+
+  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=new Packet(buf);
+
+  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;
+
+  void init(){
+  }
+
+  public void start() throws JSchException{
+    try{
+
+      PipedOutputStream pos=new PipedOutputStream();
+      io.setOutputStream(pos);
+      PipedInputStream pis=new MyPipedInputStream(pos, 32*1024);
+      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(rmpsize);
+      packet=new Packet(buf);
+      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{
+      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{
+    src=localAbsolutePath(src);
+    dst=remoteAbsolutePath(dst);
+
+    try{
+
+      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{
+      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");
+      }
+
+      _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{
+      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){
+        data=new byte[buf.buffer.length
+                      -(5+13+21+handle.length
+                        +32 +20 // padding and mac
+                        )
+        ];
+      }
+
+      long offset=0;
+      if(mode==RESUME || mode==APPEND){
+       offset+=skip;
+      }
+
+      int startid=seq;
+      int _ackid=seq;
+      int ackcount=0;
+      while(true){
+        int nread=0;
+        int s=0;
+        int datalen=0;
+        int count=0;
+
+        if(!dontcopy){
+          datalen=data.length-s;
+        }
+        else{
+          data=buf.buffer;
+          s=5+13+21+handle.length;
+          datalen=buf.buffer.length -s
+            -32 -20; // padding and mac
+        }
+
+        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 _i=count;
+        while(_i>0){
+          _i-=sendWRITE(handle, offset, data, 0, _i);
+          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){
+                  if(_ackid==seq){
+                    System.err.println("ack error: startid="+startid+" seq="+seq+" _ackid="+_ackid);
+                  } 
+                  else{
+                    //throw new SftpException(SSH_FX_FAILURE, "ack error:");
+                    throw new SftpException(SSH_FX_FAILURE, "ack error: startid="+startid+" seq="+seq+" _ackid="+_ackid);
+                  }
+                }
+                ackcount++;
+              }
+              else{
+                break;
+              }
+            }
+          }
+        }
+        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{
+    dst=remoteAbsolutePath(dst);
+    try{
+
+      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);
+
+    src=remoteAbsolutePath(src);
+    dst=localAbsolutePath(dst);
+
+    try{
+      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);
+        } 
+
+       String _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;
+        }
+
+       if(mode==RESUME){
+         long size_of_src=attr.getSize();
+         long size_of_dst=new File(_dst).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(new File(_dst).length());
+         }
+       }
+
+       FileOutputStream fos=null;
+        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(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{
+      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_len=0;
+      loop:
+      while(true){
+
+        request_len=buf.buffer.length-13;
+        if(server_version==0){ request_len=1024; }
+        sendREAD(handle, offset, request_len);
+
+        header=header(buf, header);
+        length=header.length;
+        type=header.type;
+
+        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 i=buf.getInt();   // length of data 
+        int foo=i;
+
+        while(foo>0){
+          int bar=foo;
+          if(bar>buf.buffer.length){
+            bar=buf.buffer.length;
+          }
+          i=io_in.read(buf.buffer, 0, bar);
+          if(i<0){
+            break loop;
+         }
+          int data_len=i;
+          dst.write(buf.buffer, 0, data_len);
+
+          offset+=data_len;
+          foo-=data_len;
+
+          if(monitor!=null){
+            if(!monitor.count(data_len)){
+              while(foo>0){
+                i=io_in.read(buf.buffer, 
+                             0, 
+                             (buf.buffer.length<foo?buf.buffer.length:foo));
+                if(i<=0) break;
+                foo-=i;
+              }
+              break loop;
+            }
+          }
+
+        }
+       //System.err.println("length: "+length);  // length should be 0
+      }
+      dst.flush();
+
+      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, "", (Throwable)e);
+      throw new SftpException(SSH_FX_FAILURE, "");
+    }
+  }
+
+  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{
+    src=remoteAbsolutePath(src);
+    try{
+      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 i=buf.getInt(); rest_length-=4;
+
+             offset+=rest_length;
+             int foo=i;
+             if(foo>0){
+               int bar=rest_length;
+               if(bar>len){
+                 bar=len;
+               }
+               i=io_in.read(d, s, bar);
+               if(i<0){
+                 return -1;
+               }
+               rest_length-=i;
+
+               if(rest_length>0){
+                 if(rest_byte.length<rest_length){
+                   rest_byte=new byte[rest_length];
+                 }
+                 int _s=0;
+                 int _len=rest_length;
+                 int j;
+                 while(_len>0){
+                   j=io_in.read(rest_byte, _s, _len);
+                   if(j<=0)break;
+                   _s+=j;
+                   _len-=j;
+                 }
+               }
+
+               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{
+       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.");
+       }
+
+       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{
+       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{
+       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{
+      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{
+      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{
+      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{
+      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{
+      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{
+      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{
+      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{
+      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{
+      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{
+      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{
+        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;
+    packet.reset();
+    if(buf.buffer.length<buf.index+13+21+handle.length+length
+       +32 +20  // padding and mac
+){
+      _length=buf.buffer.length-(buf.index+13+21+handle.length
+                                 +32 +20  // padding and mac
+);
+      //System.err.println("_length="+_length+" length="+length);
+    }
+
+    putHEAD(SSH_FXP_WRITE, 21+handle.length+_length);       // 14
+    buf.putInt(seq++);                                      //  4
+    buf.putString(handle);                                  //  4+handle.length
+    buf.putLong(offset);                                    //  8
+    if(buf.buffer!=data){
+    buf.putString(data, start, _length);                    //  4+_length
+    }
+    else{
+      buf.putInt(_length);
+      buf.skip(_length);
+    }
+    getSession().write(packet, this, 21+handle.length+_length+4);
+    return _length;
+  }
+
+  private void sendREAD(byte[] handle, long offset, int length) 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);
+  }
+
+  private void putHEAD(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 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 i=path.length-1;
+    while(i>=0){
+      if(path[i]=='*' || path[i]=='?'){
+        if(i>0 && path[i-1]=='\\'){
+          i--;
+          if(i>0 && path[i-1]=='\\'){    // \\* or \\?
+            break;
+          }
+        }
+        else{
+          break;
+        }
+      }
+      i--;
+    }
+    // System.err.println("isPattern: ["+(new String(path))+"] "+(!(i<0)));
+    return !(i<0);
+  }
+
+  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(sversion > 3 && 
+       !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/src/com/jcraft/jsch/ChannelShell.java b/src/com/jcraft/jsch/ChannelShell.java
new file mode 100644 (file)
index 0000000..b956e1d
--- /dev/null
@@ -0,0 +1,70 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/ChannelSubsystem.java b/src/com/jcraft/jsch/ChannelSubsystem.java
new file mode 100644 (file)
index 0000000..e485177
--- /dev/null
@@ -0,0 +1,83 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2005-2010 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=true; }
+  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/src/com/jcraft/jsch/ChannelX11.java b/src/com/jcraft/jsch/ChannelX11.java
new file mode 100644 (file)
index 0000000..e469a73
--- /dev/null
@@ -0,0 +1,268 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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;
+    }
+  }
+
+  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
+                    -32 -20 // padding and mac
+                    );
+       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/src/com/jcraft/jsch/Cipher.java b/src/com/jcraft/jsch/Cipher.java
new file mode 100644 (file)
index 0000000..b7ac84c
--- /dev/null
@@ -0,0 +1,40 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/CipherNone.java b/src/com/jcraft/jsch/CipherNone.java
new file mode 100644 (file)
index 0000000..ff50c5b
--- /dev/null
@@ -0,0 +1,42 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/Compression.java b/src/com/jcraft/jsch/Compression.java
new file mode 100644 (file)
index 0000000..091073c
--- /dev/null
@@ -0,0 +1,38 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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);
+  int compress(byte[] buf, int start, int len);
+  byte[] uncompress(byte[] buf, int start, int[] len);
+}
diff --git a/src/com/jcraft/jsch/DH.java b/src/com/jcraft/jsch/DH.java
new file mode 100644 (file)
index 0000000..8e43021
--- /dev/null
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/DHG1.java b/src/com/jcraft/jsch/DHG1.java
new file mode 100644 (file)
index 0000000..3e8b807
--- /dev/null
@@ -0,0 +1,310 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/DHGEX.java b/src/com/jcraft/jsch/DHGEX.java
new file mode 100644 (file)
index 0000000..79d994a
--- /dev/null
@@ -0,0 +1,340 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/ForwardedTCPIPDaemon.java b/src/com/jcraft/jsch/ForwardedTCPIPDaemon.java
new file mode 100644 (file)
index 0000000..d071a07
--- /dev/null
@@ -0,0 +1,36 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/GSSContext.java b/src/com/jcraft/jsch/GSSContext.java
new file mode 100644 (file)
index 0000000..42edb8d
--- /dev/null
@@ -0,0 +1,38 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2004-2010 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/src/com/jcraft/jsch/HASH.java b/src/com/jcraft/jsch/HASH.java
new file mode 100644 (file)
index 0000000..c29c341
--- /dev/null
@@ -0,0 +1,37 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/HostKey.java b/src/com/jcraft/jsch/HostKey.java
new file mode 100644 (file)
index 0000000..b847045
--- /dev/null
@@ -0,0 +1,104 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/HostKeyRepository.java b/src/com/jcraft/jsch/HostKeyRepository.java
new file mode 100644 (file)
index 0000000..aae6c98
--- /dev/null
@@ -0,0 +1,44 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2004-2010 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/src/com/jcraft/jsch/IO.java b/src/com/jcraft/jsch/IO.java
new file mode 100644 (file)
index 0000000..87ef8ad
--- /dev/null
@@ -0,0 +1,132 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/Identity.java b/src/com/jcraft/jsch/Identity.java
new file mode 100644 (file)
index 0000000..099fabf
--- /dev/null
@@ -0,0 +1,41 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/IdentityFile.java b/src/com/jcraft/jsch/IdentityFile.java
new file mode 100644 (file)
index 0000000..8a43cd5
--- /dev/null
@@ -0,0 +1,886 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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;
+    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]=='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/src/com/jcraft/jsch/JSch.java b/src/com/jcraft/jsch/JSch.java
new file mode 100644 (file)
index 0000000..540fc1e
--- /dev/null
@@ -0,0 +1,296 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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{
+  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-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("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("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");
+  }
+  java.util.Vector pool=new java.util.Vector();
+  java.util.Vector identities=new java.util.Vector();
+  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);
+    //pool.addElement(s);
+    return s;
+  }
+
+  protected void addSession(Session session){
+    synchronized(pool){
+      pool.addElement(session);
+    }
+  }
+
+  protected boolean removeSession(Session session){
+    synchronized(pool){
+      return pool.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);
+      }
+    }
+    synchronized(identities){
+      if(!identities.contains(identity)){
+       identities.addElement(identity);
+      }
+    }
+  }
+
+  public void removeIdentity(String name) throws JSchException{
+    synchronized(identities){
+      for(int i=0; i<identities.size(); i++){
+        Identity identity=(Identity)(identities.elementAt(i));
+       if(!identity.getName().equals(name))
+          continue;
+        identities.removeElement(identity);
+        identity.clear();
+        break;
+      }
+    }
+  }
+
+  public Vector getIdentityNames() throws JSchException{
+    Vector foo=new Vector();
+    synchronized(identities){
+      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{
+    synchronized(identities){
+      Vector foo=getIdentityNames();
+      for(int i=0; i<foo.size(); i++){
+        String name=((String)foo.elementAt(i));
+        removeIdentity(name);
+      }
+    }
+  }
+
+  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) JSch.logger=DEVNULL;
+    JSch.logger=logger;
+  }
+  static Logger getLogger(){
+    return logger;
+  }
+}
diff --git a/src/com/jcraft/jsch/JSchAuthCancelException.java b/src/com/jcraft/jsch/JSchAuthCancelException.java
new file mode 100644 (file)
index 0000000..13a5e8d
--- /dev/null
@@ -0,0 +1,45 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/JSchException.java b/src/com/jcraft/jsch/JSchException.java
new file mode 100644 (file)
index 0000000..5119fc6
--- /dev/null
@@ -0,0 +1,48 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/JSchPartialAuthException.java b/src/com/jcraft/jsch/JSchPartialAuthException.java
new file mode 100644 (file)
index 0000000..edae89b
--- /dev/null
@@ -0,0 +1,45 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/KeyExchange.java b/src/com/jcraft/jsch/KeyExchange.java
new file mode 100644 (file)
index 0000000..5be3ac0
--- /dev/null
@@ -0,0 +1,159 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/KeyPair.java b/src/com/jcraft/jsch/KeyPair.java
new file mode 100644 (file)
index 0000000..5d6392a
--- /dev/null
@@ -0,0 +1,665 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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();
+
+  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);
+         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;
+
+    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]=='B'&& 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{
+            //System.err.println("invalid format: "+identity);
+           throw new JSchException("invalid privatekey: "+prvkey);
+         }
+          i+=3;
+         continue;
+       }
+        if(buf[i]=='C'&& 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)); 
+       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[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);
+             }
+           }
+         }
+       }
+       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;
+
+      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/src/com/jcraft/jsch/KeyPairDSA.java b/src/com/jcraft/jsch/KeyPairDSA.java
new file mode 100644 (file)
index 0000000..ad4f112
--- /dev/null
@@ -0,0 +1,221 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/KeyPairGenDSA.java b/src/com/jcraft/jsch/KeyPairGenDSA.java
new file mode 100644 (file)
index 0000000..1656163
--- /dev/null
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/KeyPairGenRSA.java b/src/com/jcraft/jsch/KeyPairGenRSA.java
new file mode 100644 (file)
index 0000000..eb2a5f3
--- /dev/null
@@ -0,0 +1,43 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/KeyPairRSA.java b/src/com/jcraft/jsch/KeyPairRSA.java
new file mode 100644 (file)
index 0000000..9f52f28
--- /dev/null
@@ -0,0 +1,320 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/KnownHosts.java b/src/com/jcraft/jsch/KnownHosts.java
new file mode 100644 (file)
index 0000000..88c9feb
--- /dev/null
@@ -0,0 +1,506 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/Logger.java b/src/com/jcraft/jsch/Logger.java
new file mode 100644 (file)
index 0000000..6b48b7f
--- /dev/null
@@ -0,0 +1,54 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2010 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/src/com/jcraft/jsch/MAC.java b/src/com/jcraft/jsch/MAC.java
new file mode 100644 (file)
index 0000000..14884b4
--- /dev/null
@@ -0,0 +1,39 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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/src/com/jcraft/jsch/Packet.java b/src/com/jcraft/jsch/Packet.java
new file mode 100644 (file)
index 0000000..56a2dd9
--- /dev/null
@@ -0,0 +1,114 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2010 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("");
+*/
+  }
+