name
and value
are needed to be passed
+ * to the remote in your favorite encoding,
+ * use {@link #setEnv(byte[], byte[])}.
+ * Refer to RFC4254 6.4 Environment Variable Passing.
+ *
+ * @param name A name for environment variable.
+ * @param value A value for environment variable.
+ */
+ public void setEnv(String name, String value) {
+ setEnv(Util.str2byte(name), Util.str2byte(value));
+ }
+
+ /**
+ * Set the environment variable.
+ * Refer to RFC4254 6.4 Environment Variable Passing.
+ *
+ * @param name A name of environment variable.
+ * @param value A value of environment variable.
+ * @see #setEnv(String, String)
+ */
+ 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.
+ * Refer to RFC4254 6.2. Requesting a Pseudo-Terminal.
+ *
+ * @param enable
+ */
+ 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.
+ * Refer to RFC4254 6.7. Window Dimension Change Message.
+ *
+ * @param col terminal width, columns
+ * @param row terminal height, rows
+ * @param wp terminal width, pixels
+ * @param hp terminal height, pixels
+ */
+ public void setPtySize(int col, int row, int wp, int hp) {
+ setPtyType(this.ttype, col, row, wp, hp);
+ if (!pty || !isConnected()) {
+ return;
+ }
+ try {
+ RequestWindowChange request = new RequestWindowChange();
+ request.setSize(col, row, wp, hp);
+ request.request(getSession(), this);
+ }
+ catch (Exception e) {
+ //System.err.println("ChannelSessio.setPtySize: "+e);
+ }
+ }
+
+ /**
+ * Set the terminal type.
+ * This method is not effective after Channel#connect().
+ *
+ * @param ttype terminal type(for example, "vt100")
+ * @see #setPtyType(String, int, int, int, int)
+ */
+ public void setPtyType(String ttype) {
+ setPtyType(ttype, 80, 24, 640, 480);
+ }
+
+ /**
+ * Set the terminal type.
+ * This method is not effective after Channel#connect().
+ *
+ * @param ttype terminal type(for example, "vt100")
+ * @param col terminal width, columns
+ * @param row terminal height, rows
+ * @param wp terminal width, pixels
+ * @param hp terminal height, pixels
+ */
+ public void setPtyType(String ttype, int col, int row, int wp, int hp) {
+ this.ttype = ttype;
+ this.tcol = col;
+ this.trow = row;
+ this.twp = wp;
+ this.thp = hp;
+ }
+
+ protected void sendRequests() throws Exception {
+ Session _session = getSession();
+ Request request;
+ if (agent_forwarding) {
+ request = new RequestAgentForwarding();
+ request.request(_session, this);
+ }
+
+ if (xforwading) {
+ request = new RequestX11();
+ request.request(_session, this);
+ }
+
+ if (pty) {
+ request = new RequestPtyReq();
+ ((RequestPtyReq) request).setTType(ttype);
+ ((RequestPtyReq) request).setTSize(tcol, trow, twp, thp);
+ if (terminal_mode != null) {
+ ((RequestPtyReq) request).setTerminalMode(terminal_mode);
+ }
+ request.request(_session, this);
+ }
+
+ if (env != null) {
+ for (Enumeration _env = env.keys(); _env.hasMoreElements(); ) {
+ Object name = _env.nextElement();
+ Object value = env.get(name);
+ request = new RequestEnv();
+ ((RequestEnv) request).setEnv(toByteArray(name),
+ toByteArray(value));
+ request.request(_session, this);
+ }
+ }
+ }
+
+ private byte[] toByteArray(Object o) {
+ if (o instanceof String) {
+ return Util.str2byte((String) o);
+ }
+ return (byte[]) o;
+ }
+
+ public void run() {
+ //System.err.println(this+":run >");
+
+ Buffer buf = new Buffer(rmpsize);
+ Packet packet = new Packet(buf);
+ int i = -1;
+ try {
+ while (isConnected() &&
+ thread != null &&
+ io != null &&
+ io.in != null) {
+ i = io.in.read(buf.buffer,
+ 14,
+ buf.buffer.length - 14
+ - Session.buffer_margin
+ );
+ if (i == 0) continue;
+ if (i == -1) {
+ eof();
+ break;
+ }
+ if (close) break;
+ //System.out.println("write: "+i);
+ packet.reset();
+ buf.putByte((byte) Session.SSH_MSG_CHANNEL_DATA);
+ buf.putInt(recipient);
+ buf.putInt(i);
+ buf.skip(i);
+ getSession().write(packet, this, i);
+ }
+ }
+ catch (Exception e) {
+ //System.err.println("# ChannelExec.run");
+ //e.printStackTrace();
+ }
+ Thread _thread = thread;
+ if (_thread != null) {
+ synchronized (_thread) {
+ _thread.notifyAll();
+ }
+ }
+ thread = null;
+ //System.err.println(this+":run <");
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/ChannelSftp.java b/src/main/java/com/jcraft/jsch/ChannelSftp.java
new file mode 100644
index 0000000..542a670
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/ChannelSftp.java
@@ -0,0 +1,3205 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.io.*;
+import java.util.Vector;
+
+public class ChannelSftp extends ChannelSession {
+
+ static private final int LOCAL_MAXIMUM_PACKET_SIZE = 32 * 1024;
+ static private final int LOCAL_WINDOW_SIZE_MAX = (64 * LOCAL_MAXIMUM_PACKET_SIZE);
+
+ private static final byte SSH_FXP_INIT = 1;
+ private static final byte SSH_FXP_VERSION = 2;
+ private static final byte SSH_FXP_OPEN = 3;
+ private static final byte SSH_FXP_CLOSE = 4;
+ private static final byte SSH_FXP_READ = 5;
+ private static final byte SSH_FXP_WRITE = 6;
+ private static final byte SSH_FXP_LSTAT = 7;
+ private static final byte SSH_FXP_FSTAT = 8;
+ private static final byte SSH_FXP_SETSTAT = 9;
+ private static final byte SSH_FXP_FSETSTAT = 10;
+ private static final byte SSH_FXP_OPENDIR = 11;
+ private static final byte SSH_FXP_READDIR = 12;
+ private static final byte SSH_FXP_REMOVE = 13;
+ private static final byte SSH_FXP_MKDIR = 14;
+ private static final byte SSH_FXP_RMDIR = 15;
+ private static final byte SSH_FXP_REALPATH = 16;
+ private static final byte SSH_FXP_STAT = 17;
+ private static final byte SSH_FXP_RENAME = 18;
+ private static final byte SSH_FXP_READLINK = 19;
+ private static final byte SSH_FXP_SYMLINK = 20;
+ private static final byte SSH_FXP_STATUS = 101;
+ private static final byte SSH_FXP_HANDLE = 102;
+ private static final byte SSH_FXP_DATA = 103;
+ private static final byte SSH_FXP_NAME = 104;
+ private static final byte SSH_FXP_ATTRS = 105;
+ private static final byte SSH_FXP_EXTENDED = (byte) 200;
+ private static final byte SSH_FXP_EXTENDED_REPLY = (byte) 201;
+
+ // pflags
+ private static final int SSH_FXF_READ = 0x00000001;
+ private static final int SSH_FXF_WRITE = 0x00000002;
+ private static final int SSH_FXF_APPEND = 0x00000004;
+ private static final int SSH_FXF_CREAT = 0x00000008;
+ private static final int SSH_FXF_TRUNC = 0x00000010;
+ private static final int SSH_FXF_EXCL = 0x00000020;
+
+ private static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
+ private static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
+ private static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
+ private static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008;
+ private static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
+
+ public static final int SSH_FX_OK = 0;
+ public static final int SSH_FX_EOF = 1;
+ public static final int SSH_FX_NO_SUCH_FILE = 2;
+ public static final int SSH_FX_PERMISSION_DENIED = 3;
+ public static final int SSH_FX_FAILURE = 4;
+ public static final int SSH_FX_BAD_MESSAGE = 5;
+ public static final int SSH_FX_NO_CONNECTION = 6;
+ public static final int SSH_FX_CONNECTION_LOST = 7;
+ public static final int SSH_FX_OP_UNSUPPORTED = 8;
+ /*
+ SSH_FX_OK
+ Indicates successful completion of the operation.
+ SSH_FX_EOF
+ indicates end-of-file condition; for SSH_FX_READ it means that no
+ more data is available in the file, and for SSH_FX_READDIR it
+ indicates that no more files are contained in the directory.
+ SSH_FX_NO_SUCH_FILE
+ is returned when a reference is made to a file which should exist
+ but doesn't.
+ SSH_FX_PERMISSION_DENIED
+ is returned when the authenticated user does not have sufficient
+ permissions to perform the operation.
+ SSH_FX_FAILURE
+ is a generic catch-all error message; it should be returned if an
+ error occurs for which there is no more specific error code
+ defined.
+ SSH_FX_BAD_MESSAGE
+ may be returned if a badly formatted packet or protocol
+ incompatibility is detected.
+ SSH_FX_NO_CONNECTION
+ is a pseudo-error which indicates that the client has no
+ connection to the server (it can only be generated locally by the
+ client, and MUST NOT be returned by servers).
+ SSH_FX_CONNECTION_LOST
+ is a pseudo-error which indicates that the connection to the
+ server has been lost (it can only be generated locally by the
+ client, and MUST NOT be returned by servers).
+ SSH_FX_OP_UNSUPPORTED
+ indicates that an attempt was made to perform an operation which
+ is not supported for the server (it may be generated locally by
+ the client if e.g. the version number exchange indicates that a
+ required feature is not supported by the server, or it may be
+ returned by the server if the server does not implement an
+ operation).
+*/
+ private static final int MAX_MSG_LENGTH = 256 * 1024;
+
+ public static final int OVERWRITE = 0;
+ public static final int RESUME = 1;
+ public static final int APPEND = 2;
+
+ private boolean interactive = false;
+ private int seq = 1;
+ private int[] ackid = new int[1];
+
+ private Buffer buf;
+ private Packet packet;
+
+ // The followings will be used in file uploading.
+ private Buffer obuf;
+ private Packet opacket;
+
+ private int client_version = 3;
+ private int server_version = 3;
+ private String version = String.valueOf(client_version);
+
+ private java.util.Hashtable extensions = null;
+ private InputStream io_in = null;
+
+ private boolean extension_posix_rename = false;
+ private boolean extension_statvfs = false;
+ // private boolean extension_fstatvfs = false;
+ private boolean extension_hardlink = false;
+
+/*
+10. Changes from previous protocol versions
+ The SSH File Transfer Protocol has changed over time, before it's
+ standardization. The following is a description of the incompatible
+ changes between different versions.
+10.1 Changes between versions 3 and 2
+ o The SSH_FXP_READLINK and SSH_FXP_SYMLINK messages were added.
+ o The SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY messages were added.
+ o The SSH_FXP_STATUS message was changed to include fields `error
+ message' and `language tag'.
+10.2 Changes between versions 2 and 1
+ o The SSH_FXP_RENAME message was added.
+10.3 Changes between versions 1 and 0
+ o Implementation changes, no actual protocol changes.
+*/
+
+ private static final String file_separator = java.io.File.separator;
+ private static final char file_separatorc = java.io.File.separatorChar;
+ private static boolean fs_is_bs = (byte) java.io.File.separatorChar == '\\';
+
+ private String cwd;
+ private String home;
+ private String lcwd;
+
+ private static final String UTF8 = "UTF-8";
+ private String fEncoding = UTF8;
+ private boolean fEncoding_is_utf8 = true;
+
+ private RequestQueue rq = new RequestQueue(16);
+
+ /**
+ * Specify how many requests may be sent at any one time.
+ * Increasing this value may slightly improve file transfer speed but will
+ * increase memory usage. The default is 16 requests.
+ *
+ * @param bulk_requests how many requests may be outstanding at any one time.
+ */
+ public void setBulkRequests(int bulk_requests) throws JSchException {
+ if (bulk_requests > 0)
+ rq = new RequestQueue(bulk_requests);
+ else
+ throw new JSchException("setBulkRequests: " +
+ bulk_requests + " must be greater than 0.");
+ }
+
+ /**
+ * This method will return the value how many requests may be
+ * sent at any one time.
+ *
+ * @return how many requests may be sent at any one time.
+ */
+ public int getBulkRequests() {
+ return rq.size();
+ }
+
+ public ChannelSftp() {
+ super();
+ setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
+ setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
+ setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
+ }
+
+ void init() {
+ }
+
+ public void start() throws JSchException {
+ try {
+
+ PipedOutputStream pos = new PipedOutputStream();
+ io.setOutputStream(pos);
+ PipedInputStream pis = new MyPipedInputStream(pos, rmpsize);
+ io.setInputStream(pis);
+
+ io_in = io.in;
+
+ if (io_in == null) {
+ throw new JSchException("channel is down");
+ }
+
+ Request request = new RequestSftp();
+ request.request(getSession(), this);
+
+ /*
+ System.err.println("lmpsize: "+lmpsize);
+ System.err.println("lwsize: "+lwsize);
+ System.err.println("rmpsize: "+rmpsize);
+ System.err.println("rwsize: "+rwsize);
+ */
+
+ buf = new Buffer(lmpsize);
+ packet = new Packet(buf);
+
+ obuf = new Buffer(rmpsize);
+ opacket = new Packet(obuf);
+
+ int i = 0;
+ int length;
+ int type;
+ byte[] str;
+
+ // send SSH_FXP_INIT
+ sendINIT();
+
+ // receive SSH_FXP_VERSION
+ Header header = new Header();
+ header = header(buf, header);
+ length = header.length;
+ if (length > MAX_MSG_LENGTH) {
+ throw new SftpException(SSH_FX_FAILURE,
+ "Received message is too long: " + length);
+ }
+ type = header.type; // 2 -> SSH_FXP_VERSION
+ server_version = header.rid;
+ //System.err.println("SFTP protocol server-version="+server_version);
+ extensions = new java.util.Hashtable();
+ if (length > 0) {
+ // 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));
+ }
+ }
+
+ if (extensions.get("posix-rename@openssh.com") != null &&
+ extensions.get("posix-rename@openssh.com").equals("1")) {
+ extension_posix_rename = true;
+ }
+
+ if (extensions.get("statvfs@openssh.com") != null &&
+ extensions.get("statvfs@openssh.com").equals("2")) {
+ extension_statvfs = true;
+ }
+
+ /*
+ if(extensions.get("fstatvfs@openssh.com")!=null &&
+ extensions.get("fstatvfs@openssh.com").equals("2")){
+ extension_fstatvfs = true;
+ }
+ */
+
+ if (extensions.get("hardlink@openssh.com") != null &&
+ extensions.get("hardlink@openssh.com").equals("1")) {
+ extension_hardlink = true;
+ }
+
+ lcwd = new File(".").getCanonicalPath();
+ }
+ catch (Exception e) {
+ //System.err.println(e);
+ if (e instanceof JSchException) throw (JSchException) e;
+ if (e instanceof Throwable)
+ throw new JSchException(e.toString(), (Throwable) e);
+ throw new JSchException(e.toString());
+ }
+ }
+
+ public void quit() {
+ disconnect();
+ }
+
+ public void exit() {
+ disconnect();
+ }
+
+ public void lcd(String path) throws SftpException {
+ path = localAbsolutePath(path);
+ if ((new File(path)).isDirectory()) {
+ try {
+ path = (new File(path)).getCanonicalPath();
+ }
+ catch (Exception e) {
+ }
+ lcwd = path;
+ return;
+ }
+ throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such directory");
+ }
+
+ public void cd(String path) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+ path = isUnique(path);
+
+ byte[] str = _realpath(path);
+ SftpATTRS attr = _stat(str);
+
+ if ((attr.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS) == 0) {
+ throw new SftpException(SSH_FX_FAILURE,
+ "Can't change directory: " + path);
+ }
+ if (!attr.isDir()) {
+ throw new SftpException(SSH_FX_FAILURE,
+ "Can't change directory: " + path);
+ }
+
+ setCwd(Util.byte2str(str, fEncoding));
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public void put(String src, String dst) throws SftpException {
+ put(src, dst, null, OVERWRITE);
+ }
+
+ public void put(String src, String dst, int mode) throws SftpException {
+ put(src, dst, null, mode);
+ }
+
+ public void put(String src, String dst,
+ SftpProgressMonitor monitor) throws SftpException {
+ put(src, dst, monitor, OVERWRITE);
+ }
+
+ /**
+ * Sends data from src
file to dst
file.
+ * The mode
should be OVERWRITE
,
+ * RESUME
or APPEND
.
+ *
+ * @param src source file
+ * @param dst destination file
+ * @param monitor progress monitor
+ * @param mode how data should be added to dst
+ */
+ public void put(String src, String dst,
+ SftpProgressMonitor monitor, int mode) throws SftpException {
+
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ src = localAbsolutePath(src);
+ dst = remoteAbsolutePath(dst);
+
+ Vector v = glob_remote(dst);
+ int vsize = v.size();
+ if (vsize != 1) {
+ if (vsize == 0) {
+ if (isPattern(dst))
+ throw new SftpException(SSH_FX_FAILURE, dst);
+ else
+ dst = Util.unquote(dst);
+ }
+ throw new SftpException(SSH_FX_FAILURE, v.toString());
+ }
+ else {
+ dst = (String) (v.elementAt(0));
+ }
+
+ boolean isRemoteDir = isRemoteDir(dst);
+
+ v = glob_local(src);
+ vsize = v.size();
+
+ StringBuffer dstsb = null;
+ if (isRemoteDir) {
+ if (!dst.endsWith("/")) {
+ dst += "/";
+ }
+ dstsb = new StringBuffer(dst);
+ }
+ else if (vsize > 1) {
+ throw new SftpException(SSH_FX_FAILURE,
+ "Copying multiple files, but the destination is missing or a file.");
+ }
+
+ for (int j = 0; j < vsize; j++) {
+ String _src = (String) (v.elementAt(j));
+ String _dst = null;
+ if (isRemoteDir) {
+ int i = _src.lastIndexOf(file_separatorc);
+ if (fs_is_bs) {
+ int ii = _src.lastIndexOf('/');
+ if (ii != -1 && ii > i)
+ i = ii;
+ }
+ if (i == -1) dstsb.append(_src);
+ else dstsb.append(_src.substring(i + 1));
+ _dst = dstsb.toString();
+ dstsb.delete(dst.length(), _dst.length());
+ }
+ else {
+ _dst = dst;
+ }
+ //System.err.println("_dst "+_dst);
+
+ long size_of_dst = 0;
+ if (mode == RESUME) {
+ try {
+ SftpATTRS attr = _stat(_dst);
+ size_of_dst = attr.getSize();
+ }
+ catch (Exception eee) {
+ //System.err.println(eee);
+ }
+ long size_of_src = new File(_src).length();
+ if (size_of_src < size_of_dst) {
+ throw new SftpException(SSH_FX_FAILURE,
+ "failed to resume for " + _dst);
+ }
+ if (size_of_src == size_of_dst) {
+ return;
+ }
+ }
+
+ if (monitor != null) {
+ monitor.init(SftpProgressMonitor.PUT, _src, _dst,
+ (new File(_src)).length());
+ if (mode == RESUME) {
+ monitor.count(size_of_dst);
+ }
+ }
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(_src);
+ _put(fis, _dst, monitor, mode);
+ }
+ finally {
+ if (fis != null) {
+ fis.close();
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, e.toString());
+ }
+ }
+
+ public void put(InputStream src, String dst) throws SftpException {
+ put(src, dst, null, OVERWRITE);
+ }
+
+ public void put(InputStream src, String dst, int mode) throws SftpException {
+ put(src, dst, null, mode);
+ }
+
+ public void put(InputStream src, String dst,
+ SftpProgressMonitor monitor) throws SftpException {
+ put(src, dst, monitor, OVERWRITE);
+ }
+
+ /**
+ * Sends data from the input stream src
to dst
file.
+ * The mode
should be OVERWRITE
,
+ * RESUME
or APPEND
.
+ *
+ * @param src input stream
+ * @param dst destination file
+ * @param monitor progress monitor
+ * @param mode how data should be added to dst
+ */
+ public void put(InputStream src, String dst,
+ SftpProgressMonitor monitor, int mode) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ dst = remoteAbsolutePath(dst);
+
+ Vector v = glob_remote(dst);
+ int vsize = v.size();
+ if (vsize != 1) {
+ if (vsize == 0) {
+ if (isPattern(dst))
+ throw new SftpException(SSH_FX_FAILURE, dst);
+ else
+ dst = Util.unquote(dst);
+ }
+ throw new SftpException(SSH_FX_FAILURE, v.toString());
+ }
+ else {
+ dst = (String) (v.elementAt(0));
+ }
+
+ if (monitor != null) {
+ monitor.init(SftpProgressMonitor.PUT,
+ "-", dst,
+ SftpProgressMonitor.UNKNOWN_SIZE);
+ }
+
+ _put(src, dst, monitor, mode);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) {
+ if (((SftpException) e).id == SSH_FX_FAILURE &&
+ isRemoteDir(dst)) {
+ throw new SftpException(SSH_FX_FAILURE, dst + " is a directory");
+ }
+ throw (SftpException) e;
+ }
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, e.toString());
+ }
+ }
+
+ public void _put(InputStream src, String dst,
+ SftpProgressMonitor monitor, int mode) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ byte[] dstb = Util.str2byte(dst, fEncoding);
+ long skip = 0;
+ if (mode == RESUME || mode == APPEND) {
+ try {
+ SftpATTRS attr = _stat(dstb);
+ skip = attr.getSize();
+ }
+ catch (Exception eee) {
+ //System.err.println(eee);
+ }
+ }
+ if (mode == RESUME && skip > 0) {
+ long skipped = src.skip(skip);
+ if (skipped < skip) {
+ throw new SftpException(SSH_FX_FAILURE, "failed to resume for " + dst);
+ }
+ }
+
+ if (mode == OVERWRITE) {
+ sendOPENW(dstb);
+ }
+ else {
+ sendOPENA(dstb);
+ }
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) {
+ throw new SftpException(SSH_FX_FAILURE, "invalid type=" + type);
+ }
+ if (type == SSH_FXP_STATUS) {
+ int i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+ byte[] handle = buf.getString(); // handle
+ byte[] data = null;
+
+ boolean dontcopy = true;
+
+ if (!dontcopy) { // This case will not work anymore.
+ data = new byte[obuf.buffer.length
+ - (5 + 13 + 21 + handle.length + Session.buffer_margin
+ )
+ ];
+ }
+
+ long offset = 0;
+ if (mode == RESUME || mode == APPEND) {
+ offset += skip;
+ }
+
+ int startid = seq;
+ int ackcount = 0;
+ int _s = 0;
+ int _datalen = 0;
+
+ if (!dontcopy) { // This case will not work anymore.
+ _datalen = data.length;
+ }
+ else {
+ data = obuf.buffer;
+ _s = 5 + 13 + 21 + handle.length;
+ _datalen = obuf.buffer.length - _s - Session.buffer_margin;
+ }
+
+ int bulk_requests = rq.size();
+
+ while (true) {
+ int nread = 0;
+ int count = 0;
+ int s = _s;
+ int datalen = _datalen;
+
+ do {
+ nread = src.read(data, s, datalen);
+ if (nread > 0) {
+ s += nread;
+ datalen -= nread;
+ count += nread;
+ }
+ }
+ while (datalen > 0 && nread > 0);
+ if (count <= 0) break;
+
+ int foo = count;
+ while (foo > 0) {
+ if ((seq - 1) == startid ||
+ ((seq - startid) - ackcount) >= bulk_requests) {
+ while (((seq - startid) - ackcount) >= bulk_requests) {
+ if (checkStatus(ackid, header)) {
+ int _ackid = ackid[0];
+ if (startid > _ackid || _ackid > seq - 1) {
+ if (_ackid == seq) {
+ System.err.println("ack error: startid=" + startid + " seq=" + seq + " _ackid=" + _ackid);
+ }
+ else {
+ throw new SftpException(SSH_FX_FAILURE, "ack error: startid=" + startid + " seq=" + seq + " " +
+ "_ackid=" + _ackid);
+ }
+ }
+ ackcount++;
+ }
+ else {
+ break;
+ }
+ }
+ }
+ if (dontcopy) {
+ foo -= sendWRITE(handle, offset, data, 0, foo);
+ if (data != obuf.buffer) {
+ data = obuf.buffer;
+ _datalen = obuf.buffer.length - _s - Session.buffer_margin;
+ }
+ }
+ else {
+ foo -= sendWRITE(handle, offset, data, _s, foo);
+ }
+ }
+ offset += count;
+ if (monitor != null && !monitor.count(count)) {
+ break;
+ }
+ }
+ int _ackcount = seq - startid;
+ while (_ackcount > ackcount) {
+ if (!checkStatus(null, header)) {
+ break;
+ }
+ ackcount++;
+ }
+ if (monitor != null) monitor.end();
+ _sendCLOSE(handle, header);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, e.toString());
+ }
+ }
+
+ public OutputStream put(String dst) throws SftpException {
+ return put(dst, (SftpProgressMonitor) null, OVERWRITE);
+ }
+
+ public OutputStream put(String dst, final int mode) throws SftpException {
+ return put(dst, (SftpProgressMonitor) null, mode);
+ }
+
+ public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode) throws SftpException {
+ return put(dst, monitor, mode, 0);
+ }
+
+ /**
+ * Sends data from the output stream to dst
file.
+ * The mode
should be OVERWRITE
,
+ * RESUME
or APPEND
.
+ *
+ * @param dst destination file
+ * @param monitor progress monitor
+ * @param mode how data should be added to dst
+ * @param offset data will be added at offset
+ * @return output stream, which accepts data to be transferred.
+ */
+ public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode, long offset) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ dst = remoteAbsolutePath(dst);
+ dst = isUnique(dst);
+
+ if (isRemoteDir(dst)) {
+ throw new SftpException(SSH_FX_FAILURE, dst + " is a directory");
+ }
+
+ byte[] dstb = Util.str2byte(dst, fEncoding);
+
+ long skip = 0;
+ if (mode == RESUME || mode == APPEND) {
+ try {
+ SftpATTRS attr = _stat(dstb);
+ skip = attr.getSize();
+ }
+ catch (Exception eee) {
+ //System.err.println(eee);
+ }
+ }
+
+ if (monitor != null) {
+ monitor.init(SftpProgressMonitor.PUT,
+ "-", dst,
+ SftpProgressMonitor.UNKNOWN_SIZE);
+ }
+
+ if (mode == OVERWRITE) {
+ sendOPENW(dstb);
+ }
+ else {
+ sendOPENA(dstb);
+ }
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ if (type == SSH_FXP_STATUS) {
+ int i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+ final byte[] handle = buf.getString(); // handle
+
+ if (mode == RESUME || mode == APPEND) {
+ offset += skip;
+ }
+
+ final long[] _offset = new long[1];
+ _offset[0] = offset;
+ OutputStream out = new OutputStream() {
+ private boolean init = true;
+ private boolean isClosed = false;
+ private int[] ackid = new int[1];
+ private int startid = 0;
+ private int _ackid = 0;
+ private int ackcount = 0;
+ private int writecount = 0;
+ private Header header = new Header();
+
+ public void write(byte[] d) throws java.io.IOException {
+ write(d, 0, d.length);
+ }
+
+ public void write(byte[] d, int s, int len) throws java.io.IOException {
+ if (init) {
+ startid = seq;
+ _ackid = seq;
+ init = false;
+ }
+
+ if (isClosed) {
+ throw new IOException("stream already closed");
+ }
+
+ try {
+ int _len = len;
+ while (_len > 0) {
+ int sent = sendWRITE(handle, _offset[0], d, s, _len);
+ writecount++;
+ _offset[0] += sent;
+ s += sent;
+ _len -= sent;
+ if ((seq - 1) == startid ||
+ io_in.available() >= 1024) {
+ while (io_in.available() > 0) {
+ if (checkStatus(ackid, header)) {
+ _ackid = ackid[0];
+ if (startid > _ackid || _ackid > seq - 1) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ ackcount++;
+ }
+ else {
+ break;
+ }
+ }
+ }
+ }
+ if (monitor != null && !monitor.count(len)) {
+ close();
+ throw new IOException("canceled");
+ }
+ }
+ catch (IOException e) {
+ throw e;
+ }
+ catch (Exception e) {
+ throw new IOException(e.toString());
+ }
+ }
+
+ byte[] _data = new byte[1];
+
+ public void write(int foo) throws java.io.IOException {
+ _data[0] = (byte) foo;
+ write(_data, 0, 1);
+ }
+
+ public void flush() throws java.io.IOException {
+
+ if (isClosed) {
+ throw new IOException("stream already closed");
+ }
+
+ if (!init) {
+ try {
+ while (writecount > ackcount) {
+ if (!checkStatus(null, header)) {
+ break;
+ }
+ ackcount++;
+ }
+ }
+ catch (SftpException e) {
+ throw new IOException(e.toString());
+ }
+ }
+ }
+
+ public void close() throws java.io.IOException {
+ if (isClosed) {
+ return;
+ }
+ flush();
+ if (monitor != null) monitor.end();
+ try {
+ _sendCLOSE(handle, header);
+ }
+ catch (IOException e) {
+ throw e;
+ }
+ catch (Exception e) {
+ throw new IOException(e.toString());
+ }
+ isClosed = true;
+ }
+ };
+ return out;
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public void get(String src, String dst) throws SftpException {
+ get(src, dst, null, OVERWRITE);
+ }
+
+ public void get(String src, String dst,
+ SftpProgressMonitor monitor) throws SftpException {
+ get(src, dst, monitor, OVERWRITE);
+ }
+
+ public void get(String src, String dst,
+ SftpProgressMonitor monitor, int mode) throws SftpException {
+ // System.out.println("get: "+src+" "+dst);
+
+ boolean _dstExist = false;
+ String _dst = null;
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ src = remoteAbsolutePath(src);
+ dst = localAbsolutePath(dst);
+
+ Vector v = glob_remote(src);
+ int vsize = v.size();
+ if (vsize == 0) {
+ throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such file");
+ }
+
+ File dstFile = new File(dst);
+ boolean isDstDir = dstFile.isDirectory();
+ StringBuffer dstsb = null;
+ if (isDstDir) {
+ if (!dst.endsWith(file_separator)) {
+ dst += file_separator;
+ }
+ dstsb = new StringBuffer(dst);
+ }
+ else if (vsize > 1) {
+ throw new SftpException(SSH_FX_FAILURE,
+ "Copying multiple files, but destination is missing or a file.");
+ }
+
+ for (int j = 0; j < vsize; j++) {
+ String _src = (String) (v.elementAt(j));
+ SftpATTRS attr = _stat(_src);
+ if (attr.isDir()) {
+ throw new SftpException(SSH_FX_FAILURE,
+ "not supported to get directory " + _src);
+ }
+
+ _dst = null;
+ if (isDstDir) {
+ int i = _src.lastIndexOf('/');
+ if (i == -1) dstsb.append(_src);
+ else dstsb.append(_src.substring(i + 1));
+ _dst = dstsb.toString();
+ if (_dst.indexOf("..") != -1) {
+ String dstc = (new java.io.File(dst)).getCanonicalPath();
+ String _dstc = (new java.io.File(_dst)).getCanonicalPath();
+ if (!(_dstc.length() > dstc.length() &&
+ _dstc.substring(0, dstc.length() + 1).equals(dstc + file_separator))) {
+ throw new SftpException(SSH_FX_FAILURE,
+ "writing to an unexpected file " + _src);
+ }
+ }
+ dstsb.delete(dst.length(), _dst.length());
+ }
+ else {
+ _dst = dst;
+ }
+
+ File _dstFile = new File(_dst);
+ if (mode == RESUME) {
+ long size_of_src = attr.getSize();
+ long size_of_dst = _dstFile.length();
+ if (size_of_dst > size_of_src) {
+ throw new SftpException(SSH_FX_FAILURE,
+ "failed to resume for " + _dst);
+ }
+ if (size_of_dst == size_of_src) {
+ return;
+ }
+ }
+
+ if (monitor != null) {
+ monitor.init(SftpProgressMonitor.GET, _src, _dst, attr.getSize());
+ if (mode == RESUME) {
+ monitor.count(_dstFile.length());
+ }
+ }
+
+ FileOutputStream fos = null;
+ _dstExist = _dstFile.exists();
+ try {
+ if (mode == OVERWRITE) {
+ fos = new FileOutputStream(_dst);
+ }
+ else {
+ fos = new FileOutputStream(_dst, true); // append
+ }
+ // System.err.println("_get: "+_src+", "+_dst);
+ _get(_src, fos, monitor, mode, new File(_dst).length());
+ }
+ finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ if (!_dstExist && _dst != null) {
+ File _dstFile = new File(_dst);
+ if (_dstFile.exists() && _dstFile.length() == 0) {
+ _dstFile.delete();
+ }
+ }
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public void get(String src, OutputStream dst) throws SftpException {
+ get(src, dst, null, OVERWRITE, 0);
+ }
+
+ public void get(String src, OutputStream dst,
+ SftpProgressMonitor monitor) throws SftpException {
+ get(src, dst, monitor, OVERWRITE, 0);
+ }
+
+ public void get(String src, OutputStream dst,
+ SftpProgressMonitor monitor, int mode, long skip) throws SftpException {
+//System.err.println("get: "+src+", "+dst);
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ src = remoteAbsolutePath(src);
+ src = isUnique(src);
+
+ if (monitor != null) {
+ SftpATTRS attr = _stat(src);
+ monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize());
+ if (mode == RESUME) {
+ monitor.count(skip);
+ }
+ }
+ _get(src, dst, monitor, mode, skip);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ private void _get(String src, OutputStream dst,
+ SftpProgressMonitor monitor, int mode, long skip) throws SftpException {
+ //System.err.println("_get: "+src+", "+dst);
+
+ byte[] srcb = Util.str2byte(src, fEncoding);
+ try {
+ sendOPENR(srcb);
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+
+ if (type == SSH_FXP_STATUS) {
+ int i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+
+ byte[] handle = buf.getString(); // filename
+
+ long offset = 0;
+ if (mode == RESUME) {
+ offset += skip;
+ }
+
+ int request_max = 1;
+ rq.init();
+ long request_offset = offset;
+
+ int request_len = buf.buffer.length - 13;
+ if (server_version == 0) {
+ request_len = 1024;
+ }
+
+ loop:
+ while (true) {
+
+ while (rq.count() < request_max) {
+ sendREAD(handle, request_offset, request_len, rq);
+ request_offset += request_len;
+ }
+
+ header = header(buf, header);
+ length = header.length;
+ type = header.type;
+
+ RequestQueue.Request rr = null;
+ try {
+ rr = rq.get(header.rid);
+ }
+ catch (RequestQueue.OutOfOrderException e) {
+ request_offset = e.offset;
+ skip(header.length);
+ rq.cancel(header, buf);
+ continue;
+ }
+
+ if (type == SSH_FXP_STATUS) {
+ fill(buf, length);
+ int i = buf.getInt();
+ if (i == SSH_FX_EOF) {
+ break loop;
+ }
+ throwStatusError(buf, i);
+ }
+
+ if (type != SSH_FXP_DATA) {
+ break loop;
+ }
+
+ buf.rewind();
+ fill(buf.buffer, 0, 4);
+ length -= 4;
+ int length_of_data = buf.getInt(); // length of data
+
+ /**
+ Since sftp protocol version 6, "end-of-file" has been defined,
+
+ byte SSH_FXP_DATA
+ uint32 request-id
+ string data
+ bool end-of-file [optional]
+
+ but some sftpd server will send such a field in the sftp protocol 3 ;-(
+ */
+ int optional_data = length - length_of_data;
+
+ int foo = length_of_data;
+ while (foo > 0) {
+ int bar = foo;
+ if (bar > buf.buffer.length) {
+ bar = buf.buffer.length;
+ }
+ int data_len = io_in.read(buf.buffer, 0, bar);
+ if (data_len < 0) {
+ break loop;
+ }
+
+ dst.write(buf.buffer, 0, data_len);
+
+ offset += data_len;
+ foo -= data_len;
+
+ if (monitor != null) {
+ if (!monitor.count(data_len)) {
+ skip(foo);
+ if (optional_data > 0) {
+ skip(optional_data);
+ }
+ break loop;
+ }
+ }
+
+ }
+ //System.err.println("length: "+length); // length should be 0
+
+ if (optional_data > 0) {
+ skip(optional_data);
+ }
+
+ if (length_of_data < rr.length) { //
+ rq.cancel(header, buf);
+ sendREAD(handle, rr.offset + length_of_data, (int) (rr.length - length_of_data), rq);
+ request_offset = rr.offset + rr.length;
+ }
+
+ if (request_max < rq.size()) {
+ request_max++;
+ }
+ }
+ dst.flush();
+
+ if (monitor != null) monitor.end();
+
+ rq.cancel(header, buf);
+
+ _sendCLOSE(handle, header);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+
+ private class RequestQueue {
+ class OutOfOrderException extends Exception {
+ long offset;
+
+ OutOfOrderException(long offset) {
+ this.offset = offset;
+ }
+ }
+
+ class Request {
+ int id;
+ long offset;
+ long length;
+ }
+
+ Request[] rrq = null;
+ int head, count;
+
+ RequestQueue(int size) {
+ rrq = new Request[size];
+ for (int i = 0; i < rrq.length; i++) {
+ rrq[i] = new Request();
+ }
+ init();
+ }
+
+ void init() {
+ head = count = 0;
+ }
+
+ void add(int id, long offset, int length) {
+ if (count == 0) head = 0;
+ int tail = head + count;
+ if (tail >= rrq.length) tail -= rrq.length;
+ rrq[tail].id = id;
+ rrq[tail].offset = offset;
+ rrq[tail].length = length;
+ count++;
+ }
+
+ Request get(int id) throws OutOfOrderException, SftpException {
+ count -= 1;
+ int i = head;
+ head++;
+ if (head == rrq.length) head = 0;
+ if (rrq[i].id != id) {
+ long offset = getOffset();
+ boolean find = false;
+ for (int j = 0; j < rrq.length; j++) {
+ if (rrq[j].id == id) {
+ find = true;
+ rrq[j].id = 0;
+ break;
+ }
+ }
+ if (find)
+ throw new OutOfOrderException(offset);
+ throw new SftpException(SSH_FX_FAILURE,
+ "RequestQueue: unknown request id " + id);
+ }
+ rrq[i].id = 0;
+ return rrq[i];
+ }
+
+ int count() {
+ return count;
+ }
+
+ int size() {
+ return rrq.length;
+ }
+
+ void cancel(Header header, Buffer buf) throws IOException {
+ int _count = count;
+ for (int i = 0; i < _count; i++) {
+ header = header(buf, header);
+ int length = header.length;
+ for (int j = 0; j < rrq.length; j++) {
+ if (rrq[j].id == header.rid) {
+ rrq[j].id = 0;
+ break;
+ }
+ }
+ skip(length);
+ }
+ init();
+ }
+
+ long getOffset() {
+ long result = Long.MAX_VALUE;
+
+ for (int i = 0; i < rrq.length; i++) {
+ if (rrq[i].id == 0)
+ continue;
+ if (result > rrq[i].offset)
+ result = rrq[i].offset;
+ }
+
+ return result;
+ }
+ }
+
+ public InputStream get(String src) throws SftpException {
+ return get(src, null, 0L);
+ }
+
+ public InputStream get(String src, SftpProgressMonitor monitor) throws SftpException {
+ return get(src, monitor, 0L);
+ }
+
+ /**
+ * @deprecated This method will be deleted in the future.
+ */
+ public InputStream get(String src, int mode) throws SftpException {
+ return get(src, null, 0L);
+ }
+
+ /**
+ * @deprecated This method will be deleted in the future.
+ */
+ public InputStream get(String src, final SftpProgressMonitor monitor, final int mode) throws SftpException {
+ return get(src, monitor, 0L);
+ }
+
+ public InputStream get(String src, final SftpProgressMonitor monitor, final long skip) throws SftpException {
+
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ src = remoteAbsolutePath(src);
+ src = isUnique(src);
+
+ byte[] srcb = Util.str2byte(src, fEncoding);
+
+ SftpATTRS attr = _stat(srcb);
+ if (monitor != null) {
+ monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize());
+ }
+
+ sendOPENR(srcb);
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ if (type == SSH_FXP_STATUS) {
+ int i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+
+ final byte[] handle = buf.getString(); // handle
+
+ rq.init();
+
+ 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();
+ int request_max = 1;
+ long request_offset = offset;
+
+ 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;
+ }
+
+ if (rq.count() == 0
+ || true // working around slow transfer speed for
+ // some sftp servers including Titan FTP.
+ ) {
+ int request_len = buf.buffer.length - 13;
+ if (server_version == 0) {
+ request_len = 1024;
+ }
+
+ while (rq.count() < request_max) {
+ try {
+ sendREAD(handle, request_offset, request_len, rq);
+ }
+ catch (Exception e) {
+ throw new IOException("error");
+ }
+ request_offset += request_len;
+ }
+ }
+
+ header = header(buf, header);
+ rest_length = header.length;
+ int type = header.type;
+ int id = header.rid;
+
+ RequestQueue.Request rr = null;
+ try {
+ rr = rq.get(header.rid);
+ }
+ catch (RequestQueue.OutOfOrderException e) {
+ request_offset = e.offset;
+ skip(header.length);
+ rq.cancel(header, buf);
+ return 0;
+ }
+ catch (SftpException e) {
+ throw new IOException("error: " + e.toString());
+ }
+
+ if (type != SSH_FXP_STATUS && type != SSH_FXP_DATA) {
+ throw new IOException("error");
+ }
+ if (type == SSH_FXP_STATUS) {
+ fill(buf, rest_length);
+ int i = buf.getInt();
+ rest_length = 0;
+ if (i == SSH_FX_EOF) {
+ close();
+ return -1;
+ }
+ //throwStatusError(buf, i);
+ throw new IOException("error");
+ }
+
+ buf.rewind();
+ fill(buf.buffer, 0, 4);
+ int length_of_data = buf.getInt();
+ rest_length -= 4;
+
+ /**
+ Since sftp protocol version 6, "end-of-file" has been defined,
+
+ byte SSH_FXP_DATA
+ uint32 request-id
+ string data
+ bool end-of-file [optional]
+
+ but some sftpd server will send such a field in the sftp protocol 3 ;-(
+ */
+ int optional_data = rest_length - length_of_data;
+
+ offset += length_of_data;
+ int foo = length_of_data;
+ if (foo > 0) {
+ int bar = foo;
+ if (bar > len) {
+ bar = len;
+ }
+ int i = io_in.read(d, s, bar);
+ if (i < 0) {
+ return -1;
+ }
+ foo -= i;
+ rest_length = foo;
+
+ if (foo > 0) {
+ if (rest_byte.length < foo) {
+ rest_byte = new byte[foo];
+ }
+ int _s = 0;
+ int _len = foo;
+ int j;
+ while (_len > 0) {
+ j = io_in.read(rest_byte, _s, _len);
+ if (j <= 0) break;
+ _s += j;
+ _len -= j;
+ }
+ }
+
+ if (optional_data > 0) {
+ io_in.skip(optional_data);
+ }
+
+ if (length_of_data < rr.length) { //
+ rq.cancel(header, buf);
+ try {
+ sendREAD(handle,
+ rr.offset + length_of_data,
+ (int) (rr.length - length_of_data), rq);
+ }
+ catch (Exception e) {
+ throw new IOException("error");
+ }
+ request_offset = rr.offset + rr.length;
+ }
+
+ if (request_max < rq.size()) {
+ request_max++;
+ }
+
+ 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();
+ rq.cancel(header, buf);
+ 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 {
+ final java.util.Vector v = new Vector();
+ LsEntrySelector selector = new LsEntrySelector() {
+ public int select(LsEntry entry) {
+ v.addElement(entry);
+ return CONTINUE;
+ }
+ };
+ ls(path, selector);
+ return v;
+ }
+
+ /**
+ * List files specified by the remote path
.
+ * Each files and directories will be passed to
+ * LsEntrySelector#select(LsEntry)
method, and if that method
+ * returns LsEntrySelector#BREAK
, the operation will be
+ * canceled immediately.
+ *
+ * @see ChannelSftp.LsEntrySelector
+ * @since 0.1.47
+ */
+ public void ls(String path, LsEntrySelector selector) throws SftpException {
+ //System.out.println("ls: "+path);
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+ byte[] pattern = null;
+ java.util.Vector v = new java.util.Vector();
+
+ int foo = path.lastIndexOf('/');
+ String dir = path.substring(0, ((foo == 0) ? 1 : foo));
+ String _pattern = path.substring(foo + 1);
+ dir = Util.unquote(dir);
+
+ // If pattern has included '*' or '?', we need to convert
+ // to UTF-8 string before globbing.
+ byte[][] _pattern_utf8 = new byte[1][];
+ boolean pattern_has_wildcard = isPattern(_pattern, _pattern_utf8);
+
+ if (pattern_has_wildcard) {
+ pattern = _pattern_utf8[0];
+ }
+ else {
+ String upath = Util.unquote(path);
+ //SftpATTRS attr=_lstat(upath);
+ SftpATTRS attr = _stat(upath);
+ if (attr.isDir()) {
+ pattern = null;
+ dir = upath;
+ }
+ else {
+ /*
+ // If we can generage longname by ourself,
+ // we don't have to use openDIR.
+ String filename=Util.unquote(_pattern);
+ String longname=...
+ v.addElement(new LsEntry(filename, longname, attr));
+ return v;
+ */
+
+ if (fEncoding_is_utf8) {
+ pattern = _pattern_utf8[0];
+ pattern = Util.unquote(pattern);
+ }
+ else {
+ _pattern = Util.unquote(_pattern);
+ pattern = Util.str2byte(_pattern, fEncoding);
+ }
+
+ }
+ }
+
+ sendOPENDIR(Util.str2byte(dir, fEncoding));
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ if (type == SSH_FXP_STATUS) {
+ int i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+
+ int cancel = LsEntrySelector.CONTINUE;
+ byte[] handle = buf.getString(); // handle
+
+ while (cancel == LsEntrySelector.CONTINUE) {
+
+ 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);
+
+ if (cancel == LsEntrySelector.BREAK) {
+ count--;
+ continue;
+ }
+
+ 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);
+ }
+
+ cancel = selector.select(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);
+ }
+ }
+ */
+
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public String readlink(String path) throws SftpException {
+ try {
+ if (server_version < 3) {
+ throw new SftpException(SSH_FX_OP_UNSUPPORTED,
+ "The remote sshd is too old to support symlink operation.");
+ }
+
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+
+ path = isUnique(path);
+
+ sendREADLINK(Util.str2byte(path, fEncoding));
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS && type != SSH_FXP_NAME) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ if (type == SSH_FXP_NAME) {
+ int count = buf.getInt(); // count
+ byte[] filename = null;
+ for (int i = 0; i < count; i++) {
+ filename = buf.getString();
+ if (server_version <= 3) {
+ byte[] longname = buf.getString();
+ }
+ SftpATTRS.getATTR(buf);
+ }
+ return Util.byte2str(filename, fEncoding);
+ }
+
+ int i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ return null;
+ }
+
+ public void symlink(String oldpath, String newpath) throws SftpException {
+ if (server_version < 3) {
+ throw new SftpException(SSH_FX_OP_UNSUPPORTED,
+ "The remote sshd is too old to support symlink operation.");
+ }
+
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ String _oldpath = remoteAbsolutePath(oldpath);
+ newpath = remoteAbsolutePath(newpath);
+
+ _oldpath = isUnique(_oldpath);
+ if (oldpath.charAt(0) != '/') { // relative path
+ String cwd = getCwd();
+ oldpath = _oldpath.substring(cwd.length() + (cwd.endsWith("/") ? 0 : 1));
+ }
+ else {
+ oldpath = _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 hardlink(String oldpath, String newpath) throws SftpException {
+ if (!extension_hardlink) {
+ throw new SftpException(SSH_FX_OP_UNSUPPORTED,
+ "hardlink@openssh.com is not supported");
+ }
+
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ String _oldpath = remoteAbsolutePath(oldpath);
+ newpath = remoteAbsolutePath(newpath);
+
+ _oldpath = isUnique(_oldpath);
+ if (oldpath.charAt(0) != '/') { // relative path
+ String cwd = getCwd();
+ oldpath = _oldpath.substring(cwd.length() + (cwd.endsWith("/") ? 0 : 1));
+ }
+ else {
+ oldpath = _oldpath;
+ }
+
+ if (isPattern(newpath)) {
+ throw new SftpException(SSH_FX_FAILURE, newpath);
+ }
+ newpath = Util.unquote(newpath);
+
+ sendHARDLINK(Util.str2byte(oldpath, fEncoding),
+ Util.str2byte(newpath, fEncoding));
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+
+ int i = buf.getInt();
+ if (i == SSH_FX_OK) return;
+ throwStatusError(buf, i);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public void rename(String oldpath, String newpath) throws SftpException {
+ if (server_version < 2) {
+ throw new SftpException(SSH_FX_OP_UNSUPPORTED,
+ "The remote sshd is too old to support rename operation.");
+ }
+
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ oldpath = remoteAbsolutePath(oldpath);
+ newpath = remoteAbsolutePath(newpath);
+
+ oldpath = isUnique(oldpath);
+
+ Vector v = glob_remote(newpath);
+ int vsize = v.size();
+ if (vsize >= 2) {
+ throw new SftpException(SSH_FX_FAILURE, v.toString());
+ }
+ if (vsize == 1) {
+ newpath = (String) (v.elementAt(0));
+ }
+ else { // vsize==0
+ if (isPattern(newpath))
+ throw new SftpException(SSH_FX_FAILURE, newpath);
+ newpath = Util.unquote(newpath);
+ }
+
+ sendRENAME(Util.str2byte(oldpath, fEncoding),
+ Util.str2byte(newpath, fEncoding));
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+
+ int i = buf.getInt();
+ if (i == SSH_FX_OK) return;
+ throwStatusError(buf, i);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public void rm(String path) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+
+ Vector v = glob_remote(path);
+ int vsize = v.size();
+
+ Header header = new Header();
+
+ for (int j = 0; j < vsize; j++) {
+ path = (String) (v.elementAt(j));
+ sendREMOVE(Util.str2byte(path, fEncoding));
+
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ int i = buf.getInt();
+ if (i != SSH_FX_OK) {
+ throwStatusError(buf, i);
+ }
+ }
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ private boolean isRemoteDir(String path) {
+ try {
+ sendSTAT(Util.str2byte(path, fEncoding));
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_ATTRS) {
+ return false;
+ }
+ SftpATTRS attr = SftpATTRS.getATTR(buf);
+ return attr.isDir();
+ }
+ catch (Exception e) {
+ }
+ return false;
+ }
+
+ public void chgrp(int gid, String path) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+
+ Vector v = glob_remote(path);
+ int vsize = v.size();
+ for (int j = 0; j < vsize; j++) {
+ path = (String) (v.elementAt(j));
+
+ SftpATTRS attr = _stat(path);
+
+ attr.setFLAGS(0);
+ attr.setUIDGID(attr.uid, gid);
+ _setStat(path, attr);
+ }
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public void chown(int uid, String path) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+
+ Vector v = glob_remote(path);
+ int vsize = v.size();
+ for (int j = 0; j < vsize; j++) {
+ path = (String) (v.elementAt(j));
+
+ SftpATTRS attr = _stat(path);
+
+ attr.setFLAGS(0);
+ attr.setUIDGID(uid, attr.gid);
+ _setStat(path, attr);
+ }
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public void chmod(int permissions, String path) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+
+ Vector v = glob_remote(path);
+ int vsize = v.size();
+ for (int j = 0; j < vsize; j++) {
+ path = (String) (v.elementAt(j));
+
+ SftpATTRS attr = _stat(path);
+
+ attr.setFLAGS(0);
+ attr.setPERMISSIONS(permissions);
+ _setStat(path, attr);
+ }
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public void setMtime(String path, int mtime) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+
+ Vector v = glob_remote(path);
+ int vsize = v.size();
+ for (int j = 0; j < vsize; j++) {
+ path = (String) (v.elementAt(j));
+
+ SftpATTRS attr = _stat(path);
+
+ attr.setFLAGS(0);
+ attr.setACMODTIME(attr.getATime(), mtime);
+ _setStat(path, attr);
+ }
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public void rmdir(String path) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+
+ Vector v = glob_remote(path);
+ int vsize = v.size();
+
+ Header header = new Header();
+
+ for (int j = 0; j < vsize; j++) {
+ path = (String) (v.elementAt(j));
+ sendRMDIR(Util.str2byte(path, fEncoding));
+
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+
+ int i = buf.getInt();
+ if (i != SSH_FX_OK) {
+ throwStatusError(buf, i);
+ }
+ }
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public void mkdir(String path) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+
+ sendMKDIR(Util.str2byte(path, fEncoding), null);
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+
+ int i = buf.getInt();
+ if (i == SSH_FX_OK) return;
+ throwStatusError(buf, i);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public SftpATTRS stat(String path) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+ path = isUnique(path);
+
+ return _stat(path);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ //return null;
+ }
+
+ private SftpATTRS _stat(byte[] path) throws SftpException {
+ try {
+
+ sendSTAT(path);
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_ATTRS) {
+ if (type == SSH_FXP_STATUS) {
+ int i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ SftpATTRS attr = SftpATTRS.getATTR(buf);
+ return attr;
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ //return null;
+ }
+
+ private SftpATTRS _stat(String path) throws SftpException {
+ return _stat(Util.str2byte(path, fEncoding));
+ }
+
+ public SftpStatVFS statVFS(String path) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+ path = isUnique(path);
+
+ return _statVFS(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 SftpStatVFS _statVFS(byte[] path) throws SftpException {
+ if (!extension_statvfs) {
+ throw new SftpException(SSH_FX_OP_UNSUPPORTED,
+ "statvfs@openssh.com is not supported");
+ }
+
+ try {
+
+ sendSTATVFS(path);
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != (SSH_FXP_EXTENDED_REPLY & 0xff)) {
+ if (type == SSH_FXP_STATUS) {
+ int i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ else {
+ SftpStatVFS stat = SftpStatVFS.getStatVFS(buf);
+ return stat;
+ }
+ }
+ 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 SftpStatVFS _statVFS(String path) throws SftpException {
+ return _statVFS(Util.str2byte(path, fEncoding));
+ }
+
+ public SftpATTRS lstat(String path) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+ path = isUnique(path);
+
+ return _lstat(path);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ private SftpATTRS _lstat(String path) throws SftpException {
+ try {
+ sendLSTAT(Util.str2byte(path, fEncoding));
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_ATTRS) {
+ if (type == SSH_FXP_STATUS) {
+ int i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ SftpATTRS attr = SftpATTRS.getATTR(buf);
+ return attr;
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ private byte[] _realpath(String path) throws SftpException, IOException, Exception {
+ sendREALPATH(Util.str2byte(path, fEncoding));
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS && type != SSH_FXP_NAME) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ int i;
+ if (type == SSH_FXP_STATUS) {
+ i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+ i = buf.getInt(); // count
+
+ byte[] str = null;
+ while (i-- > 0) {
+ str = buf.getString(); // absolute path;
+ if (server_version <= 3) {
+ byte[] lname = buf.getString(); // long filename
+ }
+ SftpATTRS attr = SftpATTRS.getATTR(buf); // dummy attribute
+ }
+ return str;
+ }
+
+ public void setStat(String path, SftpATTRS attr) throws SftpException {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ path = remoteAbsolutePath(path);
+
+ Vector v = glob_remote(path);
+ int vsize = v.size();
+ for (int j = 0; j < vsize; j++) {
+ path = (String) (v.elementAt(j));
+ _setStat(path, attr);
+ }
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ private void _setStat(String path, SftpATTRS attr) throws SftpException {
+ try {
+ sendSETSTAT(Util.str2byte(path, fEncoding), attr);
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ int i = buf.getInt();
+ if (i != SSH_FX_OK) {
+ throwStatusError(buf, i);
+ }
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public String pwd() throws SftpException {
+ return getCwd();
+ }
+
+ public String lpwd() {
+ return lcwd;
+ }
+
+ public String version() {
+ return version;
+ }
+
+ public String getHome() throws SftpException {
+ if (home == null) {
+ try {
+ ((MyPipedInputStream) io_in).updateReadSide();
+
+ byte[] _home = _realpath("");
+ home = Util.byte2str(_home, fEncoding);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+ return home;
+ }
+
+ private String getCwd() throws SftpException {
+ if (cwd == null)
+ cwd = getHome();
+ return cwd;
+ }
+
+ private void setCwd(String cwd) {
+ this.cwd = cwd;
+ }
+
+ private void read(byte[] buf, int s, int l) throws IOException, SftpException {
+ int i = 0;
+ while (l > 0) {
+ i = io_in.read(buf, s, l);
+ if (i <= 0) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ s += i;
+ l -= i;
+ }
+ }
+
+ private boolean checkStatus(int[] ackid, Header header) throws IOException, SftpException {
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+ if (ackid != null)
+ ackid[0] = header.rid;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ int i = buf.getInt();
+ if (i != SSH_FX_OK) {
+ throwStatusError(buf, i);
+ }
+ return true;
+ }
+
+ private boolean _sendCLOSE(byte[] handle, Header header) throws Exception {
+ sendCLOSE(handle);
+ return checkStatus(null, header);
+ }
+
+ private void sendINIT() throws Exception {
+ packet.reset();
+ putHEAD(SSH_FXP_INIT, 5);
+ buf.putInt(3); // version 3
+ getSession().write(packet, this, 5 + 4);
+ }
+
+ private void sendREALPATH(byte[] path) throws Exception {
+ sendPacketPath(SSH_FXP_REALPATH, path);
+ }
+
+ private void sendSTAT(byte[] path) throws Exception {
+ sendPacketPath(SSH_FXP_STAT, path);
+ }
+
+ private void sendSTATVFS(byte[] path) throws Exception {
+ sendPacketPath((byte) 0, path, "statvfs@openssh.com");
+ }
+
+ /*
+ private void sendFSTATVFS(byte[] handle) throws Exception{
+ sendPacketPath((byte)0, handle, "fstatvfs@openssh.com");
+ }
+ */
+ 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 sendHARDLINK(byte[] p1, byte[] p2) throws Exception {
+ sendPacketPath((byte) 0, p1, p2, "hardlink@openssh.com");
+ }
+
+ 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,
+ extension_posix_rename ? "posix-rename@openssh.com" : null);
+ }
+
+ 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 {
+ sendPacketPath(fxp, path, (String) null);
+ }
+
+ private void sendPacketPath(byte fxp, byte[] path, String extension) throws Exception {
+ packet.reset();
+ int len = 9 + path.length;
+ if (extension == null) {
+ putHEAD(fxp, len);
+ buf.putInt(seq++);
+ }
+ else {
+ len += (4 + extension.length());
+ putHEAD(SSH_FXP_EXTENDED, len);
+ buf.putInt(seq++);
+ buf.putString(Util.str2byte(extension));
+ }
+ buf.putString(path); // path
+ getSession().write(packet, this, len + 4);
+ }
+
+ private void sendPacketPath(byte fxp, byte[] p1, byte[] p2) throws Exception {
+ sendPacketPath(fxp, p1, p2, null);
+ }
+
+ private void sendPacketPath(byte fxp, byte[] p1, byte[] p2, String extension) throws Exception {
+ packet.reset();
+ int len = 13 + p1.length + p2.length;
+ if (extension == null) {
+ putHEAD(fxp, len);
+ buf.putInt(seq++);
+ }
+ else {
+ len += (4 + extension.length());
+ putHEAD(SSH_FXP_EXTENDED, len);
+ buf.putInt(seq++);
+ buf.putString(Util.str2byte(extension));
+ }
+ buf.putString(p1);
+ buf.putString(p2);
+ getSession().write(packet, this, len + 4);
+ }
+
+ private int sendWRITE(byte[] handle, long offset,
+ byte[] data, int start, int length) throws Exception {
+ int _length = length;
+ opacket.reset();
+ if (obuf.buffer.length < obuf.index + 13 + 21 + handle.length + length + Session.buffer_margin) {
+ _length = obuf.buffer.length - (obuf.index + 13 + 21 + handle.length + Session.buffer_margin);
+ // System.err.println("_length="+_length+" length="+length);
+ }
+
+ putHEAD(obuf, SSH_FXP_WRITE, 21 + handle.length + _length); // 14
+ obuf.putInt(seq++); // 4
+ obuf.putString(handle); // 4+handle.length
+ obuf.putLong(offset); // 8
+ if (obuf.buffer != data) {
+ obuf.putString(data, start, _length); // 4+_length
+ }
+ else {
+ obuf.putInt(_length);
+ obuf.skip(_length);
+ }
+ getSession().write(opacket, this, 21 + handle.length + _length + 4);
+ return _length;
+ }
+
+ private void sendREAD(byte[] handle, long offset, int length) throws Exception {
+ sendREAD(handle, offset, length, null);
+ }
+
+ private void sendREAD(byte[] handle, long offset, int length,
+ RequestQueue rrq) throws Exception {
+ packet.reset();
+ putHEAD(SSH_FXP_READ, 21 + handle.length);
+ buf.putInt(seq++);
+ buf.putString(handle);
+ buf.putLong(offset);
+ buf.putInt(length);
+ getSession().write(packet, this, 21 + handle.length + 4);
+ if (rrq != null) {
+ rrq.add(seq - 1, offset, length);
+ }
+ }
+
+ private void putHEAD(Buffer buf, byte type, int length) throws Exception {
+ buf.putByte((byte) Session.SSH_MSG_CHANNEL_DATA);
+ buf.putInt(recipient);
+ buf.putInt(length + 4);
+ buf.putInt(length);
+ buf.putByte(type);
+ }
+
+ private void putHEAD(byte type, int length) throws Exception {
+ putHEAD(buf, type, length);
+ }
+
+ private Vector glob_remote(String _path) throws Exception {
+ Vector v = new Vector();
+ int i = 0;
+
+ int foo = _path.lastIndexOf('/');
+ if (foo < 0) { // it is not absolute path.
+ v.addElement(Util.unquote(_path));
+ return v;
+ }
+
+ String dir = _path.substring(0, ((foo == 0) ? 1 : foo));
+ String _pattern = _path.substring(foo + 1);
+
+ dir = Util.unquote(dir);
+
+ byte[] pattern = null;
+ byte[][] _pattern_utf8 = new byte[1][];
+ boolean pattern_has_wildcard = isPattern(_pattern, _pattern_utf8);
+
+ if (!pattern_has_wildcard) {
+ if (!dir.equals("/"))
+ dir += "/";
+ v.addElement(dir + Util.unquote(_pattern));
+ return v;
+ }
+
+ pattern = _pattern_utf8[0];
+
+ sendOPENDIR(Util.str2byte(dir, fEncoding));
+
+ Header header = new Header();
+ header = header(buf, header);
+ int length = header.length;
+ int type = header.type;
+
+ fill(buf, length);
+
+ if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ if (type == SSH_FXP_STATUS) {
+ i = buf.getInt();
+ throwStatusError(buf, i);
+ }
+
+ byte[] handle = buf.getString(); // filename
+ String pdir = null; // parent directory
+
+ while (true) {
+ sendREADDIR(handle);
+ header = header(buf, header);
+ length = header.length;
+ type = header.type;
+
+ if (type != SSH_FXP_STATUS && type != SSH_FXP_NAME) {
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ if (type == SSH_FXP_STATUS) {
+ fill(buf, length);
+ break;
+ }
+
+ buf.rewind();
+ fill(buf.buffer, 0, 4);
+ length -= 4;
+ int count = buf.getInt();
+
+ byte[] str;
+ int flags;
+
+ buf.reset();
+ while (count > 0) {
+ if (length > 0) {
+ buf.shift();
+ int j = (buf.buffer.length > (buf.index + length)) ? length : (buf.buffer.length - buf.index);
+ i = io_in.read(buf.buffer, buf.index, j);
+ if (i <= 0) break;
+ buf.index += i;
+ length -= i;
+ }
+
+ byte[] filename = buf.getString();
+ //System.err.println("filename: "+new String(filename));
+ if (server_version <= 3) {
+ str = buf.getString(); // longname
+ }
+ SftpATTRS attrs = SftpATTRS.getATTR(buf);
+
+ byte[] _filename = filename;
+ String f = null;
+ boolean found = false;
+
+ if (!fEncoding_is_utf8) {
+ f = Util.byte2str(filename, fEncoding);
+ _filename = Util.str2byte(f, UTF8);
+ }
+ found = Util.glob(pattern, _filename);
+
+ if (found) {
+ if (f == null) {
+ f = Util.byte2str(filename, fEncoding);
+ }
+ if (pdir == null) {
+ pdir = dir;
+ if (!pdir.endsWith("/")) {
+ pdir += "/";
+ }
+ }
+ v.addElement(pdir + f);
+ }
+ count--;
+ }
+ }
+ if (_sendCLOSE(handle, header))
+ return v;
+ return null;
+ }
+
+ private boolean isPattern(byte[] path) {
+ int length = path.length;
+ int i = 0;
+ while (i < length) {
+ if (path[i] == '*' || path[i] == '?')
+ return true;
+ if (path[i] == '\\' && (i + 1) < length)
+ i++;
+ i++;
+ }
+ return false;
+ }
+
+ private Vector glob_local(String _path) throws Exception {
+//System.err.println("glob_local: "+_path);
+ Vector v = new Vector();
+ byte[] path = Util.str2byte(_path, UTF8);
+ int i = path.length - 1;
+ while (i >= 0) {
+ if (path[i] != '*' && path[i] != '?') {
+ i--;
+ continue;
+ }
+ if (!fs_is_bs &&
+ i > 0 && path[i - 1] == '\\') {
+ i--;
+ if (i > 0 && path[i - 1] == '\\') {
+ i--;
+ i--;
+ continue;
+ }
+ }
+ break;
+ }
+
+ if (i < 0) {
+ v.addElement(fs_is_bs ? _path : Util.unquote(_path));
+ return v;
+ }
+
+ while (i >= 0) {
+ if (path[i] == file_separatorc ||
+ (fs_is_bs && path[i] == '/')) { // On Windows, '/' is also the separator.
+ break;
+ }
+ i--;
+ }
+
+ if (i < 0) {
+ v.addElement(fs_is_bs ? _path : Util.unquote(_path));
+ return v;
+ }
+
+ byte[] dir;
+ if (i == 0) {
+ dir = new byte[]{(byte) file_separatorc};
+ }
+ else {
+ dir = new byte[i];
+ System.arraycopy(path, 0, dir, 0, i);
+ }
+
+ byte[] pattern = new byte[path.length - i - 1];
+ System.arraycopy(path, i + 1, pattern, 0, pattern.length);
+
+//System.err.println("dir: "+new String(dir)+" pattern: "+new String(pattern));
+ try {
+ String[] children = (new File(Util.byte2str(dir, UTF8))).list();
+ String pdir = Util.byte2str(dir) + file_separator;
+ for (int j = 0; j < children.length; j++) {
+//System.err.println("children: "+children[j]);
+ if (Util.glob(pattern, Util.str2byte(children[j], UTF8))) {
+ v.addElement(pdir + children[j]);
+ }
+ }
+ }
+ catch (Exception e) {
+ }
+ return v;
+ }
+
+ private void throwStatusError(Buffer buf, int i) throws SftpException {
+ if (server_version >= 3 && // WindRiver's sftp will send invalid
+ buf.getLength() >= 4) { // SSH_FXP_STATUS packet.
+ byte[] str = buf.getString();
+ //byte[] tag=buf.getString();
+ throw new SftpException(i, Util.byte2str(str, UTF8));
+ }
+ else {
+ throw new SftpException(i, "Failure");
+ }
+ }
+
+ private static boolean isLocalAbsolutePath(String path) {
+ return (new File(path)).isAbsolute();
+ }
+
+ public void disconnect() {
+ super.disconnect();
+ }
+
+ private boolean isPattern(String path, byte[][] utf8) {
+ byte[] _path = Util.str2byte(path, UTF8);
+ if (utf8 != null)
+ utf8[0] = _path;
+ return isPattern(_path);
+ }
+
+ private boolean isPattern(String path) {
+ return isPattern(path, null);
+ }
+
+ private void fill(Buffer buf, int len) throws IOException {
+ buf.reset();
+ fill(buf.buffer, 0, len);
+ buf.skip(len);
+ }
+
+ private int fill(byte[] buf, int s, int len) throws IOException {
+ int i = 0;
+ int foo = s;
+ while (len > 0) {
+ i = io_in.read(buf, s, len);
+ if (i <= 0) {
+ throw new IOException("inputstream is closed");
+ //return (s-foo)==0 ? i : s-foo;
+ }
+ s += i;
+ len -= i;
+ }
+ return s - foo;
+ }
+
+ private void skip(long foo) throws IOException {
+ while (foo > 0) {
+ long bar = io_in.skip(foo);
+ if (bar <= 0)
+ break;
+ foo -= bar;
+ }
+ }
+
+ class Header {
+ int length;
+ int type;
+ int rid;
+ }
+
+ private Header header(Buffer buf, Header header) throws IOException {
+ buf.rewind();
+ int i = fill(buf.buffer, 0, 9);
+ header.length = buf.getInt() - 5;
+ header.type = buf.getByte() & 0xff;
+ header.rid = buf.getInt();
+ return header;
+ }
+
+ private String remoteAbsolutePath(String path) throws SftpException {
+ if (path.charAt(0) == '/') return path;
+ String cwd = getCwd();
+// if(cwd.equals(getHome())) return path;
+ if (cwd.endsWith("/")) return cwd + path;
+ return cwd + "/" + path;
+ }
+
+ private String localAbsolutePath(String path) {
+ if (isLocalAbsolutePath(path)) return path;
+ if (lcwd.endsWith(file_separator)) return lcwd + path;
+ return lcwd + file_separator + path;
+ }
+
+ /**
+ * This method will check if the given string can be expanded to the
+ * unique string. If it can be expanded to mutiple files, SftpException
+ * will be thrown.
+ *
+ * @return the returned string is unquoted.
+ */
+ private String isUnique(String path) throws SftpException, Exception {
+ Vector v = glob_remote(path);
+ if (v.size() != 1) {
+ throw new SftpException(SSH_FX_FAILURE, path + " is not unique: " + v.toString());
+ }
+ return (String) (v.elementAt(0));
+ }
+
+ public int getServerVersion() throws SftpException {
+ if (!isConnected()) {
+ throw new SftpException(SSH_FX_FAILURE, "The channel is not connected.");
+ }
+ return server_version;
+ }
+
+ public void setFilenameEncoding(String encoding) throws SftpException {
+ int sversion = getServerVersion();
+ if (3 <= sversion && sversion <= 5 &&
+ !encoding.equals(UTF8)) {
+ throw new SftpException(SSH_FX_FAILURE,
+ "The encoding can not be changed for this sftp server.");
+ }
+ if (encoding.equals(UTF8)) {
+ encoding = UTF8;
+ }
+ fEncoding = encoding;
+ fEncoding_is_utf8 = fEncoding.equals(UTF8);
+ }
+
+ public String getExtension(String key) {
+ if (extensions == null)
+ return null;
+ return (String) extensions.get(key);
+ }
+
+ public String realpath(String path) throws SftpException {
+ try {
+ byte[] _path = _realpath(remoteAbsolutePath(path));
+ return Util.byte2str(_path, fEncoding);
+ }
+ catch (Exception e) {
+ if (e instanceof SftpException) throw (SftpException) e;
+ if (e instanceof Throwable)
+ throw new SftpException(SSH_FX_FAILURE, "", (Throwable) e);
+ throw new SftpException(SSH_FX_FAILURE, "");
+ }
+ }
+
+ public class LsEntry implements Comparable {
+ private String filename;
+ private String longname;
+ private SftpATTRS attrs;
+
+ LsEntry(String filename, String longname, SftpATTRS attrs) {
+ setFilename(filename);
+ setLongname(longname);
+ setAttrs(attrs);
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ ;
+
+ void setFilename(String filename) {
+ this.filename = filename;
+ }
+
+ ;
+
+ public String getLongname() {
+ return longname;
+ }
+
+ ;
+
+ void setLongname(String longname) {
+ this.longname = longname;
+ }
+
+ ;
+
+ public SftpATTRS getAttrs() {
+ return attrs;
+ }
+
+ ;
+
+ void setAttrs(SftpATTRS attrs) {
+ this.attrs = attrs;
+ }
+
+ ;
+
+ public String toString() {
+ return longname;
+ }
+
+ public int compareTo(Object o) throws ClassCastException {
+ if (o instanceof LsEntry) {
+ return filename.compareTo(((LsEntry) o).getFilename());
+ }
+ throw new ClassCastException("a decendent of LsEntry must be given.");
+ }
+ }
+
+ /**
+ * This interface will be passed as an argument for ls
method.
+ *
+ * @see ChannelSftp.LsEntry
+ * @see #ls(String, ChannelSftp.LsEntrySelector)
+ * @since 0.1.47
+ */
+ public interface LsEntrySelector {
+ public final int CONTINUE = 0;
+ public final int BREAK = 1;
+
+ /**
+ * The select
method will be invoked in ls
+ * method for each file entry. If this method returns BREAK,
+ * ls
will be canceled.
+ *
+ * @param entry one of entry from ls
+ * @return if BREAK is returned, the 'ls' operation will be canceled.
+ */
+ public int select(LsEntry entry);
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/ChannelShell.java b/src/main/java/com/jcraft/jsch/ChannelShell.java
new file mode 100644
index 0000000..4003a39
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/ChannelShell.java
@@ -0,0 +1,68 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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 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/main/java/com/jcraft/jsch/ChannelSubsystem.java b/src/main/java/com/jcraft/jsch/ChannelSubsystem.java
new file mode 100644
index 0000000..57596b0
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/ChannelSubsystem.java
@@ -0,0 +1,99 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2005-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class ChannelSubsystem extends ChannelSession {
+ boolean xforwading = false;
+ boolean pty = false;
+ boolean want_reply = true;
+ String subsystem = "";
+
+ public void setXForwarding(boolean foo) {
+ xforwading = foo;
+ }
+
+ public void setPty(boolean foo) {
+ pty = foo;
+ }
+
+ public void setWantReply(boolean foo) {
+ want_reply = foo;
+ }
+
+ public void setSubsystem(String foo) {
+ subsystem = foo;
+ }
+
+ public void start() throws JSchException {
+ Session _session = getSession();
+ try {
+ Request request;
+ if (xforwading) {
+ request = new RequestX11();
+ request.request(_session, this);
+ }
+ if (pty) {
+ request = new RequestPtyReq();
+ request.request(_session, this);
+ }
+ request = new RequestSubsystem();
+ ((RequestSubsystem) request).request(_session, this, subsystem, want_reply);
+ }
+ catch (Exception e) {
+ if (e instanceof JSchException) {
+ throw (JSchException) e;
+ }
+ if (e instanceof Throwable)
+ throw new JSchException("ChannelSubsystem", (Throwable) e);
+ throw new JSchException("ChannelSubsystem");
+ }
+ if (io.in != null) {
+ thread = new Thread(this);
+ thread.setName("Subsystem for " + _session.host);
+ if (_session.daemon_thread) {
+ thread.setDaemon(_session.daemon_thread);
+ }
+ thread.start();
+ }
+ }
+
+ void init() throws JSchException {
+ io.setInputStream(getSession().in);
+ io.setOutputStream(getSession().out);
+ }
+
+ public void setErrStream(java.io.OutputStream out) {
+ setExtOutputStream(out);
+ }
+
+ public java.io.InputStream getErrStream() throws java.io.IOException {
+ return getExtInputStream();
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/ChannelX11.java b/src/main/java/com/jcraft/jsch/ChannelX11.java
new file mode 100644
index 0000000..36538df
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/ChannelX11.java
@@ -0,0 +1,282 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.net.Socket;
+
+class 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; ihost
is included with the key
.
+ *
+ * @return #NOT_INCLUDED, #OK or #CHANGED
+ * @see #NOT_INCLUDED
+ * @see #OK
+ * @see #CHANGED
+ */
+ int check(String host, byte[] key);
+
+ /**
+ * Adds a host key hostkey
+ *
+ * @param hostkey a host key to be added
+ * @param ui a user interface for showing messages or promping inputs.
+ * @see UserInfo
+ */
+ void add(HostKey hostkey, UserInfo ui);
+
+ /**
+ * Removes a host key if there exists mached key with
+ * host
, type
.
+ *
+ * @see #remove(String host, String type, byte[] key)
+ */
+ void remove(String host, String type);
+
+ /**
+ * Removes a host key if there exists a matched key with
+ * host
, type
and key
.
+ */
+ void remove(String host, String type, byte[] key);
+
+ /**
+ * Returns id of this repository.
+ *
+ * @return identity in String
+ */
+ String getKnownHostsRepositoryID();
+
+ /**
+ * Retuns a list for host keys managed in this repository.
+ *
+ * @see #getHostKey(String host, String type)
+ */
+ HostKey[] getHostKey();
+
+ /**
+ * Retuns a list for host keys managed in this repository.
+ *
+ * @param host a hostname used in searching host keys.
+ * If null
is given, every host key will be listed.
+ * @param type a key type used in searching host keys,
+ * and it should be "ssh-dss" or "ssh-rsa".
+ * If null
is given, a key type type will not be ignored.
+ */
+ HostKey[] getHostKey(String host, String type);
+}
diff --git a/src/main/java/com/jcraft/jsch/IO.java b/src/main/java/com/jcraft/jsch/IO.java
new file mode 100644
index 0000000..38cc957
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/IO.java
@@ -0,0 +1,150 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+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/main/java/com/jcraft/jsch/Identity.java b/src/main/java/com/jcraft/jsch/Identity.java
new file mode 100644
index 0000000..689f257
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/Identity.java
@@ -0,0 +1,88 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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 {
+
+ /**
+ * Decrypts this identity with the specified pass-phrase.
+ *
+ * @param passphrase the pass-phrase for this identity.
+ * @return true if the decryption is succeeded
+ * or this identity is not cyphered.
+ */
+ public boolean setPassphrase(byte[] passphrase) throws JSchException;
+
+ /**
+ * Returns the public-key blob.
+ *
+ * @return the public-key blob
+ */
+ public byte[] getPublicKeyBlob();
+
+ /**
+ * Signs on data with this identity, and returns the result.
+ *
+ * @param data data to be signed
+ * @return the signature
+ */
+ public byte[] getSignature(byte[] data);
+
+ /**
+ * @see #setPassphrase(byte[] passphrase)
+ * @deprecated The decryption should be done automatically in #setPassphase(byte[] passphrase)
+ */
+ public boolean decrypt();
+
+ /**
+ * Returns the name of the key algorithm.
+ *
+ * @return "ssh-rsa" or "ssh-dss"
+ */
+ public String getAlgName();
+
+ /**
+ * Returns the name of this identity.
+ * It will be useful to identify this object in the {@link IdentityRepository}.
+ */
+ public String getName();
+
+ /**
+ * Returns true if this identity is cyphered.
+ *
+ * @return true if this identity is cyphered.
+ */
+ public boolean isEncrypted();
+
+ /**
+ * Disposes internally allocated data, like byte array for the private key.
+ */
+ public void clear();
+}
diff --git a/src/main/java/com/jcraft/jsch/IdentityFile.java b/src/main/java/com/jcraft/jsch/IdentityFile.java
new file mode 100644
index 0000000..e95fcd4
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/IdentityFile.java
@@ -0,0 +1,134 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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 IdentityFile implements Identity {
+ private JSch jsch;
+ private KeyPair kpair;
+ private String identity;
+
+ static IdentityFile newInstance(String prvfile, String pubfile, JSch jsch) throws JSchException {
+ KeyPair kpair = KeyPair.load(jsch, prvfile, pubfile);
+ return new IdentityFile(jsch, prvfile, kpair);
+ }
+
+ static IdentityFile newInstance(String name, byte[] prvkey, byte[] pubkey, JSch jsch) throws JSchException {
+
+ KeyPair kpair = KeyPair.load(jsch, prvkey, pubkey);
+ return new IdentityFile(jsch, name, kpair);
+ }
+
+ private IdentityFile(JSch jsch, String name, KeyPair kpair) throws JSchException {
+ this.jsch = jsch;
+ this.identity = name;
+ this.kpair = kpair;
+ }
+
+ /**
+ * Decrypts this identity with the specified pass-phrase.
+ *
+ * @param passphrase the pass-phrase for this identity.
+ * @return true if the decryption is succeeded
+ * or this identity is not cyphered.
+ */
+ public boolean setPassphrase(byte[] passphrase) throws JSchException {
+ return kpair.decrypt(passphrase);
+ }
+
+ /**
+ * Returns the public-key blob.
+ *
+ * @return the public-key blob
+ */
+ public byte[] getPublicKeyBlob() {
+ return kpair.getPublicKeyBlob();
+ }
+
+ /**
+ * Signs on data with this identity, and returns the result.
+ *
+ * @param data data to be signed
+ * @return the signature
+ */
+ public byte[] getSignature(byte[] data) {
+ return kpair.getSignature(data);
+ }
+
+ /**
+ * @see #setPassphrase(byte[] passphrase)
+ * @deprecated This method should not be invoked.
+ */
+ public boolean decrypt() {
+ throw new RuntimeException("not implemented");
+ }
+
+ /**
+ * Returns the name of the key algorithm.
+ *
+ * @return "ssh-rsa" or "ssh-dss"
+ */
+ public String getAlgName() {
+ return new String(kpair.getKeyTypeName());
+ }
+
+ /**
+ * Returns the name of this identity.
+ * It will be useful to identify this object in the {@link IdentityRepository}.
+ */
+ public String getName() {
+ return identity;
+ }
+
+ /**
+ * Returns true if this identity is cyphered.
+ *
+ * @return true if this identity is cyphered.
+ */
+ public boolean isEncrypted() {
+ return kpair.isEncrypted();
+ }
+
+ /**
+ * Disposes internally allocated data, like byte array for the private key.
+ */
+ public void clear() {
+ kpair.dispose();
+ kpair = null;
+ }
+
+ /**
+ * Returns an instance of {@link KeyPair} used in this {@link Identity}.
+ *
+ * @return an instance of {@link KeyPair} used in this {@link Identity}.
+ */
+ public KeyPair getKeyPair() {
+ return kpair;
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/IdentityRepository.java b/src/main/java/com/jcraft/jsch/IdentityRepository.java
new file mode 100644
index 0000000..f70b957
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/IdentityRepository.java
@@ -0,0 +1,131 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2012-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.util.Vector;
+
+public interface IdentityRepository {
+ public static final int UNAVAILABLE = 0;
+ public static final int NOTRUNNING = 1;
+ public static final int RUNNING = 2;
+
+ public String getName();
+
+ public int getStatus();
+
+ public Vector getIdentities();
+
+ public boolean add(byte[] identity);
+
+ public boolean remove(byte[] blob);
+
+ public void removeAll();
+
+ /**
+ * JSch will accept ciphered keys, but some implementations of
+ * IdentityRepository can not. For example, IdentityRepository for
+ * ssh-agent and pageant only accept plain keys. The following class has
+ * been introduced to cache ciphered keys for them, and pass them
+ * whenever they are de-ciphered.
+ */
+ static class Wrapper implements IdentityRepository {
+ private IdentityRepository ir;
+ private Vector cache = new Vector();
+ private boolean keep_in_cache = false;
+
+ Wrapper(IdentityRepository ir) {
+ this(ir, false);
+ }
+
+ Wrapper(IdentityRepository ir, boolean keep_in_cache) {
+ this.ir = ir;
+ this.keep_in_cache = keep_in_cache;
+ }
+
+ public String getName() {
+ return ir.getName();
+ }
+
+ public int getStatus() {
+ return ir.getStatus();
+ }
+
+ public boolean add(byte[] identity) {
+ return ir.add(identity);
+ }
+
+ public boolean remove(byte[] blob) {
+ return ir.remove(blob);
+ }
+
+ public void removeAll() {
+ cache.removeAllElements();
+ ir.removeAll();
+ }
+
+ public Vector getIdentities() {
+ Vector result = new Vector();
+ for (int i = 0; i < cache.size(); i++) {
+ Identity identity = (Identity) (cache.elementAt(i));
+ result.add(identity);
+ }
+ Vector tmp = ir.getIdentities();
+ for (int i = 0; i < tmp.size(); i++) {
+ result.add(tmp.elementAt(i));
+ }
+ return result;
+ }
+
+ void add(Identity identity) {
+ if (!keep_in_cache &&
+ !identity.isEncrypted() && (identity instanceof IdentityFile)) {
+ try {
+ ir.add(((IdentityFile) identity).getKeyPair().forSSHAgent());
+ }
+ catch (JSchException e) {
+ // an exception will not be thrown.
+ }
+ }
+ else
+ cache.addElement(identity);
+ }
+
+ void check() {
+ if (cache.size() > 0) {
+ Object[] identities = cache.toArray();
+ for (int i = 0; i < identities.length; i++) {
+ Identity identity = (Identity) (identities[i]);
+ cache.removeElement(identity);
+ add(identity);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/JSch.java b/src/main/java/com/jcraft/jsch/JSch.java
new file mode 100644
index 0000000..dd77c68
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/JSch.java
@@ -0,0 +1,565 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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 {
+ /**
+ * The version number.
+ */
+ public static final String VERSION = "0.1.54";
+
+ static java.util.Hashtable config = new java.util.Hashtable();
+
+ static {
+ config.put("kex", "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1," +
+ "diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1");
+ config.put("server_host_key", "ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521");
+ config.put("cipher.s2c",
+ "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc");
+ config.put("cipher.c2s",
+ "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc");
+
+ config.put("mac.s2c", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96");
+ config.put("mac.c2s", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96");
+ config.put("compression.s2c", "none");
+ config.put("compression.c2s", "none");
+
+ config.put("lang.s2c", "");
+ config.put("lang.c2s", "");
+
+ config.put("compression_level", "6");
+
+ config.put("diffie-hellman-group-exchange-sha1",
+ "com.jcraft.jsch.DHGEX");
+ config.put("diffie-hellman-group1-sha1",
+ "com.jcraft.jsch.DHG1");
+ config.put("diffie-hellman-group14-sha1",
+ "com.jcraft.jsch.DHG14"); // available since JDK8.
+ config.put("diffie-hellman-group-exchange-sha256",
+ "com.jcraft.jsch.DHGEX256"); // available since JDK1.4.2.
+ // On JDK8, 2048bits will be used.
+ config.put("ecdsa-sha2-nistp256", "com.jcraft.jsch.jce.SignatureECDSA");
+ config.put("ecdsa-sha2-nistp384", "com.jcraft.jsch.jce.SignatureECDSA");
+ config.put("ecdsa-sha2-nistp521", "com.jcraft.jsch.jce.SignatureECDSA");
+
+ config.put("ecdh-sha2-nistp256", "com.jcraft.jsch.DHEC256");
+ config.put("ecdh-sha2-nistp384", "com.jcraft.jsch.DHEC384");
+ config.put("ecdh-sha2-nistp521", "com.jcraft.jsch.DHEC521");
+
+ config.put("ecdh-sha2-nistp", "com.jcraft.jsch.jce.ECDHN");
+
+ 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-sha2-256", "com.jcraft.jsch.jce.HMACSHA256");
+ // The "hmac-sha2-512" will require the key-length 2048 for DH,
+ // but Sun's JCE has not allowed to use such a long key.
+ //config.put("hmac-sha2-512", "com.jcraft.jsch.jce.HMACSHA512");
+ 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("sha-256", "com.jcraft.jsch.jce.SHA256");
+ config.put("sha-384", "com.jcraft.jsch.jce.SHA384");
+ config.put("sha-512", "com.jcraft.jsch.jce.SHA512");
+ 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("signature.ecdsa", "com.jcraft.jsch.jce.SignatureECDSA");
+ config.put("keypairgen.dsa", "com.jcraft.jsch.jce.KeyPairGenDSA");
+ config.put("keypairgen.rsa", "com.jcraft.jsch.jce.KeyPairGenRSA");
+ config.put("keypairgen.ecdsa", "com.jcraft.jsch.jce.KeyPairGenECDSA");
+ config.put("random", "com.jcraft.jsch.jce.Random");
+
+ config.put("none", "com.jcraft.jsch.CipherNone");
+
+ config.put("aes128-cbc", "com.jcraft.jsch.jce.AES128CBC");
+ config.put("aes192-cbc", "com.jcraft.jsch.jce.AES192CBC");
+ config.put("aes256-cbc", "com.jcraft.jsch.jce.AES256CBC");
+
+ config.put("aes128-ctr", "com.jcraft.jsch.jce.AES128CTR");
+ config.put("aes192-ctr", "com.jcraft.jsch.jce.AES192CTR");
+ config.put("aes256-ctr", "com.jcraft.jsch.jce.AES256CTR");
+ config.put("3des-ctr", "com.jcraft.jsch.jce.TripleDESCTR");
+ config.put("arcfour", "com.jcraft.jsch.jce.ARCFOUR");
+ config.put("arcfour128", "com.jcraft.jsch.jce.ARCFOUR128");
+ config.put("arcfour256", "com.jcraft.jsch.jce.ARCFOUR256");
+
+ config.put("userauth.none", "com.jcraft.jsch.UserAuthNone");
+ config.put("userauth.password", "com.jcraft.jsch.UserAuthPassword");
+ config.put("userauth.keyboard-interactive", "com.jcraft.jsch.UserAuthKeyboardInteractive");
+ config.put("userauth.publickey", "com.jcraft.jsch.UserAuthPublicKey");
+ config.put("userauth.gssapi-with-mic", "com.jcraft.jsch.UserAuthGSSAPIWithMIC");
+ config.put("gssapi-with-mic.krb5", "com.jcraft.jsch.jgss.GSSContextKrb5");
+
+ config.put("zlib", "com.jcraft.jsch.jcraft.Compression");
+ config.put("zlib@openssh.com", "com.jcraft.jsch.jcraft.Compression");
+
+ config.put("pbkdf", "com.jcraft.jsch.jce.PBKDF");
+
+ config.put("StrictHostKeyChecking", "ask");
+ config.put("HashKnownHosts", "no");
+
+ config.put("PreferredAuthentications", "gssapi-with-mic,publickey,keyboard-interactive,password");
+
+ config.put("CheckCiphers", "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-ctr,arcfour,arcfour128," +
+ "arcfour256");
+ config.put("CheckKexes", "diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521");
+ config.put("CheckSignatures", "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521");
+
+ config.put("MaxAuthTries", "6");
+ config.put("ClearAllForwardings", "no");
+ }
+
+ private java.util.Vector sessionPool = new java.util.Vector();
+
+ private IdentityRepository defaultIdentityRepository =
+ new LocalIdentityRepository(this);
+
+ private IdentityRepository identityRepository = defaultIdentityRepository;
+
+ private ConfigRepository configRepository = null;
+
+ /**
+ * Sets the identityRepository
, which will be referred
+ * in the public key authentication.
+ *
+ * @param identityRepository if null
is given,
+ * the default repository, which usually refers to ~/.ssh/, will be used.
+ * @see #getIdentityRepository()
+ */
+ public synchronized void setIdentityRepository(IdentityRepository identityRepository) {
+ if (identityRepository == null) {
+ this.identityRepository = defaultIdentityRepository;
+ }
+ else {
+ this.identityRepository = identityRepository;
+ }
+ }
+
+ public synchronized IdentityRepository getIdentityRepository() {
+ return this.identityRepository;
+ }
+
+ public ConfigRepository getConfigRepository() {
+ return this.configRepository;
+ }
+
+ public void setConfigRepository(ConfigRepository configRepository) {
+ this.configRepository = configRepository;
+ }
+
+ 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() {
+ /*
+ // The JCE of Sun's Java5 on Mac OS X has the resource leak bug
+ // in calculating HMAC, so we need to use our own implementations.
+ 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){
+ }
+ */
+ }
+
+ /**
+ * Instantiates the Session
object with
+ * host
. The user name and port number will be retrieved from
+ * ConfigRepository. If user name is not given,
+ * the system property "user.name" will be referred.
+ *
+ * @param host hostname
+ * @return the instance of Session
class.
+ * @throws JSchException if username
or host
are invalid.
+ * @see #getSession(String username, String host, int port)
+ * @see com.jcraft.jsch.Session
+ * @see com.jcraft.jsch.ConfigRepository
+ */
+ public Session getSession(String host)
+ throws JSchException {
+ return getSession(null, host, 22);
+ }
+
+ /**
+ * Instantiates the Session
object with
+ * username
and host
.
+ * The TCP port 22 will be used in making the connection.
+ * Note that the TCP connection must not be established
+ * until Session#connect().
+ *
+ * @param username user name
+ * @param host hostname
+ * @return the instance of Session
class.
+ * @throws JSchException if username
or host
are invalid.
+ * @see #getSession(String username, String host, int port)
+ * @see com.jcraft.jsch.Session
+ */
+ public Session getSession(String username, String host)
+ throws JSchException {
+ return getSession(username, host, 22);
+ }
+
+ /**
+ * Instantiates the Session
object with given
+ * username
, host
and port
.
+ * Note that the TCP connection must not be established
+ * until Session#connect().
+ *
+ * @param username user name
+ * @param host hostname
+ * @param port port number
+ * @return the instance of Session
class.
+ * @throws JSchException if username
or host
are invalid.
+ * @see #getSession(String username, String host, int port)
+ * @see com.jcraft.jsch.Session
+ */
+ public Session getSession(String username, String host, int port) throws JSchException {
+ if (host == null) {
+ throw new JSchException("host must not be null.");
+ }
+ Session s = new Session(this, username, host, port);
+ return s;
+ }
+
+ protected void addSession(Session session) {
+ synchronized (sessionPool) {
+ sessionPool.addElement(session);
+ }
+ }
+
+ protected boolean removeSession(Session session) {
+ synchronized (sessionPool) {
+ return sessionPool.remove(session);
+ }
+ }
+
+ /**
+ * Sets the hostkey repository.
+ *
+ * @param hkrepo
+ * @see com.jcraft.jsch.HostKeyRepository
+ * @see com.jcraft.jsch.KnownHosts
+ */
+ public void setHostKeyRepository(HostKeyRepository hkrepo) {
+ known_hosts = hkrepo;
+ }
+
+ /**
+ * Sets the instance of KnownHosts
, which refers
+ * to filename
.
+ *
+ * @param filename filename of known_hosts file.
+ * @throws JSchException if the given filename is invalid.
+ * @see com.jcraft.jsch.KnownHosts
+ */
+ 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);
+ }
+ }
+ }
+
+ /**
+ * Sets the instance of KnownHosts
generated with
+ * stream
.
+ *
+ * @param stream the instance of InputStream from known_hosts file.
+ * @throws JSchException if an I/O error occurs.
+ * @see com.jcraft.jsch.KnownHosts
+ */
+ 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);
+ }
+ }
+ }
+
+ /**
+ * Returns the current hostkey repository.
+ * By the default, this method will the instance of KnownHosts
.
+ *
+ * @return current hostkey repository.
+ * @see com.jcraft.jsch.HostKeyRepository
+ * @see com.jcraft.jsch.KnownHosts
+ */
+ public HostKeyRepository getHostKeyRepository() {
+ if (known_hosts == null) known_hosts = new KnownHosts(this);
+ return known_hosts;
+ }
+
+ /**
+ * Sets the private key, which will be referred in
+ * the public key authentication.
+ *
+ * @param prvkey filename of the private key.
+ * @throws JSchException if prvkey
is invalid.
+ * @see #addIdentity(String prvkey, String passphrase)
+ */
+ public void addIdentity(String prvkey) throws JSchException {
+ addIdentity(prvkey, (byte[]) null);
+ }
+
+ /**
+ * Sets the private key, which will be referred in
+ * the public key authentication.
+ * Before registering it into identityRepository,
+ * it will be deciphered with passphrase
.
+ *
+ * @param prvkey filename of the private key.
+ * @param passphrase passphrase for prvkey
.
+ * @throws JSchException if passphrase
is not right.
+ * @see #addIdentity(String prvkey, byte[] passphrase)
+ */
+ 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);
+ }
+
+ /**
+ * Sets the private key, which will be referred in
+ * the public key authentication.
+ * Before registering it into identityRepository,
+ * it will be deciphered with passphrase
.
+ *
+ * @param prvkey filename of the private key.
+ * @param passphrase passphrase for prvkey
.
+ * @throws JSchException if passphrase
is not right.
+ * @see #addIdentity(String prvkey, String pubkey, byte[] passphrase)
+ */
+ public void addIdentity(String prvkey, byte[] passphrase) throws JSchException {
+ Identity identity = IdentityFile.newInstance(prvkey, null, this);
+ addIdentity(identity, passphrase);
+ }
+
+ /**
+ * Sets the private key, which will be referred in
+ * the public key authentication.
+ * Before registering it into identityRepository,
+ * it will be deciphered with passphrase
.
+ *
+ * @param prvkey filename of the private key.
+ * @param pubkey filename of the public key.
+ * @param passphrase passphrase for prvkey
.
+ * @throws JSchException if passphrase
is not right.
+ */
+ public void addIdentity(String prvkey, String pubkey, byte[] passphrase) throws JSchException {
+ Identity identity = IdentityFile.newInstance(prvkey, pubkey, this);
+ addIdentity(identity, passphrase);
+ }
+
+ /**
+ * Sets the private key, which will be referred in
+ * the public key authentication.
+ * Before registering it into identityRepository,
+ * it will be deciphered with passphrase
.
+ *
+ * @param name name of the identity to be used to
+ * retrieve it in the identityRepository.
+ * @param prvkey private key in byte array.
+ * @param pubkey public key in byte array.
+ * @param passphrase passphrase for prvkey
.
+ */
+ public void addIdentity(String name, byte[] prvkey, byte[] pubkey, byte[] passphrase) throws JSchException {
+ Identity identity = IdentityFile.newInstance(name, prvkey, pubkey, this);
+ addIdentity(identity, passphrase);
+ }
+
+ /**
+ * Sets the private key, which will be referred in
+ * the public key authentication.
+ * Before registering it into identityRepository,
+ * it will be deciphered with passphrase
.
+ *
+ * @param identity private key.
+ * @param passphrase passphrase for identity
.
+ * @throws JSchException if passphrase
is not right.
+ */
+ public void addIdentity(Identity identity, byte[] passphrase) throws JSchException {
+ if (passphrase != null) {
+ try {
+ byte[] goo = new byte[passphrase.length];
+ System.arraycopy(passphrase, 0, goo, 0, passphrase.length);
+ passphrase = goo;
+ identity.setPassphrase(passphrase);
+ }
+ finally {
+ Util.bzero(passphrase);
+ }
+ }
+
+ if (identityRepository instanceof LocalIdentityRepository) {
+ ((LocalIdentityRepository) identityRepository).add(identity);
+ }
+ else if (identity instanceof IdentityFile && !identity.isEncrypted()) {
+ identityRepository.add(((IdentityFile) identity).getKeyPair().forSSHAgent());
+ }
+ else {
+ synchronized (this) {
+ if (!(identityRepository instanceof IdentityRepository.Wrapper)) {
+ setIdentityRepository(new IdentityRepository.Wrapper(identityRepository));
+ }
+ }
+ ((IdentityRepository.Wrapper) identityRepository).add(identity);
+ }
+ }
+
+ /**
+ * @deprecated use #removeIdentity(Identity identity)
+ */
+ public void removeIdentity(String name) throws JSchException {
+ Vector identities = identityRepository.getIdentities();
+ for (int i = 0; i < identities.size(); i++) {
+ Identity identity = (Identity) (identities.elementAt(i));
+ if (!identity.getName().equals(name))
+ continue;
+ if (identityRepository instanceof LocalIdentityRepository) {
+ ((LocalIdentityRepository) identityRepository).remove(identity);
+ }
+ else
+ identityRepository.remove(identity.getPublicKeyBlob());
+ }
+ }
+
+ /**
+ * Removes the identity from identityRepository.
+ *
+ * @param identity the indentity to be removed.
+ * @throws JSchException if identity
is invalid.
+ */
+ public void removeIdentity(Identity identity) throws JSchException {
+ identityRepository.remove(identity.getPublicKeyBlob());
+ }
+
+ /**
+ * Lists names of identities included in the identityRepository.
+ *
+ * @return names of identities
+ * @throws JSchException if identityReposory has problems.
+ */
+ public Vector getIdentityNames() throws JSchException {
+ Vector foo = new Vector();
+ Vector identities = identityRepository.getIdentities();
+ for (int i = 0; i < identities.size(); i++) {
+ Identity identity = (Identity) (identities.elementAt(i));
+ foo.addElement(identity.getName());
+ }
+ return foo;
+ }
+
+ /**
+ * Removes all identities from identityRepository.
+ *
+ * @throws JSchException if identityReposory has problems.
+ */
+ public void removeAllIdentity() throws JSchException {
+ identityRepository.removeAll();
+ }
+
+ /**
+ * Returns the config value for the specified key.
+ *
+ * @param key key for the configuration.
+ * @return config value
+ */
+ public static String getConfig(String key) {
+ synchronized (config) {
+ return (String) (config.get(key));
+ }
+ }
+
+ /**
+ * Sets or Overrides the configuration.
+ *
+ * @param newconf configurations
+ */
+ 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)));
+ }
+ }
+ }
+
+ /**
+ * Sets or Overrides the configuration.
+ *
+ * @param key key for the configuration
+ * @param value value for the configuration
+ */
+ public static void setConfig(String key, String value) {
+ config.put(key, value);
+ }
+
+ /**
+ * Sets the logger
+ *
+ * @param logger logger
+ * @see com.jcraft.jsch.Logger
+ */
+ public static void setLogger(Logger logger) {
+ if (logger == null) logger = DEVNULL;
+ JSch.logger = logger;
+ }
+
+ static Logger getLogger() {
+ return logger;
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/JSchAuthCancelException.java b/src/main/java/com/jcraft/jsch/JSchAuthCancelException.java
new file mode 100644
index 0000000..c01aad9
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/JSchAuthCancelException.java
@@ -0,0 +1,48 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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/main/java/com/jcraft/jsch/JSchException.java b/src/main/java/com/jcraft/jsch/JSchException.java
new file mode 100644
index 0000000..0e87c8b
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/JSchException.java
@@ -0,0 +1,52 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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/main/java/com/jcraft/jsch/JSchPartialAuthException.java b/src/main/java/com/jcraft/jsch/JSchPartialAuthException.java
new file mode 100644
index 0000000..518bf24
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/JSchPartialAuthException.java
@@ -0,0 +1,48 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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/main/java/com/jcraft/jsch/KeyExchange.java b/src/main/java/com/jcraft/jsch/KeyExchange.java
new file mode 100644
index 0000000..1ebd072
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/KeyExchange.java
@@ -0,0 +1,358 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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 int getState();
+
+ protected final int RSA = 0;
+ protected final int DSS = 1;
+ protected final int ECDSA = 2;
+ private int type = 0;
+ private String key_alg_name = "";
+
+ public String getKeyType() {
+ if (type == DSS) return "DSA";
+ if (type == RSA) return "RSA";
+ return "ECDSA";
+ }
+
+ public String getKeyAlgorithName() {
+ return key_alg_name;
+ }
+
+ 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);
+
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ for (int i = 0; i < PROPOSAL_MAX; i++) {
+ JSch.getLogger().log(Logger.INFO,
+ "kex: server: " + Util.byte2str(sb.getString()));
+ }
+ for (int i = 0; i < PROPOSAL_MAX; i++) {
+ JSch.getLogger().log(Logger.INFO,
+ "kex: client: " + Util.byte2str(cb.getString()));
+ }
+ sb.setOffSet(17);
+ 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]);
+ }
+
+ 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;
+ }
+
+ /*
+ * It seems JCE included in Oracle's Java7u6(and later) has suddenly changed
+ * its behavior. The secrete generated by KeyAgreement#generateSecret()
+ * may start with 0, even if it is a positive value.
+ */
+ protected byte[] normalize(byte[] secret) {
+ if (secret.length > 1 &&
+ secret[0] == 0 && (secret[1] & 0x80) == 0) {
+ byte[] tmp = new byte[secret.length - 1];
+ System.arraycopy(secret, 1, tmp, 0, tmp.length);
+ return normalize(tmp);
+ }
+ else {
+ return secret;
+ }
+ }
+
+ protected boolean verify(String alg, byte[] K_S, int index,
+ byte[] sig_of_H) throws Exception {
+ int i, j;
+
+ i = index;
+ boolean result = false;
+
+ if (alg.equals("ssh-rsa")) {
+ byte[] tmp;
+ byte[] ee;
+ byte[] n;
+
+ type = RSA;
+ key_alg_name = alg;
+
+ j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) |
+ ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff);
+ tmp = new byte[j];
+ System.arraycopy(K_S, i, tmp, 0, j);
+ i += j;
+ ee = tmp;
+ j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) |
+ ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff);
+ tmp = new byte[j];
+ System.arraycopy(K_S, i, tmp, 0, j);
+ i += j;
+ n = tmp;
+
+ SignatureRSA sig = null;
+ try {
+ Class c = Class.forName(session.getConfig("signature.rsa"));
+ sig = (SignatureRSA) (c.newInstance());
+ sig.init();
+ }
+ catch (Exception e) {
+ System.err.println(e);
+ }
+ sig.setPubKey(ee, n);
+ sig.update(H);
+ result = sig.verify(sig_of_H);
+
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ JSch.getLogger().log(Logger.INFO,
+ "ssh_rsa_verify: signature " + result);
+ }
+ }
+ else if (alg.equals("ssh-dss")) {
+ byte[] q = null;
+ byte[] tmp;
+ byte[] p;
+ byte[] g;
+ byte[] f;
+
+ type = DSS;
+ key_alg_name = alg;
+
+ j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) |
+ ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff);
+ tmp = new byte[j];
+ System.arraycopy(K_S, i, tmp, 0, j);
+ i += j;
+ p = tmp;
+ j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) |
+ ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff);
+ tmp = new byte[j];
+ System.arraycopy(K_S, i, tmp, 0, j);
+ i += j;
+ q = tmp;
+ j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) |
+ ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff);
+ tmp = new byte[j];
+ System.arraycopy(K_S, i, tmp, 0, j);
+ i += j;
+ g = tmp;
+ j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) |
+ ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff);
+ tmp = new byte[j];
+ System.arraycopy(K_S, i, tmp, 0, j);
+ i += j;
+ f = tmp;
+
+ SignatureDSA sig = null;
+ try {
+ Class c = Class.forName(session.getConfig("signature.dss"));
+ sig = (SignatureDSA) (c.newInstance());
+ sig.init();
+ }
+ catch (Exception e) {
+ System.err.println(e);
+ }
+ sig.setPubKey(f, p, q, g);
+ sig.update(H);
+ result = sig.verify(sig_of_H);
+
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ JSch.getLogger().log(Logger.INFO,
+ "ssh_dss_verify: signature " + result);
+ }
+ }
+ else if (alg.equals("ecdsa-sha2-nistp256") ||
+ alg.equals("ecdsa-sha2-nistp384") ||
+ alg.equals("ecdsa-sha2-nistp521")) {
+ byte[] tmp;
+ byte[] r;
+ byte[] s;
+
+ // RFC 5656,
+ type = ECDSA;
+ key_alg_name = alg;
+
+ 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;
+ j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) |
+ ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff);
+ i++;
+ tmp = new byte[(j - 1) / 2];
+ System.arraycopy(K_S, i, tmp, 0, tmp.length);
+ i += (j - 1) / 2;
+ r = tmp;
+ tmp = new byte[(j - 1) / 2];
+ System.arraycopy(K_S, i, tmp, 0, tmp.length);
+ i += (j - 1) / 2;
+ s = tmp;
+
+ SignatureECDSA sig = null;
+ try {
+ Class c = Class.forName(session.getConfig("signature.ecdsa"));
+ sig = (SignatureECDSA) (c.newInstance());
+ sig.init();
+ }
+ catch (Exception e) {
+ System.err.println(e);
+ }
+
+ sig.setPubKey(r, s);
+
+ sig.update(H);
+
+ result = sig.verify(sig_of_H);
+ }
+ else {
+ System.err.println("unknown alg");
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/main/java/com/jcraft/jsch/KeyPair.java b/src/main/java/com/jcraft/jsch/KeyPair.java
new file mode 100644
index 0000000..51b9175
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/KeyPair.java
@@ -0,0 +1,1376 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+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 ECDSA = 3;
+ public static final int UNKNOWN = 4;
+
+ static final int VENDOR_OPENSSH = 0;
+ static final int VENDOR_FSECURE = 1;
+ static final int VENDOR_PUTTY = 2;
+ static final int VENDOR_PKCS8 = 3;
+
+ 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);
+ }
+ else if (type == ECDSA) {
+ kpair = new KeyPairECDSA(jsch);
+ }
+ if (kpair != null) {
+ kpair.generate(key_size);
+ }
+ return kpair;
+ }
+
+ abstract void generate(int key_size) throws JSchException;
+
+ abstract byte[] getBegin();
+
+ abstract byte[] getEnd();
+
+ abstract int getKeySize();
+
+ public abstract byte[] getSignature(byte[] data);
+
+ public abstract Signature getVerifier();
+
+ public abstract byte[] forSSHAgent() throws JSchException;
+
+ public String getPublicKeyComment() {
+ return publicKeyComment;
+ }
+
+ public void setPublicKeyComment(String publicKeyComment) {
+ this.publicKeyComment = publicKeyComment;
+ }
+
+ protected String publicKeyComment = "no comment";
+
+ 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();
+
+ /**
+ * Writes the plain private key to the given output stream.
+ *
+ * @param out output stream
+ * @see #writePrivateKey(java.io.OutputStream out, byte[] passphrase)
+ */
+ public void writePrivateKey(java.io.OutputStream out) {
+ this.writePrivateKey(out, null);
+ }
+
+ /**
+ * Writes the cyphered private key to the given output stream.
+ *
+ * @param out output stream
+ * @param passphrase a passphrase to encrypt the private key
+ */
+ public void writePrivateKey(java.io.OutputStream out, byte[] passphrase) {
+ if (passphrase == null)
+ passphrase = this.passphrase;
+
+ byte[] plain = getPrivateKey();
+ byte[][] _iv = new byte[1][];
+ byte[] encoded = encrypt(plain, _iv, passphrase);
+ 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();
+
+ /**
+ * Returns the blob of the public key.
+ *
+ * @return blob of the public key
+ */
+ public byte[] getPublicKeyBlob() {
+ // TODO JSchException should be thrown
+ //if(publickeyblob == null)
+ // throw new JSchException("public-key blob is not available");
+ return publickeyblob;
+ }
+
+ /**
+ * Writes the public key with the specified comment to the output stream.
+ *
+ * @param out output stream
+ * @param comment comment
+ */
+ 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) {
+ }
+ }
+
+ /**
+ * Writes the public key with the specified comment to the file.
+ *
+ * @param name file name
+ * @param comment comment
+ * @see #writePublicKey(java.io.OutputStream out, String comment)
+ */
+ public void writePublicKey(String name, String comment) throws java.io.FileNotFoundException, java.io.IOException {
+ FileOutputStream fos = new FileOutputStream(name);
+ writePublicKey(fos, comment);
+ fos.close();
+ }
+
+ /**
+ * Writes the public key with the specified comment to the output stream in
+ * the format defined in http://www.ietf.org/rfc/rfc4716.txt
+ *
+ * @param out output stream
+ * @param comment comment
+ */
+ 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) {
+ }
+ }
+
+ /**
+ * Writes the public key with the specified comment to the output stream in
+ * the format defined in http://www.ietf.org/rfc/rfc4716.txt
+ *
+ * @param name file name
+ * @param comment comment
+ * @see #writeSECSHPublicKey(java.io.OutputStream out, String comment)
+ */
+ public void writeSECSHPublicKey(String name, String comment) throws java.io.FileNotFoundException, java.io.IOException {
+ FileOutputStream fos = new FileOutputStream(name);
+ writeSECSHPublicKey(fos, comment);
+ fos.close();
+ }
+
+ /**
+ * Writes the plain private key to the file.
+ *
+ * @param name file name
+ * @see #writePrivateKey(String name, byte[] passphrase)
+ */
+ public void writePrivateKey(String name) throws java.io.FileNotFoundException, java.io.IOException {
+ this.writePrivateKey(name, null);
+ }
+
+ /**
+ * Writes the cyphered private key to the file.
+ *
+ * @param name file name
+ * @param passphrase a passphrase to encrypt the private key
+ * @see #writePrivateKey(java.io.OutputStream out, byte[] passphrase)
+ */
+ public void writePrivateKey(String name, byte[] passphrase) throws java.io.FileNotFoundException, java.io.IOException {
+ FileOutputStream fos = new FileOutputStream(name);
+ writePrivateKey(fos, passphrase);
+ fos.close();
+ }
+
+ /**
+ * Returns the finger-print of the public key.
+ *
+ * @return finger print
+ */
+ public String getFingerPrint() {
+ if (hash == null) hash = genHash();
+ byte[] kblob = getPublicKeyBlob();
+ if (kblob == null) return null;
+ return Util.getFingerPrint(hash, kblob);
+ }
+
+ private byte[] encrypt(byte[] plain, byte[][] _iv, byte[] passphrase) {
+ 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) {
+
+ 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 writeOCTETSTRING(byte[] buf, int index, byte[] data) {
+ buf[index++] = 0x04;
+ index = writeLength(buf, index, data.length);
+ System.arraycopy(data, 0, buf, index, data.length);
+ index += data.length;
+ return index;
+ }
+
+ int writeDATA(byte[] buf, byte n, int index, byte[] data) {
+ buf[index++] = n;
+ index = writeLength(buf, index, data.length);
+ System.arraycopy(data, 0, buf, index, data.length);
+ index += data.length;
+ return index;
+ }
+
+ int countLength(int len) {
+ int i = 1;
+ if (len <= 0x7f) return i;
+ while (len > 0) {
+ len >>>= 8;
+ i++;
+ }
+ return i;
+ }
+
+ int writeLength(byte[] data, int index, int len) {
+ int i = countLength(len) - 1;
+ if (i == 0) {
+ data[index++] = (byte) len;
+ return index;
+ }
+ data[index++] = (byte) (0x80 | i);
+ int j = index + i;
+ while (i > 0) {
+ data[index + i - 1] = (byte) (len & 0xff);
+ len >>>= 8;
+ i--;
+ }
+ return j;
+ }
+
+ private Random genRandom() {
+ if (random == null) {
+ try {
+ Class c = Class.forName(jsch.getConfig("random"));
+ random = (Random) (c.newInstance());
+ }
+ catch (Exception e) {
+ System.err.println("connect: random " + e);
+ }
+ }
+ return random;
+ }
+
+ private HASH genHash() {
+ try {
+ Class c = Class.forName(jsch.getConfig("md5"));
+ hash = (HASH) (c.newInstance());
+ hash.init();
+ }
+ catch (Exception e) {
+ }
+ return hash;
+ }
+
+ private Cipher genCipher() {
+ try {
+ Class c;
+ c = Class.forName(jsch.getConfig("3des-cbc"));
+ cipher = (Cipher) (c.newInstance());
+ }
+ catch (Exception e) {
+ }
+ return cipher;
+ }
+
+ /*
+ hash is MD5
+ h(0) <- hash(passphrase, iv);
+ h(n) <- hash(h(n-1), passphrase, iv);
+ key <- (h(0),...,h(n))[0,..,key.length];
+ */
+ synchronized byte[] genKey(byte[] passphrase, byte[] iv) {
+ if (cipher == null) cipher = genCipher();
+ if (hash == null) hash = genHash();
+
+ byte[] key = new byte[cipher.getBlockSize()];
+ int hsize = hash.getBlockSize();
+ byte[] hn = new byte[key.length / hsize * hsize +
+ (key.length % hsize == 0 ? 0 : hsize)];
+ try {
+ byte[] tmp = null;
+ if (vendor == VENDOR_OPENSSH) {
+ for (int index = 0; index + hsize <= hn.length; ) {
+ if (tmp != null) {
+ hash.update(tmp, 0, tmp.length);
+ }
+ hash.update(passphrase, 0, passphrase.length);
+ hash.update(iv, 0, iv.length > 8 ? 8 : iv.length);
+ tmp = hash.digest();
+ System.arraycopy(tmp, 0, hn, index, tmp.length);
+ index += tmp.length;
+ }
+ System.arraycopy(hn, 0, key, 0, key.length);
+ }
+ else if (vendor == VENDOR_FSECURE) {
+ for (int index = 0; index + hsize <= hn.length; ) {
+ if (tmp != null) {
+ hash.update(tmp, 0, tmp.length);
+ }
+ hash.update(passphrase, 0, passphrase.length);
+ tmp = hash.digest();
+ System.arraycopy(tmp, 0, hn, index, tmp.length);
+ index += tmp.length;
+ }
+ System.arraycopy(hn, 0, key, 0, key.length);
+ }
+ else if (vendor == VENDOR_PUTTY) {
+ Class c = Class.forName((String) jsch.getConfig("sha-1"));
+ HASH sha1 = (HASH) (c.newInstance());
+ tmp = new byte[4];
+ key = new byte[20 * 2];
+ for (int i = 0; i < 2; i++) {
+ sha1.init();
+ tmp[3] = (byte) i;
+ sha1.update(tmp, 0, tmp.length);
+ sha1.update(passphrase, 0, passphrase.length);
+ System.arraycopy(sha1.digest(), 0, key, i * 20, 20);
+ }
+ }
+ }
+ catch (Exception e) {
+ System.err.println(e);
+ }
+ return key;
+ }
+
+ /**
+ * @deprecated use #writePrivateKey(java.io.OutputStream out, byte[] passphrase)
+ */
+ public void setPassphrase(String passphrase) {
+ if (passphrase == null || passphrase.length() == 0) {
+ setPassphrase((byte[]) null);
+ }
+ else {
+ setPassphrase(Util.str2byte(passphrase));
+ }
+ }
+
+ /**
+ * @deprecated use #writePrivateKey(String name, byte[] passphrase)
+ */
+ public void setPassphrase(byte[] passphrase) {
+ if (passphrase != null && passphrase.length == 0)
+ passphrase = null;
+ this.passphrase = passphrase;
+ }
+
+ protected boolean encrypted = false;
+ protected 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 prvfile, String pubfile) throws JSchException {
+
+ byte[] prvkey = null;
+ byte[] pubkey = null;
+
+ try {
+ prvkey = Util.fromFile(prvfile);
+ }
+ catch (IOException e) {
+ throw new JSchException(e.toString(), (Throwable) e);
+ }
+
+ String _pubfile = pubfile;
+ if (pubfile == null) {
+ _pubfile = prvfile + ".pub";
+ }
+
+ try {
+ pubkey = Util.fromFile(_pubfile);
+ }
+ catch (IOException e) {
+ if (pubfile != null) {
+ throw new JSchException(e.toString(), (Throwable) e);
+ }
+ }
+
+ try {
+ return load(jsch, prvkey, pubkey);
+ }
+ finally {
+ Util.bzero(prvkey);
+ }
+ }
+
+ public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchException {
+
+ byte[] iv = new byte[8]; // 8
+ boolean encrypted = true;
+ byte[] data = null;
+
+ byte[] publickeyblob = null;
+
+ int type = ERROR;
+ int vendor = VENDOR_OPENSSH;
+ String publicKeyComment = "";
+ Cipher cipher = null;
+
+ // prvkey from "ssh-add" command on the remote.
+ if (pubkey == null &&
+ prvkey != null &&
+ (prvkey.length > 11 &&
+ prvkey[0] == 0 && prvkey[1] == 0 && prvkey[2] == 0 &&
+ (prvkey[3] == 7 || prvkey[3] == 19))) {
+
+ Buffer buf = new Buffer(prvkey);
+ buf.skip(prvkey.length); // for using Buffer#available()
+ String _type = new String(buf.getString()); // ssh-rsa or ssh-dss
+ buf.rewind();
+
+ KeyPair kpair = null;
+ if (_type.equals("ssh-rsa")) {
+ kpair = KeyPairRSA.fromSSHAgent(jsch, buf);
+ }
+ else if (_type.equals("ssh-dss")) {
+ kpair = KeyPairDSA.fromSSHAgent(jsch, buf);
+ }
+ else if (_type.equals("ecdsa-sha2-nistp256") ||
+ _type.equals("ecdsa-sha2-nistp384") ||
+ _type.equals("ecdsa-sha2-nistp512")) {
+ kpair = KeyPairECDSA.fromSSHAgent(jsch, buf);
+ }
+ else {
+ throw new JSchException("privatekey: invalid key " + new String(prvkey, 4, 7));
+ }
+ return kpair;
+ }
+
+ try {
+ byte[] buf = prvkey;
+
+ if (buf != null) {
+ KeyPair ppk = loadPPK(jsch, buf);
+ if (ppk != null)
+ return ppk;
+ }
+
+ int len = (buf != null ? buf.length : 0);
+ int i = 0;
+
+ // skip garbage lines.
+ 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 (i + 2 >= len)
+ throw new JSchException("invalid privatekey: " + prvkey);
+ 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] == 'E' && buf[i + 1] == 'C') {
+ type = ECDSA;
+ }
+ else if (buf[i] == 'S' && buf[i + 1] == 'S' && buf[i + 2] == 'H') { // FSecure
+ type = UNKNOWN;
+ vendor = VENDOR_FSECURE;
+ }
+ else if (i + 6 < len &&
+ buf[i] == 'P' && buf[i + 1] == 'R' &&
+ buf[i + 2] == 'I' && buf[i + 3] == 'V' &&
+ buf[i + 4] == 'A' && buf[i + 5] == 'T' && buf[i + 6] == 'E') {
+ type = UNKNOWN;
+ vendor = VENDOR_PKCS8;
+ encrypted = false;
+ i += 3;
+ }
+ else if (i + 8 < len &&
+ buf[i] == 'E' && buf[i + 1] == 'N' &&
+ buf[i + 2] == 'C' && buf[i + 3] == 'R' &&
+ buf[i + 4] == 'Y' && buf[i + 5] == 'P' && buf[i + 6] == 'T' &&
+ buf[i + 7] == 'E' && buf[i + 8] == 'D') {
+ type = UNKNOWN;
+ vendor = VENDOR_PKCS8;
+ i += 5;
+ }
+ else {
+ throw new JSchException("invalid privatekey: " + prvkey);
+ }
+ i += 3;
+ continue;
+ }
+ if (buf[i] == 'A' && i + 7 < len && buf[i + 1] == 'E' && buf[i + 2] == 'S' && buf[i + 3] == '-' &&
+ buf[i + 4] == '2' && buf[i + 5] == '5' && buf[i + 6] == '6' && buf[i + 7] == '-') {
+ i += 8;
+ if (Session.checkCipher((String) jsch.getConfig("aes256-cbc"))) {
+ Class c = Class.forName((String) jsch.getConfig("aes256-cbc"));
+ cipher = (Cipher) (c.newInstance());
+ // key=new byte[cipher.getBlockSize()];
+ iv = new byte[cipher.getIVSize()];
+ }
+ else {
+ throw new JSchException("privatekey: aes256-cbc is not available " + prvkey);
+ }
+ continue;
+ }
+ if (buf[i] == 'A' && i + 7 < len && buf[i + 1] == 'E' && buf[i + 2] == 'S' && buf[i + 3] == '-' &&
+ buf[i + 4] == '1' && buf[i + 5] == '9' && buf[i + 6] == '2' && buf[i + 7] == '-') {
+ i += 8;
+ if (Session.checkCipher((String) jsch.getConfig("aes192-cbc"))) {
+ Class c = Class.forName((String) jsch.getConfig("aes192-cbc"));
+ cipher = (Cipher) (c.newInstance());
+ // key=new byte[cipher.getBlockSize()];
+ iv = new byte[cipher.getIVSize()];
+ }
+ else {
+ throw new JSchException("privatekey: aes192-cbc is not available " + prvkey);
+ }
+ continue;
+ }
+ if (buf[i] == 'A' && i + 7 < len && buf[i + 1] == 'E' && buf[i + 2] == 'S' && buf[i + 3] == '-' &&
+ buf[i + 4] == '1' && buf[i + 5] == '2' && buf[i + 6] == '8' && buf[i + 7] == '-') {
+ i += 8;
+ if (Session.checkCipher((String) jsch.getConfig("aes128-cbc"))) {
+ Class c = Class.forName((String) jsch.getConfig("aes128-cbc"));
+ cipher = (Cipher) (c.newInstance());
+ // key=new byte[cipher.getBlockSize()];
+ iv = new byte[cipher.getIVSize()];
+ }
+ else {
+ throw new JSchException("privatekey: aes128-cbc is not available " + prvkey);
+ }
+ continue;
+ }
+ if (buf[i] == 'C' && i + 3 < len && buf[i + 1] == 'B' && buf[i + 2] == 'C' && buf[i + 3] == ',') {
+ i += 4;
+ for (int ii = 0; ii < iv.length; ii++) {
+ iv[ii] = (byte) (((a2b(buf[i++]) << 4) & 0xf0) + (a2b(buf[i++]) & 0xf));
+ }
+ continue;
+ }
+ if (buf[i] == 0x0d && i + 1 < buf.length && buf[i + 1] == 0x0a) {
+ i++;
+ continue;
+ }
+ if (buf[i] == 0x0a && i + 1 < buf.length) {
+ if (buf[i + 1] == 0x0a) {
+ i += 2;
+ break;
+ }
+ if (buf[i + 1] == 0x0d &&
+ i + 2 < buf.length && buf[i + 2] == 0x0a) {
+ i += 3;
+ break;
+ }
+ boolean inheader = false;
+ for (int j = i + 1; j < buf.length; j++) {
+ if (buf[j] == 0x0a) break;
+ //if(buf[j]==0x0d) break;
+ if (buf[j] == ':') {
+ inheader = true;
+ break;
+ }
+ }
+ if (!inheader) {
+ i++;
+ if (vendor != VENDOR_PKCS8)
+ encrypted = false; // no passphrase
+ break;
+ }
+ }
+ i++;
+ }
+
+ if (buf != null) {
+
+ if (type == ERROR) {
+ throw new JSchException("invalid privatekey: " + prvkey);
+ }
+
+ int start = i;
+ while (i < len) {
+ if (buf[i] == '-') {
+ break;
+ }
+ i++;
+ }
+
+ if ((len - i) == 0 || (i - start) == 0) {
+ throw new JSchException("invalid privatekey: " + prvkey);
+ }
+
+ // The content of 'buf' will be changed, so it should be copied.
+ byte[] tmp = new byte[i - start];
+ System.arraycopy(buf, start, tmp, 0, tmp.length);
+ byte[] _buf = tmp;
+
+ start = 0;
+ i = 0;
+
+ int _len = _buf.length;
+ while (i < _len) {
+ if (_buf[i] == 0x0a) {
+ boolean xd = (_buf[i - 1] == 0x0d);
+ // ignore 0x0a (or 0x0d0x0a)
+ System.arraycopy(_buf, i + 1, _buf, i - (xd ? 1 : 0), _len - (i + 1));
+ if (xd) _len--;
+ _len--;
+ continue;
+ }
+ if (_buf[i] == '-') {
+ break;
+ }
+ i++;
+ }
+
+ if (i - start > 0)
+ data = Util.fromBase64(_buf, start, i - start);
+
+ Util.bzero(_buf);
+ }
+
+ if (data != null &&
+ data.length > 4 && // FSecure
+ data[0] == (byte) 0x3f &&
+ data[1] == (byte) 0x6f &&
+ data[2] == (byte) 0xf9 &&
+ data[3] == (byte) 0xeb) {
+
+ Buffer _buf = new Buffer(data);
+ _buf.getInt(); // 0x3f6ff9be
+ _buf.getInt();
+ byte[] _type = _buf.getString();
+ //System.err.println("type: "+new String(_type));
+ String _cipher = Util.byte2str(_buf.getString());
+ //System.err.println("cipher: "+_cipher);
+ if (_cipher.equals("3des-cbc")) {
+ _buf.getInt();
+ byte[] foo = new byte[data.length - _buf.getOffSet()];
+ _buf.getByte(foo);
+ data = foo;
+ encrypted = true;
+ throw new JSchException("unknown privatekey format: " + prvkey);
+ }
+ else if (_cipher.equals("none")) {
+ _buf.getInt();
+ _buf.getInt();
+
+ encrypted = false;
+
+ byte[] foo = new byte[data.length - _buf.getOffSet()];
+ _buf.getByte(foo);
+ data = foo;
+ }
+ }
+
+ if (pubkey != null) {
+ try {
+ buf = pubkey;
+ len = buf.length;
+ 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;
+ }
+
+ int 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 (prvkey == null || 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] == '-') {
+ if (prvkey == null &&
+ buf.length > 7) {
+ if (buf[4] == 'd') {
+ type = DSA;
+ }
+ else if (buf[4] == 'r') {
+ type = RSA;
+ }
+ }
+ i = 0;
+ while (i < len) {
+ if (buf[i] == ' ') break;
+ i++;
+ }
+ i++;
+ if (i < len) {
+ int start = i;
+ while (i < len) {
+ if (buf[i] == ' ') break;
+ i++;
+ }
+ publickeyblob = Util.fromBase64(buf, start, i - start);
+ }
+ if (i++ < len) {
+ int start = i;
+ while (i < len) {
+ if (buf[i] == '\n') break;
+ i++;
+ }
+ if (i > 0 && buf[i - 1] == 0x0d) i--;
+ if (start < i) {
+ publicKeyComment = new String(buf, start, i - start);
+ }
+ }
+ }
+ else if (buf[0] == 'e' && buf[1] == 'c' && buf[2] == 'd' && buf[3] == 's') {
+ if (prvkey == null && buf.length > 7) {
+ type = ECDSA;
+ }
+ i = 0;
+ while (i < len) {
+ if (buf[i] == ' ') break;
+ i++;
+ }
+ i++;
+ if (i < len) {
+ int start = i;
+ while (i < len) {
+ if (buf[i] == ' ') break;
+ i++;
+ }
+ publickeyblob = Util.fromBase64(buf, start, i - start);
+ }
+ if (i++ < len) {
+ int start = i;
+ while (i < len) {
+ if (buf[i] == '\n') break;
+ i++;
+ }
+ if (i > 0 && buf[i - 1] == 0x0d) i--;
+ if (start < i) {
+ publicKeyComment = new String(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);
+ }
+ else if (type == ECDSA) {
+ kpair = new KeyPairECDSA(jsch);
+ }
+ else if (vendor == VENDOR_PKCS8) {
+ kpair = new KeyPairPKCS8(jsch);
+ }
+
+ if (kpair != null) {
+ kpair.encrypted = encrypted;
+ kpair.publickeyblob = publickeyblob;
+ kpair.vendor = vendor;
+ kpair.publicKeyComment = publicKeyComment;
+ kpair.cipher = cipher;
+
+ if (encrypted) {
+ kpair.encrypted = true;
+ kpair.iv = iv;
+ kpair.data = data;
+ }
+ else {
+ if (kpair.parse(data)) {
+ kpair.encrypted = false;
+ 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();
+ }
+
+ private static final String[] header1 = {
+ "PuTTY-User-Key-File-2: ",
+ "Encryption: ",
+ "Comment: ",
+ "Public-Lines: "
+ };
+
+ private static final String[] header2 = {
+ "Private-Lines: "
+ };
+
+ private static final String[] header3 = {
+ "Private-MAC: "
+ };
+
+ static KeyPair loadPPK(JSch jsch, byte[] buf) throws JSchException {
+ byte[] pubkey = null;
+ byte[] prvkey = null;
+ int lines = 0;
+
+ Buffer buffer = new Buffer(buf);
+ java.util.Hashtable v = new java.util.Hashtable();
+
+ while (true) {
+ if (!parseHeader(buffer, v))
+ break;
+ }
+
+ String typ = (String) v.get("PuTTY-User-Key-File-2");
+ if (typ == null) {
+ return null;
+ }
+
+ lines = Integer.parseInt((String) v.get("Public-Lines"));
+ pubkey = parseLines(buffer, lines);
+
+ while (true) {
+ if (!parseHeader(buffer, v))
+ break;
+ }
+
+ lines = Integer.parseInt((String) v.get("Private-Lines"));
+ prvkey = parseLines(buffer, lines);
+
+ while (true) {
+ if (!parseHeader(buffer, v))
+ break;
+ }
+
+ prvkey = Util.fromBase64(prvkey, 0, prvkey.length);
+ pubkey = Util.fromBase64(pubkey, 0, pubkey.length);
+
+ KeyPair kpair = null;
+
+ if (typ.equals("ssh-rsa")) {
+
+ Buffer _buf = new Buffer(pubkey);
+ _buf.skip(pubkey.length);
+
+ int len = _buf.getInt();
+ _buf.getByte(new byte[len]); // ssh-rsa
+ byte[] pub_array = new byte[_buf.getInt()];
+ _buf.getByte(pub_array);
+ byte[] n_array = new byte[_buf.getInt()];
+ _buf.getByte(n_array);
+
+ kpair = new KeyPairRSA(jsch, n_array, pub_array, null);
+ }
+ else if (typ.equals("ssh-dss")) {
+ Buffer _buf = new Buffer(pubkey);
+ _buf.skip(pubkey.length);
+
+ int len = _buf.getInt();
+ _buf.getByte(new byte[len]); // ssh-dss
+
+ byte[] p_array = new byte[_buf.getInt()];
+ _buf.getByte(p_array);
+ byte[] q_array = new byte[_buf.getInt()];
+ _buf.getByte(q_array);
+ byte[] g_array = new byte[_buf.getInt()];
+ _buf.getByte(g_array);
+ byte[] y_array = new byte[_buf.getInt()];
+ _buf.getByte(y_array);
+
+ kpair = new KeyPairDSA(jsch, p_array, q_array, g_array, y_array, null);
+ }
+ else {
+ return null;
+ }
+
+ if (kpair == null)
+ return null;
+
+ kpair.encrypted = !v.get("Encryption").equals("none");
+ kpair.vendor = VENDOR_PUTTY;
+ kpair.publicKeyComment = (String) v.get("Comment");
+ if (kpair.encrypted) {
+ if (Session.checkCipher((String) jsch.getConfig("aes256-cbc"))) {
+ try {
+ Class c = Class.forName((String) jsch.getConfig("aes256-cbc"));
+ kpair.cipher = (Cipher) (c.newInstance());
+ kpair.iv = new byte[kpair.cipher.getIVSize()];
+ }
+ catch (Exception e) {
+ throw new JSchException("The cipher 'aes256-cbc' is required, but it is not available.");
+ }
+ }
+ else {
+ throw new JSchException("The cipher 'aes256-cbc' is required, but it is not available.");
+ }
+ kpair.data = prvkey;
+ }
+ else {
+ kpair.data = prvkey;
+ kpair.parse(prvkey);
+ }
+ return kpair;
+ }
+
+ private static byte[] parseLines(Buffer buffer, int lines) {
+ byte[] buf = buffer.buffer;
+ int index = buffer.index;
+ byte[] data = null;
+
+ int i = index;
+ while (lines-- > 0) {
+ while (buf.length > i) {
+ if (buf[i++] == 0x0d) {
+ if (data == null) {
+ data = new byte[i - index - 1];
+ System.arraycopy(buf, index, data, 0, i - index - 1);
+ }
+ else {
+ byte[] tmp = new byte[data.length + i - index - 1];
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ System.arraycopy(buf, index, tmp, data.length, i - index - 1);
+ for (int j = 0; j < data.length; j++) data[j] = 0; // clear
+ data = tmp;
+ }
+ break;
+ }
+ }
+ if (buf[i] == 0x0a)
+ i++;
+ index = i;
+ }
+
+ if (data != null)
+ buffer.index = index;
+
+ return data;
+ }
+
+ private static boolean parseHeader(Buffer buffer, java.util.Hashtable v) {
+ byte[] buf = buffer.buffer;
+ int index = buffer.index;
+ String key = null;
+ String value = null;
+ for (int i = index; i < buf.length; i++) {
+ if (buf[i] == 0x0d) {
+ break;
+ }
+ if (buf[i] == ':') {
+ key = new String(buf, index, i - index);
+ i++;
+ if (i < buf.length && buf[i] == ' ') {
+ i++;
+ }
+ index = i;
+ break;
+ }
+ }
+
+ if (key == null)
+ return false;
+
+ for (int i = index; i < buf.length; i++) {
+ if (buf[i] == 0x0d) {
+ value = new String(buf, index, i - index);
+ i++;
+ if (i < buf.length && buf[i] == 0x0a) {
+ i++;
+ }
+ index = i;
+ break;
+ }
+ }
+
+ if (value != null) {
+ v.put(key, value);
+ buffer.index = index;
+ }
+
+ return (key != null && value != null);
+ }
+
+ void copy(KeyPair kpair) {
+ this.publickeyblob = kpair.publickeyblob;
+ this.vendor = kpair.vendor;
+ this.publicKeyComment = kpair.publicKeyComment;
+ this.cipher = kpair.cipher;
+ }
+
+ class ASN1Exception extends Exception {
+ }
+
+ class ASN1 {
+ byte[] buf;
+ int start;
+ int length;
+
+ ASN1(byte[] buf) throws ASN1Exception {
+ this(buf, 0, buf.length);
+ }
+
+ ASN1(byte[] buf, int start, int length) throws ASN1Exception {
+ this.buf = buf;
+ this.start = start;
+ this.length = length;
+ if (start + length > buf.length)
+ throw new ASN1Exception();
+ }
+
+ int getType() {
+ return buf[start] & 0xff;
+ }
+
+ boolean isSEQUENCE() {
+ return getType() == (0x30 & 0xff);
+ }
+
+ boolean isINTEGER() {
+ return getType() == (0x02 & 0xff);
+ }
+
+ boolean isOBJECT() {
+ return getType() == (0x06 & 0xff);
+ }
+
+ boolean isOCTETSTRING() {
+ return getType() == (0x04 & 0xff);
+ }
+
+ private int getLength(int[] indexp) {
+ int index = indexp[0];
+ int length = buf[index++] & 0xff;
+ if ((length & 0x80) != 0) {
+ int foo = length & 0x7f;
+ length = 0;
+ while (foo-- > 0) {
+ length = (length << 8) + (buf[index++] & 0xff);
+ }
+ }
+ indexp[0] = index;
+ return length;
+ }
+
+ byte[] getContent() {
+ int[] indexp = new int[1];
+ indexp[0] = start + 1;
+ int length = getLength(indexp);
+ int index = indexp[0];
+ byte[] tmp = new byte[length];
+ System.arraycopy(buf, index, tmp, 0, tmp.length);
+ return tmp;
+ }
+
+ ASN1[] getContents() throws ASN1Exception {
+ int typ = buf[start];
+ int[] indexp = new int[1];
+ indexp[0] = start + 1;
+ int length = getLength(indexp);
+ if (typ == 0x05) {
+ return new ASN1[0];
+ }
+ int index = indexp[0];
+ java.util.Vector values = new java.util.Vector();
+ while (length > 0) {
+ index++;
+ length--;
+ int tmp = index;
+ indexp[0] = index;
+ int l = getLength(indexp);
+ index = indexp[0];
+ length -= (index - tmp);
+ values.addElement(new ASN1(buf, tmp - 1, 1 + (index - tmp) + l));
+ index += l;
+ length -= l;
+ }
+ ASN1[] result = new ASN1[values.size()];
+ for (int i = 0; i < values.size(); i++) {
+ result[i] = (ASN1) values.elementAt(i);
+ }
+ return result;
+ }
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/KeyPairDSA.java b/src/main/java/com/jcraft/jsch/KeyPairDSA.java
new file mode 100644
index 0000000..166c178
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/KeyPairDSA.java
@@ -0,0 +1,364 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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) {
+ this(jsch, null, null, null, null, null);
+ }
+
+ public KeyPairDSA(JSch jsch,
+ byte[] P_array,
+ byte[] Q_array,
+ byte[] G_array,
+ byte[] pub_array,
+ byte[] prv_array) {
+ super(jsch);
+ this.P_array = P_array;
+ this.Q_array = Q_array;
+ this.G_array = G_array;
+ this.pub_array = pub_array;
+ this.prv_array = prv_array;
+ if (P_array != null)
+ key_size = (new java.math.BigInteger(P_array)).bitLength();
+ }
+
+ 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();
+ if (P_array != null)
+ key_size = (new java.math.BigInteger(P_array)).bitLength();
+ return true;
+ }
+ return false;
+ }
+ else if (vendor == VENDOR_PUTTY) {
+ Buffer buf = new Buffer(plain);
+ buf.skip(plain.length);
+
+ try {
+ byte[][] tmp = buf.getBytes(1, "");
+ prv_array = tmp[0];
+ }
+ catch (JSchException e) {
+ return false;
+ }
+
+ 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;
+
+ if (P_array != null)
+ key_size = (new java.math.BigInteger(P_array)).bitLength();
+ }
+ 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;
+ byte[][] tmp = new byte[5][];
+ tmp[0] = sshdss;
+ tmp[1] = P_array;
+ tmp[2] = Q_array;
+ tmp[3] = G_array;
+ tmp[4] = pub_array;
+ return Buffer.fromBytes(tmp).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 byte[] getSignature(byte[] data) {
+ 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();
+ byte[][] tmp = new byte[2][];
+ tmp[0] = sshdss;
+ tmp[1] = sig;
+ return Buffer.fromBytes(tmp).buffer;
+ }
+ catch (Exception e) {
+ //System.err.println("e "+e);
+ }
+ return null;
+ }
+
+ public Signature getVerifier() {
+ try {
+ Class c = Class.forName((String) jsch.getConfig("signature.dss"));
+ SignatureDSA dsa = (SignatureDSA) (c.newInstance());
+ dsa.init();
+
+ if (pub_array == null && P_array == null && getPublicKeyBlob() != null) {
+ Buffer buf = new Buffer(getPublicKeyBlob());
+ buf.getString();
+ P_array = buf.getString();
+ Q_array = buf.getString();
+ G_array = buf.getString();
+ pub_array = buf.getString();
+ }
+
+ dsa.setPubKey(pub_array, P_array, Q_array, G_array);
+ return dsa;
+ }
+ catch (Exception e) {
+ //System.err.println("e "+e);
+ }
+ return null;
+ }
+
+ static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException {
+
+ byte[][] tmp = buf.getBytes(7, "invalid key format");
+
+ byte[] P_array = tmp[1];
+ byte[] Q_array = tmp[2];
+ byte[] G_array = tmp[3];
+ byte[] pub_array = tmp[4];
+ byte[] prv_array = tmp[5];
+ KeyPairDSA kpair = new KeyPairDSA(jsch,
+ P_array, Q_array, G_array,
+ pub_array, prv_array);
+ kpair.publicKeyComment = new String(tmp[6]);
+ kpair.vendor = VENDOR_OPENSSH;
+ return kpair;
+ }
+
+ public byte[] forSSHAgent() throws JSchException {
+ if (isEncrypted()) {
+ throw new JSchException("key is encrypted.");
+ }
+ Buffer buf = new Buffer();
+ buf.putString(sshdss);
+ buf.putString(P_array);
+ buf.putString(Q_array);
+ buf.putString(G_array);
+ buf.putString(pub_array);
+ buf.putString(prv_array);
+ buf.putString(Util.str2byte(publicKeyComment));
+ byte[] result = new byte[buf.getLength()];
+ buf.getByte(result, 0, result.length);
+ return result;
+ }
+
+ public void dispose() {
+ super.dispose();
+ Util.bzero(prv_array);
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/KeyPairECDSA.java b/src/main/java/com/jcraft/jsch/KeyPairECDSA.java
new file mode 100644
index 0000000..5d587ec
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/KeyPairECDSA.java
@@ -0,0 +1,414 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2015-2016 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 KeyPairECDSA extends KeyPair {
+
+ private static byte[][] oids = {
+ {(byte) 0x06, (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48, // 256
+ (byte) 0xce, (byte) 0x3d, (byte) 0x03, (byte) 0x01, (byte) 0x07},
+ {(byte) 0x06, (byte) 0x05, (byte) 0x2b, (byte) 0x81, (byte) 0x04, // 384
+ (byte) 0x00, (byte) 0x22},
+ {(byte) 0x06, (byte) 0x05, (byte) 0x2b, (byte) 0x81, (byte) 0x04, //521
+ (byte) 0x00, (byte) 0x23},
+ };
+
+ private static String[] names = {
+ "nistp256", "nistp384", "nistp521"
+ };
+
+ private byte[] name = Util.str2byte(names[0]);
+ private byte[] r_array;
+ private byte[] s_array;
+ private byte[] prv_array;
+
+ private int key_size = 256;
+
+ public KeyPairECDSA(JSch jsch) {
+ this(jsch, null, null, null, null);
+ }
+
+ public KeyPairECDSA(JSch jsch,
+ byte[] name,
+ byte[] r_array,
+ byte[] s_array,
+ byte[] prv_array) {
+ super(jsch);
+ if (name != null)
+ this.name = name;
+ this.r_array = r_array;
+ this.s_array = s_array;
+ this.prv_array = prv_array;
+ if (prv_array != null)
+ key_size = prv_array.length >= 64 ? 521 :
+ (prv_array.length >= 48 ? 384 : 256);
+ }
+
+ void generate(int key_size) throws JSchException {
+ this.key_size = key_size;
+ try {
+ Class c = Class.forName(jsch.getConfig("keypairgen.ecdsa"));
+ KeyPairGenECDSA keypairgen = (KeyPairGenECDSA) (c.newInstance());
+ keypairgen.init(key_size);
+ prv_array = keypairgen.getD();
+ r_array = keypairgen.getR();
+ s_array = keypairgen.getS();
+ name = Util.str2byte(names[prv_array.length >= 64 ? 2 :
+ (prv_array.length >= 48 ? 1 : 0)]);
+ keypairgen = null;
+ }
+ catch (Exception 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 EC PRIVATE KEY-----");
+ private static final byte[] end =
+ Util.str2byte("-----END EC PRIVATE KEY-----");
+
+ byte[] getBegin() {
+ return begin;
+ }
+
+ byte[] getEnd() {
+ return end;
+ }
+
+ byte[] getPrivateKey() {
+
+ byte[] tmp = new byte[1];
+ tmp[0] = 1;
+
+ byte[] oid = oids[
+ (r_array.length >= 64) ? 2 :
+ ((r_array.length >= 48) ? 1 : 0)
+ ];
+
+ byte[] point = toPoint(r_array, s_array);
+
+ int bar = ((point.length + 1) & 0x80) == 0 ? 3 : 4;
+ byte[] foo = new byte[point.length + bar];
+ System.arraycopy(point, 0, foo, bar, point.length);
+ foo[0] = 0x03; // BITSTRING
+ if (bar == 3) {
+ foo[1] = (byte) (point.length + 1);
+ }
+ else {
+ foo[1] = (byte) 0x81;
+ foo[2] = (byte) (point.length + 1);
+ }
+ point = foo;
+
+ int content =
+ 1 + countLength(tmp.length) + tmp.length +
+ 1 + countLength(prv_array.length) + prv_array.length +
+ 1 + countLength(oid.length) + oid.length +
+ 1 + countLength(point.length) + point.length;
+
+ int total =
+ 1 + countLength(content) + content; // SEQUENCE
+
+ byte[] plain = new byte[total];
+ int index = 0;
+ index = writeSEQUENCE(plain, index, content);
+ index = writeINTEGER(plain, index, tmp);
+ index = writeOCTETSTRING(plain, index, prv_array);
+ index = writeDATA(plain, (byte) 0xa0, index, oid);
+ index = writeDATA(plain, (byte) 0xa1, index, point);
+
+ return plain;
+ }
+
+ boolean parse(byte[] plain) {
+ try {
+
+ if (vendor == VENDOR_FSECURE) {
+ /*
+ if(plain[0]!=0x30){ // FSecure
+ return true;
+ }
+ return false;
+ */
+ return false;
+ }
+ else if (vendor == VENDOR_PUTTY) {
+ /*
+ Buffer buf=new Buffer(plain);
+ buf.skip(plain.length);
+
+ try {
+ byte[][] tmp = buf.getBytes(1, "");
+ prv_array = tmp[0];
+ }
+ catch(JSchException e){
+ return false;
+ }
+
+ 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++; // 0x04
+
+ 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;
+
+ index++; // 0xa0
+
+ length = plain[index++] & 0xff;
+ if ((length & 0x80) != 0) {
+ int foo = length & 0x7f;
+ length = 0;
+ while (foo-- > 0) {
+ length = (length << 8) + (plain[index++] & 0xff);
+ }
+ }
+
+ byte[] oid_array = new byte[length];
+ System.arraycopy(plain, index, oid_array, 0, length);
+ index += length;
+
+ for (int i = 0; i < oids.length; i++) {
+ if (Util.array_equals(oids[i], oid_array)) {
+ name = Util.str2byte(names[i]);
+ break;
+ }
+ }
+
+ index++; // 0xa1
+
+ length = plain[index++] & 0xff;
+ if ((length & 0x80) != 0) {
+ int foo = length & 0x7f;
+ length = 0;
+ while (foo-- > 0) {
+ length = (length << 8) + (plain[index++] & 0xff);
+ }
+ }
+
+ byte[] Q_array = new byte[length];
+ System.arraycopy(plain, index, Q_array, 0, length);
+ index += length;
+
+ byte[][] tmp = fromPoint(Q_array);
+ r_array = tmp[0];
+ s_array = tmp[1];
+
+ if (prv_array != null)
+ key_size = prv_array.length >= 64 ? 521 :
+ (prv_array.length >= 48 ? 384 : 256);
+ }
+ 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 (r_array == null) return null;
+
+ byte[][] tmp = new byte[3][];
+ tmp[0] = Util.str2byte("ecdsa-sha2-" + new String(name));
+ tmp[1] = name;
+ tmp[2] = new byte[1 + r_array.length + s_array.length];
+ tmp[2][0] = 4; // POINT_CONVERSION_UNCOMPRESSED
+ System.arraycopy(r_array, 0, tmp[2], 1, r_array.length);
+ System.arraycopy(s_array, 0, tmp[2], 1 + r_array.length, s_array.length);
+
+ return Buffer.fromBytes(tmp).buffer;
+ }
+
+ byte[] getKeyTypeName() {
+ return Util.str2byte("ecdsa-sha2-" + new String(name));
+ }
+
+ public int getKeyType() {
+ return ECDSA;
+ }
+
+ public int getKeySize() {
+ return key_size;
+ }
+
+ public byte[] getSignature(byte[] data) {
+ try {
+ Class c = Class.forName((String) jsch.getConfig("signature.ecdsa"));
+ SignatureECDSA ecdsa = (SignatureECDSA) (c.newInstance());
+ ecdsa.init();
+ ecdsa.setPrvKey(prv_array);
+
+ ecdsa.update(data);
+ byte[] sig = ecdsa.sign();
+
+ byte[][] tmp = new byte[2][];
+ tmp[0] = Util.str2byte("ecdsa-sha2-" + new String(name));
+ tmp[1] = sig;
+ return Buffer.fromBytes(tmp).buffer;
+ }
+ catch (Exception e) {
+ //System.err.println("e "+e);
+ }
+ return null;
+ }
+
+ public Signature getVerifier() {
+ try {
+ Class c = Class.forName((String) jsch.getConfig("signature.ecdsa"));
+ final SignatureECDSA ecdsa = (SignatureECDSA) (c.newInstance());
+ ecdsa.init();
+
+ if (r_array == null && s_array == null && getPublicKeyBlob() != null) {
+ Buffer buf = new Buffer(getPublicKeyBlob());
+ buf.getString(); // ecdsa-sha2-nistp256
+ buf.getString(); // nistp256
+ byte[][] tmp = fromPoint(buf.getString());
+ r_array = tmp[0];
+ s_array = tmp[1];
+ }
+ ecdsa.setPubKey(r_array, s_array);
+ return ecdsa;
+ }
+ catch (Exception e) {
+ //System.err.println("e "+e);
+ }
+ return null;
+ }
+
+ static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException {
+
+ byte[][] tmp = buf.getBytes(5, "invalid key format");
+
+ byte[] name = tmp[1]; // nistp256
+ byte[][] foo = fromPoint(tmp[2]);
+ byte[] r_array = foo[0];
+ byte[] s_array = foo[1];
+
+ byte[] prv_array = tmp[3];
+ KeyPairECDSA kpair = new KeyPairECDSA(jsch,
+ name,
+ r_array, s_array,
+ prv_array);
+ kpair.publicKeyComment = new String(tmp[4]);
+ kpair.vendor = VENDOR_OPENSSH;
+ return kpair;
+ }
+
+ public byte[] forSSHAgent() throws JSchException {
+ if (isEncrypted()) {
+ throw new JSchException("key is encrypted.");
+ }
+ Buffer buf = new Buffer();
+ buf.putString(Util.str2byte("ecdsa-sha2-" + new String(name)));
+ buf.putString(name);
+ buf.putString(toPoint(r_array, s_array));
+ buf.putString(prv_array);
+ buf.putString(Util.str2byte(publicKeyComment));
+ byte[] result = new byte[buf.getLength()];
+ buf.getByte(result, 0, result.length);
+ return result;
+ }
+
+ static byte[] toPoint(byte[] r_array, byte[] s_array) {
+ byte[] tmp = new byte[1 + r_array.length + s_array.length];
+ tmp[0] = 0x04;
+ System.arraycopy(r_array, 0, tmp, 1, r_array.length);
+ System.arraycopy(s_array, 0, tmp, 1 + r_array.length, s_array.length);
+ return tmp;
+ }
+
+ static byte[][] fromPoint(byte[] point) {
+ int i = 0;
+ while (point[i] != 4) i++;
+ i++;
+ byte[][] tmp = new byte[2][];
+ byte[] r_array = new byte[(point.length - i) / 2];
+ byte[] s_array = new byte[(point.length - i) / 2];
+ // point[0] == 0x04 == POINT_CONVERSION_UNCOMPRESSED
+ System.arraycopy(point, i, r_array, 0, r_array.length);
+ System.arraycopy(point, i + r_array.length, s_array, 0, s_array.length);
+ tmp[0] = r_array;
+ tmp[1] = s_array;
+
+ return tmp;
+ }
+
+ public void dispose() {
+ super.dispose();
+ Util.bzero(prv_array);
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/KeyPairGenDSA.java b/src/main/java/com/jcraft/jsch/KeyPairGenDSA.java
new file mode 100644
index 0000000..a3ef3d5
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/KeyPairGenDSA.java
@@ -0,0 +1,44 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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/main/java/com/jcraft/jsch/KeyPairGenECDSA.java b/src/main/java/com/jcraft/jsch/KeyPairGenECDSA.java
new file mode 100644
index 0000000..6e50c26
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/KeyPairGenECDSA.java
@@ -0,0 +1,40 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2015-2016 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 KeyPairGenECDSA {
+ void init(int key_size) throws Exception;
+
+ byte[] getD();
+
+ byte[] getR();
+
+ byte[] getS();
+}
diff --git a/src/main/java/com/jcraft/jsch/KeyPairGenRSA.java b/src/main/java/com/jcraft/jsch/KeyPairGenRSA.java
new file mode 100644
index 0000000..125dd7a
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/KeyPairGenRSA.java
@@ -0,0 +1,50 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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/main/java/com/jcraft/jsch/KeyPairPKCS8.java b/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java
new file mode 100644
index 0000000..000eb55
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java
@@ -0,0 +1,373 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2013-2016 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.math.BigInteger;
+import java.util.Vector;
+
+public class KeyPairPKCS8 extends KeyPair {
+ private static final byte[] rsaEncryption = {
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+ (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01
+ };
+
+ private static final byte[] dsaEncryption = {
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce,
+ (byte) 0x38, (byte) 0x04, (byte) 0x1
+ };
+
+ private static final byte[] pbes2 = {
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
+ (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0d
+ };
+
+ private static final byte[] pbkdf2 = {
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
+ (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0c
+ };
+
+ private static final byte[] aes128cbc = {
+ (byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01, (byte) 0x65,
+ (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x02
+ };
+
+ private static final byte[] aes192cbc = {
+ (byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01, (byte) 0x65,
+ (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x16
+ };
+
+ private static final byte[] aes256cbc = {
+ (byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01, (byte) 0x65,
+ (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x2a
+ };
+
+ private static final byte[] pbeWithMD5AndDESCBC = {
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
+ (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x03
+ };
+
+ private KeyPair kpair = null;
+
+ public KeyPairPKCS8(JSch jsch) {
+ super(jsch);
+ }
+
+ void generate(int key_size) throws JSchException {
+ }
+
+ 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() {
+ return null;
+ }
+
+ boolean parse(byte[] plain) {
+
+ /* from RFC5208
+ PrivateKeyInfo ::= SEQUENCE {
+ version Version,
+ privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ privateKey PrivateKey,
+ attributes [0] IMPLICIT Attributes OPTIONAL
+ }
+ Version ::= INTEGER
+ PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+ PrivateKey ::= OCTET STRING
+ Attributes ::= SET OF Attribute
+ }
+ */
+
+ try {
+ Vector values = new Vector();
+
+ ASN1[] contents = null;
+ ASN1 asn1 = new ASN1(plain);
+ contents = asn1.getContents();
+
+ ASN1 privateKeyAlgorithm = contents[1];
+ ASN1 privateKey = contents[2];
+
+ contents = privateKeyAlgorithm.getContents();
+ byte[] privateKeyAlgorithmID = contents[0].getContent();
+ contents = contents[1].getContents();
+ if (contents.length > 0) {
+ for (int i = 0; i < contents.length; i++) {
+ values.addElement(contents[i].getContent());
+ }
+ }
+
+ byte[] _data = privateKey.getContent();
+
+ KeyPair _kpair = null;
+ if (Util.array_equals(privateKeyAlgorithmID, rsaEncryption)) {
+ _kpair = new KeyPairRSA(jsch);
+ _kpair.copy(this);
+ if (_kpair.parse(_data)) {
+ kpair = _kpair;
+ }
+ }
+ else if (Util.array_equals(privateKeyAlgorithmID, dsaEncryption)) {
+ asn1 = new ASN1(_data);
+ if (values.size() == 0) { // embedded DSA parameters format
+ /*
+ SEQUENCE
+ SEQUENCE
+ INTEGER // P_array
+ INTEGER // Q_array
+ INTEGER // G_array
+ INTEGER // prv_array
+ */
+ contents = asn1.getContents();
+ byte[] bar = contents[1].getContent();
+ contents = contents[0].getContents();
+ for (int i = 0; i < contents.length; i++) {
+ values.addElement(contents[i].getContent());
+ }
+ values.addElement(bar);
+ }
+ else {
+ /*
+ INTEGER // prv_array
+ */
+ values.addElement(asn1.getContent());
+ }
+
+ byte[] P_array = (byte[]) values.elementAt(0);
+ byte[] Q_array = (byte[]) values.elementAt(1);
+ byte[] G_array = (byte[]) values.elementAt(2);
+ byte[] prv_array = (byte[]) values.elementAt(3);
+ // Y = g^X mode p
+ byte[] pub_array =
+ (new BigInteger(G_array)).
+ modPow(new BigInteger(prv_array), new BigInteger(P_array)).
+ toByteArray();
+
+ KeyPairDSA _key = new KeyPairDSA(jsch,
+ P_array, Q_array, G_array,
+ pub_array, prv_array);
+ plain = _key.getPrivateKey();
+
+ _kpair = new KeyPairDSA(jsch);
+ _kpair.copy(this);
+ if (_kpair.parse(plain)) {
+ kpair = _kpair;
+ }
+ }
+ }
+ catch (ASN1Exception e) {
+ return false;
+ }
+ catch (Exception e) {
+ //System.err.println(e);
+ return false;
+ }
+ return kpair != null;
+ }
+
+ public byte[] getPublicKeyBlob() {
+ return kpair.getPublicKeyBlob();
+ }
+
+ byte[] getKeyTypeName() {
+ return kpair.getKeyTypeName();
+ }
+
+ public int getKeyType() {
+ return kpair.getKeyType();
+ }
+
+ public int getKeySize() {
+ return kpair.getKeySize();
+ }
+
+ public byte[] getSignature(byte[] data) {
+ return kpair.getSignature(data);
+ }
+
+ public Signature getVerifier() {
+ return kpair.getVerifier();
+ }
+
+ public byte[] forSSHAgent() throws JSchException {
+ return kpair.forSSHAgent();
+ }
+
+ public boolean decrypt(byte[] _passphrase) {
+ if (!isEncrypted()) {
+ return true;
+ }
+ if (_passphrase == null) {
+ return !isEncrypted();
+ }
+
+ /*
+ SEQUENCE
+ SEQUENCE
+ OBJECT :PBES2
+ SEQUENCE
+ SEQUENCE
+ OBJECT :PBKDF2
+ SEQUENCE
+ OCTET STRING [HEX DUMP]:E4E24ADC9C00BD4D
+ INTEGER :0800
+ SEQUENCE
+ OBJECT :aes-128-cbc
+ OCTET STRING [HEX DUMP]:5B66E6B3BF03944C92317BC370CC3AD0
+ OCTET STRING [HEX DUMP]:
+
+or
+
+ SEQUENCE
+ SEQUENCE
+ OBJECT :pbeWithMD5AndDES-CBC
+ SEQUENCE
+ OCTET STRING [HEX DUMP]:DBF75ECB69E3C0FC
+ INTEGER :0800
+ OCTET STRING [HEX DUMP]
+ */
+
+ try {
+
+ ASN1[] contents = null;
+ ASN1 asn1 = new ASN1(data);
+
+ contents = asn1.getContents();
+
+ byte[] _data = contents[1].getContent();
+
+ ASN1 pbes = contents[0];
+ contents = pbes.getContents();
+ byte[] pbesid = contents[0].getContent();
+ ASN1 pbesparam = contents[1];
+
+ byte[] salt = null;
+ int iterations = 0;
+ byte[] iv = null;
+ byte[] encryptfuncid = null;
+
+ if (Util.array_equals(pbesid, pbes2)) {
+ contents = pbesparam.getContents();
+ ASN1 pbkdf = contents[0];
+ ASN1 encryptfunc = contents[1];
+ contents = pbkdf.getContents();
+ byte[] pbkdfid = contents[0].getContent();
+ ASN1 pbkdffunc = contents[1];
+ contents = pbkdffunc.getContents();
+ salt = contents[0].getContent();
+ iterations =
+ Integer.parseInt((new BigInteger(contents[1].getContent())).toString());
+
+ contents = encryptfunc.getContents();
+ encryptfuncid = contents[0].getContent();
+ iv = contents[1].getContent();
+ }
+ else if (Util.array_equals(pbesid, pbeWithMD5AndDESCBC)) {
+ // not supported
+ return false;
+ }
+ else {
+ return false;
+ }
+
+ Cipher cipher = getCipher(encryptfuncid);
+ if (cipher == null) return false;
+
+ byte[] key = null;
+ try {
+ Class c = Class.forName((String) jsch.getConfig("pbkdf"));
+ PBKDF tmp = (PBKDF) (c.newInstance());
+ key = tmp.getKey(_passphrase, salt, iterations, cipher.getBlockSize());
+ }
+ catch (Exception ee) {
+ }
+
+ if (key == null) {
+ return false;
+ }
+
+ cipher.init(Cipher.DECRYPT_MODE, key, iv);
+ Util.bzero(key);
+ byte[] plain = new byte[_data.length];
+ cipher.update(_data, 0, _data.length, plain, 0);
+ if (parse(plain)) {
+ encrypted = false;
+ return true;
+ }
+ }
+ catch (ASN1Exception e) {
+ // System.err.println(e);
+ }
+ catch (Exception e) {
+ // System.err.println(e);
+ }
+
+ return false;
+ }
+
+ Cipher getCipher(byte[] id) {
+ Cipher cipher = null;
+ String name = null;
+ try {
+ if (Util.array_equals(id, aes128cbc)) {
+ name = "aes128-cbc";
+ }
+ else if (Util.array_equals(id, aes192cbc)) {
+ name = "aes192-cbc";
+ }
+ else if (Util.array_equals(id, aes256cbc)) {
+ name = "aes256-cbc";
+ }
+ Class c = Class.forName((String) jsch.getConfig(name));
+ cipher = (Cipher) (c.newInstance());
+ }
+ catch (Exception e) {
+ if (JSch.getLogger().isEnabled(Logger.FATAL)) {
+ String message = "";
+ if (name == null) {
+ message = "unknown oid: " + Util.toHex(id);
+ }
+ else {
+ message = "function " + name + " is not supported";
+ }
+ JSch.getLogger().log(Logger.FATAL, "PKCS8: " + message);
+ }
+ }
+ return cipher;
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/KeyPairRSA.java b/src/main/java/com/jcraft/jsch/KeyPairRSA.java
new file mode 100644
index 0000000..f70ca35
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/KeyPairRSA.java
@@ -0,0 +1,459 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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.math.BigInteger;
+
+public class KeyPairRSA extends KeyPair {
+ private byte[] n_array; // modulus p multiply q
+ private byte[] pub_array; // e
+ private byte[] prv_array; // d e^-1 mod (p-1)(q-1)
+
+ private byte[] p_array; // prime p
+ private byte[] q_array; // prime q
+ private byte[] ep_array; // prime exponent p dmp1 == prv mod (p-1)
+ private byte[] eq_array; // prime exponent q dmq1 == prv mod (q-1)
+ private byte[] c_array; // coefficient iqmp == modinv(q, p) == q^-1 mod p
+
+ private int key_size = 1024;
+
+ public KeyPairRSA(JSch jsch) {
+ this(jsch, null, null, null);
+ }
+
+ public KeyPairRSA(JSch jsch,
+ byte[] n_array,
+ byte[] pub_array,
+ byte[] prv_array) {
+ super(jsch);
+ this.n_array = n_array;
+ this.pub_array = pub_array;
+ this.prv_array = prv_array;
+ if (n_array != null) {
+ key_size = (new java.math.BigInteger(n_array)).bitLength();
+ }
+ }
+
+ 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) {
+
+ try {
+ int index = 0;
+ int length = 0;
+
+ if (vendor == VENDOR_PUTTY) {
+ Buffer buf = new Buffer(plain);
+ buf.skip(plain.length);
+
+ try {
+ byte[][] tmp = buf.getBytes(4, "");
+ prv_array = tmp[0];
+ p_array = tmp[1];
+ q_array = tmp[2];
+ c_array = tmp[3];
+ }
+ catch (JSchException e) {
+ return false;
+ }
+
+ getEPArray();
+ getEQArray();
+
+ return true;
+ }
+
+ 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();
+ if (n_array != null) {
+ key_size = (new java.math.BigInteger(n_array)).bitLength();
+ }
+
+ getEPArray();
+ getEQArray();
+ getCArray();
+
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ Key must be in the following ASN.1 DER encoding,
+ RSAPrivateKey ::= SEQUENCE {
+ version Version,
+ modulus INTEGER, -- n
+ publicExponent INTEGER, -- e
+ privateExponent INTEGER, -- d
+ prime1 INTEGER, -- p
+ prime2 INTEGER, -- q
+ exponent1 INTEGER, -- d mod (p-1)
+ exponent2 INTEGER, -- d mod (q-1)
+ coefficient INTEGER, -- (inverse of q) mod p
+ otherPrimeInfos OtherPrimeInfos OPTIONAL
+ }
+ */
+
+ 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);
+ }
+ }
+ n_array = new byte[length];
+ System.arraycopy(plain, index, n_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;
+
+ 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);
+ }
+ }
+ ep_array = new byte[length];
+ System.arraycopy(plain, index, ep_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);
+ }
+ }
+ eq_array = new byte[length];
+ System.arraycopy(plain, index, eq_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);
+ }
+ }
+ c_array = new byte[length];
+ System.arraycopy(plain, index, c_array, 0, length);
+ index += length;
+
+ if (n_array != null) {
+ key_size = (new java.math.BigInteger(n_array)).bitLength();
+ }
+
+ }
+ 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;
+ byte[][] tmp = new byte[3][];
+ tmp[0] = sshrsa;
+ tmp[1] = pub_array;
+ tmp[2] = n_array;
+ return Buffer.fromBytes(tmp).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 byte[] getSignature(byte[] data) {
+ try {
+ Class c = Class.forName((String) jsch.getConfig("signature.rsa"));
+ SignatureRSA rsa = (SignatureRSA) (c.newInstance());
+ rsa.init();
+ rsa.setPrvKey(prv_array, n_array);
+
+ rsa.update(data);
+ byte[] sig = rsa.sign();
+ byte[][] tmp = new byte[2][];
+ tmp[0] = sshrsa;
+ tmp[1] = sig;
+ return Buffer.fromBytes(tmp).buffer;
+ }
+ catch (Exception e) {
+ }
+ return null;
+ }
+
+ public Signature getVerifier() {
+ try {
+ Class c = Class.forName((String) jsch.getConfig("signature.rsa"));
+ SignatureRSA rsa = (SignatureRSA) (c.newInstance());
+ rsa.init();
+
+ if (pub_array == null && n_array == null && getPublicKeyBlob() != null) {
+ Buffer buf = new Buffer(getPublicKeyBlob());
+ buf.getString();
+ pub_array = buf.getString();
+ n_array = buf.getString();
+ }
+
+ rsa.setPubKey(pub_array, n_array);
+ return rsa;
+ }
+ catch (Exception e) {
+ }
+ return null;
+ }
+
+ static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException {
+
+ byte[][] tmp = buf.getBytes(8, "invalid key format");
+
+ byte[] n_array = tmp[1];
+ byte[] pub_array = tmp[2];
+ byte[] prv_array = tmp[3];
+ KeyPairRSA kpair = new KeyPairRSA(jsch, n_array, pub_array, prv_array);
+ kpair.c_array = tmp[4]; // iqmp
+ kpair.p_array = tmp[5];
+ kpair.q_array = tmp[6];
+ kpair.publicKeyComment = new String(tmp[7]);
+ kpair.vendor = VENDOR_OPENSSH;
+ return kpair;
+ }
+
+ public byte[] forSSHAgent() throws JSchException {
+ if (isEncrypted()) {
+ throw new JSchException("key is encrypted.");
+ }
+ Buffer buf = new Buffer();
+ buf.putString(sshrsa);
+ buf.putString(n_array);
+ buf.putString(pub_array);
+ buf.putString(prv_array);
+ buf.putString(getCArray());
+ buf.putString(p_array);
+ buf.putString(q_array);
+ buf.putString(Util.str2byte(publicKeyComment));
+ byte[] result = new byte[buf.getLength()];
+ buf.getByte(result, 0, result.length);
+ return result;
+ }
+
+ private byte[] getEPArray() {
+ if (ep_array == null) {
+ ep_array = (new BigInteger(prv_array)).mod(new BigInteger(p_array).subtract(BigInteger.ONE)).toByteArray();
+ }
+ return ep_array;
+ }
+
+ private byte[] getEQArray() {
+ if (eq_array == null) {
+ eq_array = (new BigInteger(prv_array)).mod(new BigInteger(q_array).subtract(BigInteger.ONE)).toByteArray();
+ }
+ return eq_array;
+ }
+
+ private byte[] getCArray() {
+ if (c_array == null) {
+ c_array = (new BigInteger(q_array)).modInverse(new BigInteger(p_array)).toByteArray();
+ }
+ return c_array;
+ }
+
+ public void dispose() {
+ super.dispose();
+ Util.bzero(prv_array);
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/KnownHosts.java b/src/main/java/com/jcraft/jsch/KnownHosts.java
new file mode 100644
index 0000000..c10d94a
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/KnownHosts.java
@@ -0,0 +1,655 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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";
+
+ 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;
+ this.hmacsha1 = getHMACSHA1();
+ pool = new java.util.Vector();
+ }
+
+ void setKnownHosts(String filename) throws JSchException {
+ try {
+ known_hosts = filename;
+ FileInputStream fis = new FileInputStream(Util.checkTilde(filename));
+ setKnownHosts(fis);
+ }
+ catch (FileNotFoundException e) {
+ // The non-existing file should be allowed.
+ }
+ }
+
+ void setKnownHosts(InputStream input) throws JSchException {
+ pool.removeAllElements();
+ StringBuffer sb = new StringBuffer();
+ byte i;
+ int j;
+ boolean error = false;
+ try {
+ InputStream fis = input;
+ 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;
+ }
+
+ while (j < bufl) {
+ i = buf[j];
+ if (i == ' ' || i == '\t') {
+ j++;
+ continue;
+ }
+ break;
+ }
+
+ String marker = "";
+ if (host.charAt(0) == '@') {
+ marker = host;
+
+ 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;
+ }
+
+ while (j < bufl) {
+ i = buf[j];
+ if (i == ' ' || i == '\t') {
+ j++;
+ continue;
+ }
+ break;
+ }
+ }
+
+ sb.setLength(0);
+ type = -1;
+ while (j < bufl) {
+ i = buf[j++];
+ if (i == 0x20 || i == '\t') {
+ break;
+ }
+ sb.append((char) i);
+ }
+ String tmp = sb.toString();
+ if (HostKey.name2type(tmp) != HostKey.UNKNOWN) {
+ type = HostKey.name2type(tmp);
+ }
+ else {
+ j = bufl;
+ }
+ if (j >= bufl) {
+ addInvalidLine(Util.byte2str(buf, 0, bufl));
+ continue loop;
+ }
+
+ while (j < bufl) {
+ i = buf[j];
+ if (i == ' ' || i == '\t') {
+ j++;
+ continue;
+ }
+ break;
+ }
+
+ sb.setLength(0);
+ while (j < bufl) {
+ i = buf[j++];
+ if (i == 0x0d) {
+ continue;
+ }
+ if (i == 0x0a) {
+ break;
+ }
+ if (i == 0x20 || i == '\t') {
+ break;
+ }
+ sb.append((char) i);
+ }
+ key = sb.toString();
+ if (key.length() == 0) {
+ addInvalidLine(Util.byte2str(buf, 0, bufl));
+ continue loop;
+ }
+
+ while (j < bufl) {
+ i = buf[j];
+ if (i == ' ' || i == '\t') {
+ j++;
+ continue;
+ }
+ break;
+ }
+
+ /**
+ "man sshd" has following descriptions,
+ Note that the lines in these files are typically hundreds
+ of characters long, and you definitely don't want to type
+ in the host keys by hand. Rather, generate them by a script,
+ ssh-keyscan(1) or by taking /usr/local/etc/ssh_host_key.pub and
+ adding the host names at the front.
+ This means that a comment is allowed to appear at the end of each
+ key entry.
+ */
+ String comment = null;
+ if (j < bufl) {
+ sb.setLength(0);
+ while (j < bufl) {
+ i = buf[j++];
+ if (i == 0x0d) {
+ continue;
+ }
+ if (i == 0x0a) {
+ break;
+ }
+ sb.append((char) i);
+ }
+ comment = sb.toString();
+ }
+
+ //System.err.println(host);
+ //System.err.println("|"+key+"|");
+
+ HostKey hk = null;
+ hk = new HashedHostKey(marker, host, type,
+ Util.fromBase64(Util.str2byte(key), 0,
+ key.length()), comment);
+ pool.addElement(hk);
+ }
+ 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());
+ }
+ finally {
+ try {
+ input.close();
+ }
+ catch (IOException e) {
+ throw new JSchException(e.toString(), (Throwable) e);
+ }
+ }
+ }
+
+ 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;
+ }
+
+ HostKey hk = null;
+ try {
+ hk = new HostKey(host, HostKey.GUESS, key);
+ }
+ catch (JSchException e) { // unsupported key
+ return result;
+ }
+
+ synchronized (pool) {
+ for (int i = 0; i < pool.size(); i++) {
+ HostKey _hk = (HostKey) (pool.elementAt(i));
+ if (_hk.isMatched(host) && _hk.type == hk.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(Util.checkTilde(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, (String) null);
+ }
+
+ public HostKey[] getHostKey(String host, String type) {
+ synchronized (pool) {
+ java.util.ArrayList v = new java.util.ArrayList();
+ 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)))) {
+ v.add(hk);
+ }
+ }
+ HostKey[] foo = new HostKey[v.size()];
+ for (int i = 0; i < v.size(); i++) {
+ foo[i] = (HostKey) v.get(i);
+ }
+ if (host != null && host.startsWith("[") && host.indexOf("]:") > 1) {
+ HostKey[] tmp =
+ getHostKey(host.substring(1, host.indexOf("]:")), type);
+ if (tmp.length > 0) {
+ HostKey[] bar = new HostKey[foo.length + tmp.length];
+ System.arraycopy(foo, 0, bar, 0, foo.length);
+ System.arraycopy(tmp, 0, bar, foo.length, tmp.length);
+ foo = bar;
+ }
+ }
+ 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(Util.checkTilde(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 marker = hk.getMarker();
+ String host = hk.getHost();
+ String type = hk.getType();
+ String comment = hk.getComment();
+ if (type.equals("UNKNOWN")) {
+ out.write(Util.str2byte(host));
+ out.write(cr);
+ continue;
+ }
+ if (marker.length() != 0) {
+ out.write(Util.str2byte(marker));
+ out.write(space);
+ }
+ out.write(Util.str2byte(host));
+ out.write(space);
+ out.write(Util.str2byte(type));
+ out.write(space);
+ out.write(Util.str2byte(hk.getKey()));
+ if (comment != null) {
+ out.write(space);
+ out.write(Util.str2byte(comment));
+ }
+ out.write(cr);
+ }
+ }
+ }
+ catch (Exception e) {
+ System.err.println(e);
+ }
+ }
+
+ 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 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 {
+ this("", host, type, key, null);
+ }
+
+ HashedHostKey(String marker, String host, int type, byte[] key, String comment) throws JSchException {
+ super(marker, host, type, key, comment);
+ 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/main/java/com/jcraft/jsch/LocalIdentityRepository.java b/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java
new file mode 100644
index 0000000..3acff2d
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java
@@ -0,0 +1,151 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2012-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.util.Vector;
+
+class LocalIdentityRepository implements IdentityRepository {
+ private static final String name = "Local Identity Repository";
+
+ private Vector identities = new Vector();
+ private JSch jsch;
+
+ LocalIdentityRepository(JSch jsch) {
+ this.jsch = jsch;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getStatus() {
+ return RUNNING;
+ }
+
+ public synchronized Vector getIdentities() {
+ removeDupulicates();
+ Vector v = new Vector();
+ for (int i = 0; i < identities.size(); i++) {
+ v.addElement(identities.elementAt(i));
+ }
+ return v;
+ }
+
+ public synchronized void add(Identity identity) {
+ if (!identities.contains(identity)) {
+ byte[] blob1 = identity.getPublicKeyBlob();
+ if (blob1 == null) {
+ identities.addElement(identity);
+ return;
+ }
+ for (int i = 0; i < identities.size(); i++) {
+ byte[] blob2 = ((Identity) identities.elementAt(i)).getPublicKeyBlob();
+ if (blob2 != null && Util.array_equals(blob1, blob2)) {
+ if (!identity.isEncrypted() &&
+ ((Identity) identities.elementAt(i)).isEncrypted()) {
+ remove(blob2);
+ }
+ else {
+ return;
+ }
+ }
+ }
+ identities.addElement(identity);
+ }
+ }
+
+ public synchronized boolean add(byte[] identity) {
+ try {
+ Identity _identity =
+ IdentityFile.newInstance("from remote:", identity, null, jsch);
+ add(_identity);
+ return true;
+ }
+ catch (JSchException e) {
+ return false;
+ }
+ }
+
+ synchronized void remove(Identity identity) {
+ if (identities.contains(identity)) {
+ identities.removeElement(identity);
+ identity.clear();
+ }
+ else {
+ remove(identity.getPublicKeyBlob());
+ }
+ }
+
+ public synchronized boolean remove(byte[] blob) {
+ if (blob == null) return false;
+ for (int i = 0; i < identities.size(); i++) {
+ Identity _identity = (Identity) (identities.elementAt(i));
+ byte[] _blob = _identity.getPublicKeyBlob();
+ if (_blob == null || !Util.array_equals(blob, _blob))
+ continue;
+ identities.removeElement(_identity);
+ _identity.clear();
+ return true;
+ }
+ return false;
+ }
+
+ public synchronized void removeAll() {
+ for (int i = 0; i < identities.size(); i++) {
+ Identity identity = (Identity) (identities.elementAt(i));
+ identity.clear();
+ }
+ identities.removeAllElements();
+ }
+
+ private void removeDupulicates() {
+ Vector v = new Vector();
+ int len = identities.size();
+ if (len == 0) return;
+ for (int i = 0; i < len; i++) {
+ Identity foo = (Identity) identities.elementAt(i);
+ byte[] foo_blob = foo.getPublicKeyBlob();
+ if (foo_blob == null) continue;
+ for (int j = i + 1; j < len; j++) {
+ Identity bar = (Identity) identities.elementAt(j);
+ byte[] bar_blob = bar.getPublicKeyBlob();
+ if (bar_blob == null) continue;
+ if (Util.array_equals(foo_blob, bar_blob) &&
+ foo.isEncrypted() == bar.isEncrypted()) {
+ v.addElement(foo_blob);
+ break;
+ }
+ }
+ }
+ for (int i = 0; i < v.size(); i++) {
+ remove((byte[]) v.elementAt(i));
+ }
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/Logger.java b/src/main/java/com/jcraft/jsch/Logger.java
new file mode 100644
index 0000000..211998d
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/Logger.java
@@ -0,0 +1,54 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2016 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/main/java/com/jcraft/jsch/MAC.java b/src/main/java/com/jcraft/jsch/MAC.java
new file mode 100644
index 0000000..718bb7d
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/MAC.java
@@ -0,0 +1,44 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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/main/java/com/jcraft/jsch/OpenSSHConfig.java b/src/main/java/com/jcraft/jsch/OpenSSHConfig.java
new file mode 100644
index 0000000..bdbbdd0
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/OpenSSHConfig.java
@@ -0,0 +1,270 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2013-2016 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.Hashtable;
+import java.util.Vector;
+
+/**
+ * This class implements ConfigRepository interface, and parses
+ * OpenSSH's configuration file. The following keywords will be recognized,
+ *
+ *
+ *
+ * @see ConfigRepository
+ */
+public class OpenSSHConfig implements ConfigRepository {
+
+ /**
+ * Parses the given string, and returns an instance of ConfigRepository.
+ *
+ * @param conf string, which includes OpenSSH's config
+ * @return an instanceof OpenSSHConfig
+ */
+ public static OpenSSHConfig parse(String conf) throws IOException {
+ Reader r = new StringReader(conf);
+ try {
+ return new OpenSSHConfig(r);
+ }
+ finally {
+ r.close();
+ }
+ }
+
+ /**
+ * Parses the given file, and returns an instance of ConfigRepository.
+ *
+ * @param file OpenSSH's config file
+ * @return an instanceof OpenSSHConfig
+ */
+ public static OpenSSHConfig parseFile(String file) throws IOException {
+ Reader r = new FileReader(Util.checkTilde(file));
+ try {
+ return new OpenSSHConfig(r);
+ }
+ finally {
+ r.close();
+ }
+ }
+
+ OpenSSHConfig(Reader r) throws IOException {
+ _parse(r);
+ }
+
+ private final Hashtable config = new Hashtable();
+ private final Vector hosts = new Vector();
+
+ private void _parse(Reader r) throws IOException {
+ BufferedReader br = new BufferedReader(r);
+
+ String host = "";
+ Vector/*0
, the tcp port will be allocated.
+ *
+ * @param lport local port for local port forwarding
+ * @param host host address for local port forwarding
+ * @param rport remote port number for local port forwarding
+ * @return an allocated local TCP port number
+ * @see #setPortForwardingL(String bind_address, int lport, String host, int rport, ServerSocketFactory ssf, int connectTimeout)
+ */
+ public int setPortForwardingL(int lport, String host, int rport) throws JSchException {
+ return setPortForwardingL("127.0.0.1", lport, host, rport);
+ }
+
+ /**
+ * Registers the local port forwarding. If bind_address
is an empty string
+ * or '*', the port should be available from all interfaces.
+ * If bind_address
is "localhost"
or
+ * null
, the listening port will be bound for local use only.
+ * If lport
is 0
, the tcp port will be allocated.
+ *
+ * @param bind_address bind address for local port forwarding
+ * @param lport local port for local port forwarding
+ * @param host host address for local port forwarding
+ * @param rport remote port number for local port forwarding
+ * @return an allocated local TCP port number
+ * @see #setPortForwardingL(String bind_address, int lport, String host, int rport, ServerSocketFactory ssf, int connectTimeout)
+ */
+ public int setPortForwardingL(String bind_address, int lport, String host, int rport) throws JSchException {
+ return setPortForwardingL(bind_address, lport, host, rport, null);
+ }
+
+ /**
+ * Registers the local port forwarding.
+ * If bind_address
is an empty string or "*"
,
+ * the port should be available from all interfaces.
+ * If bind_address
is "localhost"
or
+ * null
, the listening port will be bound for local use only.
+ * If lport
is 0
, the tcp port will be allocated.
+ *
+ * @param bind_address bind address for local port forwarding
+ * @param lport local port for local port forwarding
+ * @param host host address for local port forwarding
+ * @param rport remote port number for local port forwarding
+ * @param ssf socket factory
+ * @return an allocated local TCP port number
+ * @see #setPortForwardingL(String bind_address, int lport, String host, int rport, ServerSocketFactory ssf, int connectTimeout)
+ */
+ public int setPortForwardingL(String bind_address, int lport, String host, int rport, ServerSocketFactory ssf) throws JSchException {
+ return setPortForwardingL(bind_address, lport, host, rport, ssf, 0);
+ }
+
+ /**
+ * Registers the local port forwarding.
+ * If bind_address
is an empty string
+ * or "*"
, the port should be available from all interfaces.
+ * If bind_address
is "localhost"
or
+ * null
, the listening port will be bound for local use only.
+ * If lport
is 0
, the tcp port will be allocated.
+ *
+ * @param bind_address bind address for local port forwarding
+ * @param lport local port for local port forwarding
+ * @param host host address for local port forwarding
+ * @param rport remote port number for local port forwarding
+ * @param ssf socket factory
+ * @param connectTimeout timeout for establishing port connection
+ * @return an allocated local TCP port number
+ */
+ public int setPortForwardingL(String bind_address, int lport, String host, int rport, ServerSocketFactory ssf, int connectTimeout) throws JSchException {
+ PortWatcher pw = PortWatcher.addPort(this, bind_address, lport, host, rport, ssf);
+ pw.setConnectTimeout(connectTimeout);
+ Thread tmp = new Thread(pw);
+ tmp.setName("PortWatcher Thread for " + host);
+ if (daemon_thread) {
+ tmp.setDaemon(daemon_thread);
+ }
+ tmp.start();
+ return pw.lport;
+ }
+
+ /**
+ * Cancels the local port forwarding assigned
+ * at local TCP port lport
on loopback interface.
+ *
+ * @param lport local TCP port
+ */
+ public void delPortForwardingL(int lport) throws JSchException {
+ delPortForwardingL("127.0.0.1", lport);
+ }
+
+ /**
+ * Cancels the local port forwarding assigned
+ * at local TCP port lport
on bind_address
interface.
+ *
+ * @param bind_address bind_address of network interfaces
+ * @param lport local TCP port
+ */
+ public void delPortForwardingL(String bind_address, int lport) throws JSchException {
+ PortWatcher.delPort(this, bind_address, lport);
+ }
+
+ /**
+ * Lists the registered local port forwarding.
+ *
+ * @return a list of "lport:host:hostport"
+ */
+ public String[] getPortForwardingL() throws JSchException {
+ return PortWatcher.getPortForwarding(this);
+ }
+
+ /**
+ * Registers the remote port forwarding for the loopback interface
+ * of the remote.
+ *
+ * @param rport remote port
+ * @param host host address
+ * @param lport local port
+ * @see #setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf)
+ */
+ public void setPortForwardingR(int rport, String host, int lport) throws JSchException {
+ setPortForwardingR(null, rport, host, lport, (SocketFactory) null);
+ }
+
+ /**
+ * Registers the remote port forwarding.
+ * If bind_address
is an empty string or "*"
,
+ * the port should be available from all interfaces.
+ * If bind_address
is "localhost"
or is not given,
+ * the listening port will be bound for local use only.
+ * Note that if GatewayPorts
is "no"
on the
+ * remote, "localhost"
is always used as a bind_address.
+ *
+ * @param bind_address bind address
+ * @param rport remote port
+ * @param host host address
+ * @param lport local port
+ * @see #setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf)
+ */
+ public void setPortForwardingR(String bind_address, int rport, String host, int lport) throws JSchException {
+ setPortForwardingR(bind_address, rport, host, lport, (SocketFactory) null);
+ }
+
+ /**
+ * Registers the remote port forwarding for the loopback interface
+ * of the remote.
+ *
+ * @param rport remote port
+ * @param host host address
+ * @param lport local port
+ * @param sf socket factory
+ * @see #setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf)
+ */
+ public void setPortForwardingR(int rport, String host, int lport, SocketFactory sf) throws JSchException {
+ setPortForwardingR(null, rport, host, lport, sf);
+ }
+
+ // TODO: This method should return the integer value as the assigned port.
+
+ /**
+ * Registers the remote port forwarding.
+ * If bind_address
is an empty string or "*"
,
+ * the port should be available from all interfaces.
+ * If bind_address
is "localhost"
or is not given,
+ * the listening port will be bound for local use only.
+ * Note that if GatewayPorts
is "no"
on the
+ * remote, "localhost"
is always used as a bind_address.
+ * If rport
is 0
, the TCP port will be allocated on the remote.
+ *
+ * @param bind_address bind address
+ * @param rport remote port
+ * @param host host address
+ * @param lport local port
+ * @param sf socket factory
+ */
+ public void setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf) throws JSchException {
+ int allocated = _setPortForwardingR(bind_address, rport);
+ ChannelForwardedTCPIP.addPort(this, bind_address,
+ rport, allocated, host, lport, sf);
+ }
+
+ /**
+ * Registers the remote port forwarding for the loopback interface
+ * of the remote.
+ * The TCP connection to rport
on the remote will be
+ * forwarded to an instance of the class daemon
.
+ * The class specified by daemon
must implement
+ * ForwardedTCPIPDaemon
.
+ *
+ * @param rport remote port
+ * @param daemon class name, which implements "ForwardedTCPIPDaemon"
+ * @see #setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg)
+ */
+ public void setPortForwardingR(int rport, String daemon) throws JSchException {
+ setPortForwardingR(null, rport, daemon, null);
+ }
+
+ /**
+ * Registers the remote port forwarding for the loopback interface
+ * of the remote.
+ * The TCP connection to rport
on the remote will be
+ * forwarded to an instance of the class daemon
with
+ * the argument arg
.
+ * The class specified by daemon
must implement ForwardedTCPIPDaemon
.
+ *
+ * @param rport remote port
+ * @param daemon class name, which implements "ForwardedTCPIPDaemon"
+ * @param arg arguments for "daemon"
+ * @see #setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg)
+ */
+ public void setPortForwardingR(int rport, String daemon, Object[] arg) throws JSchException {
+ setPortForwardingR(null, rport, daemon, arg);
+ }
+
+ /**
+ * Registers the remote port forwarding.
+ * If bind_address
is an empty string
+ * or "*"
, the port should be available from all interfaces.
+ * If bind_address
is "localhost"
or is not given,
+ * the listening port will be bound for local use only.
+ * Note that if GatewayPorts
is "no"
on the
+ * remote, "localhost"
is always used as a bind_address.
+ * The TCP connection to rport
on the remote will be
+ * forwarded to an instance of the class daemon
with the
+ * argument arg
.
+ * The class specified by daemon
must implement ForwardedTCPIPDaemon
.
+ *
+ * @param bind_address bind address
+ * @param rport remote port
+ * @param daemon class name, which implements "ForwardedTCPIPDaemon"
+ * @param arg arguments for "daemon"
+ * @see #setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg)
+ */
+ public void setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) throws JSchException {
+ int allocated = _setPortForwardingR(bind_address, rport);
+ ChannelForwardedTCPIP.addPort(this, bind_address,
+ rport, allocated, daemon, arg);
+ }
+
+ /**
+ * Lists the registered remote port forwarding.
+ *
+ * @return a list of "rport:host:hostport"
+ */
+ public String[] getPortForwardingR() throws JSchException {
+ return ChannelForwardedTCPIP.getPortForwarding(this);
+ }
+
+ private class Forwarding {
+ String bind_address = null;
+ int port = -1;
+ String host = null;
+ int hostport = -1;
+ }
+
+ /**
+ * The given argument may be "[bind_address:]port:host:hostport" or
+ * "[bind_address:]port host:hostport", which is from LocalForward command of
+ * ~/.ssh/config .
+ */
+ private Forwarding parseForwarding(String conf) throws JSchException {
+ String[] tmp = conf.split(" ");
+ if (tmp.length > 1) { // "[bind_address:]port host:hostport"
+ Vector foo = new Vector();
+ for (int i = 0; i < tmp.length; i++) {
+ if (tmp[i].length() == 0) continue;
+ foo.addElement(tmp[i].trim());
+ }
+ StringBuffer sb = new StringBuffer(); // join
+ for (int i = 0; i < foo.size(); i++) {
+ sb.append((String) (foo.elementAt(i)));
+ if (i + 1 < foo.size())
+ sb.append(":");
+ }
+ conf = sb.toString();
+ }
+
+ String org = conf;
+ Forwarding f = new Forwarding();
+ try {
+ if (conf.lastIndexOf(":") == -1)
+ throw new JSchException("parseForwarding: " + org);
+ f.hostport = Integer.parseInt(conf.substring(conf.lastIndexOf(":") + 1));
+ conf = conf.substring(0, conf.lastIndexOf(":"));
+ if (conf.lastIndexOf(":") == -1)
+ throw new JSchException("parseForwarding: " + org);
+ f.host = conf.substring(conf.lastIndexOf(":") + 1);
+ conf = conf.substring(0, conf.lastIndexOf(":"));
+ if (conf.lastIndexOf(":") != -1) {
+ f.port = Integer.parseInt(conf.substring(conf.lastIndexOf(":") + 1));
+ conf = conf.substring(0, conf.lastIndexOf(":"));
+ if (conf.length() == 0 || conf.equals("*")) conf = "0.0.0.0";
+ if (conf.equals("localhost")) conf = "127.0.0.1";
+ f.bind_address = conf;
+ }
+ else {
+ f.port = Integer.parseInt(conf);
+ f.bind_address = "127.0.0.1";
+ }
+ }
+ catch (NumberFormatException e) {
+ throw new JSchException("parseForwarding: " + e.toString());
+ }
+ return f;
+ }
+
+ /**
+ * Registers the local port forwarding. The argument should be
+ * in the format like "[bind_address:]port:host:hostport".
+ * If bind_address
is an empty string or "*"
,
+ * the port should be available from all interfaces.
+ * If bind_address
is "localhost"
or is not given,
+ * the listening port will be bound for local use only.
+ *
+ * @param conf configuration of local port forwarding
+ * @return an assigned port number
+ * @see #setPortForwardingL(String bind_address, int lport, String host, int rport)
+ */
+ public int setPortForwardingL(String conf) throws JSchException {
+ Forwarding f = parseForwarding(conf);
+ return setPortForwardingL(f.bind_address, f.port, f.host, f.hostport);
+ }
+
+ /**
+ * Registers the remote port forwarding. The argument should be
+ * in the format like "[bind_address:]port:host:hostport". If the
+ * bind_address is not given, the default is to only bind to loopback
+ * addresses. If the bind_address is "*"
or an empty string,
+ * then the forwarding is requested to listen on all interfaces.
+ * Note that if GatewayPorts
is "no"
on the remote,
+ * "localhost"
is always used for bind_address.
+ * If the specified remote is "0"
,
+ * the TCP port will be allocated on the remote.
+ *
+ * @param conf configuration of remote port forwarding
+ * @return an allocated TCP port on the remote.
+ * @see #setPortForwardingR(String bind_address, int rport, String host, int rport)
+ */
+ public int setPortForwardingR(String conf) throws JSchException {
+ Forwarding f = parseForwarding(conf);
+ int allocated = _setPortForwardingR(f.bind_address, f.port);
+ ChannelForwardedTCPIP.addPort(this, f.bind_address,
+ f.port, allocated, f.host, f.hostport, null);
+ return allocated;
+ }
+
+ /**
+ * Instantiates an instance of stream-forwarder to host
:port
.
+ * Set I/O stream to the given channel, and then invoke Channel#connect() method.
+ *
+ * @param host remote host, which the given stream will be plugged to.
+ * @param port remote port, which the given stream will be plugged to.
+ */
+ public Channel getStreamForwarder(String host, int port) throws JSchException {
+ ChannelDirectTCPIP channel = new ChannelDirectTCPIP();
+ channel.init();
+ this.addChannel(channel);
+ channel.setHost(host);
+ channel.setPort(port);
+ return channel;
+ }
+
+ private class GlobalRequestReply {
+ private Thread thread = null;
+ private int reply = -1;
+ private int port = 0;
+
+ void setThread(Thread thread) {
+ this.thread = thread;
+ this.reply = -1;
+ }
+
+ Thread getThread() {
+ return thread;
+ }
+
+ void setReply(int reply) {
+ this.reply = reply;
+ }
+
+ int getReply() {
+ return this.reply;
+ }
+
+ int getPort() {
+ return this.port;
+ }
+
+ void setPort(int port) {
+ this.port = port;
+ }
+ }
+
+ private GlobalRequestReply grr = new GlobalRequestReply();
+
+ private int _setPortForwardingR(String bind_address, int rport) throws JSchException {
+ synchronized (grr) {
+ Buffer buf = new Buffer(100); // ??
+ Packet packet = new Packet(buf);
+
+ String address_to_bind = ChannelForwardedTCPIP.normalize(bind_address);
+
+ grr.setThread(Thread.currentThread());
+ grr.setPort(rport);
+
+ try {
+ // byte SSH_MSG_GLOBAL_REQUEST 80
+ // string "tcpip-forward"
+ // boolean want_reply
+ // string address_to_bind
+ // uint32 port number to bind
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_GLOBAL_REQUEST);
+ buf.putString(Util.str2byte("tcpip-forward"));
+ buf.putByte((byte) 1);
+ buf.putString(Util.str2byte(address_to_bind));
+ buf.putInt(rport);
+ write(packet);
+ }
+ catch (Exception e) {
+ grr.setThread(null);
+ if (e instanceof Throwable)
+ throw new JSchException(e.toString(), (Throwable) e);
+ throw new JSchException(e.toString());
+ }
+
+ int count = 0;
+ int reply = grr.getReply();
+ while (count < 10 && reply == -1) {
+ try {
+ Thread.sleep(1000);
+ }
+ catch (Exception e) {
+ }
+ count++;
+ reply = grr.getReply();
+ }
+ grr.setThread(null);
+ if (reply != 1) {
+ throw new JSchException("remote port forwarding failed for listen port " + rport);
+ }
+ rport = grr.getPort();
+ }
+ return rport;
+ }
+
+ /**
+ * Cancels the remote port forwarding assigned at remote TCP port rport
.
+ *
+ * @param rport remote TCP port
+ */
+ public void delPortForwardingR(int rport) throws JSchException {
+ this.delPortForwardingR(null, rport);
+ }
+
+ /**
+ * Cancels the remote port forwarding assigned at
+ * remote TCP port rport
bound on the interface at
+ * bind_address
.
+ *
+ * @param bind_address bind address of the interface on the remote
+ * @param rport remote TCP port
+ */
+ public void delPortForwardingR(String bind_address, int rport) throws JSchException {
+ ChannelForwardedTCPIP.delPort(this, bind_address, rport);
+ }
+
+ private void initDeflater(String method) throws JSchException {
+ if (method.equals("none")) {
+ deflater = null;
+ return;
+ }
+ String foo = getConfig(method);
+ if (foo != null) {
+ if (method.equals("zlib") ||
+ (isAuthed && method.equals("zlib@openssh.com"))) {
+ try {
+ Class c = Class.forName(foo);
+ deflater = (Compression) (c.newInstance());
+ int level = 6;
+ try {
+ level = Integer.parseInt(getConfig("compression_level"));
+ }
+ catch (Exception ee) {
+ }
+ deflater.init(Compression.DEFLATER, level);
+ }
+ catch (NoClassDefFoundError ee) {
+ throw new JSchException(ee.toString(), ee);
+ }
+ catch (Exception ee) {
+ throw new JSchException(ee.toString(), ee);
+ //System.err.println(foo+" isn't accessible.");
+ }
+ }
+ }
+ }
+
+ private void initInflater(String method) throws JSchException {
+ if (method.equals("none")) {
+ inflater = null;
+ return;
+ }
+ String foo = getConfig(method);
+ if (foo != null) {
+ if (method.equals("zlib") ||
+ (isAuthed && method.equals("zlib@openssh.com"))) {
+ try {
+ Class c = Class.forName(foo);
+ inflater = (Compression) (c.newInstance());
+ inflater.init(Compression.INFLATER, 0);
+ }
+ catch (Exception ee) {
+ throw new JSchException(ee.toString(), ee);
+ //System.err.println(foo+" isn't accessible.");
+ }
+ }
+ }
+ }
+
+ void addChannel(Channel channel) {
+ channel.setSession(this);
+ }
+
+ public void setProxy(Proxy proxy) {
+ this.proxy = proxy;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ void setUserName(String username) {
+ this.username = username;
+ }
+
+ public void setUserInfo(UserInfo userinfo) {
+ this.userinfo = userinfo;
+ }
+
+ public UserInfo getUserInfo() {
+ return userinfo;
+ }
+
+ public void setInputStream(InputStream in) {
+ this.in = in;
+ }
+
+ public void setOutputStream(OutputStream out) {
+ this.out = out;
+ }
+
+ public void setX11Host(String host) {
+ ChannelX11.setHost(host);
+ }
+
+ public void setX11Port(int port) {
+ ChannelX11.setPort(port);
+ }
+
+ public void setX11Cookie(String cookie) {
+ ChannelX11.setCookie(cookie);
+ }
+
+ public void setPassword(String password) {
+ if (password != null)
+ this.password = Util.str2byte(password);
+ }
+
+ public void setPassword(byte[] password) {
+ if (password != null) {
+ this.password = new byte[password.length];
+ System.arraycopy(password, 0, this.password, 0, password.length);
+ }
+ }
+
+ public void setConfig(java.util.Properties newconf) {
+ setConfig((java.util.Hashtable) newconf);
+ }
+
+ public void setConfig(java.util.Hashtable newconf) {
+ synchronized (lock) {
+ if (config == null)
+ config = new java.util.Hashtable();
+ for (java.util.Enumeration e = newconf.keys(); e.hasMoreElements(); ) {
+ String key = (String) (e.nextElement());
+ config.put(key, (String) (newconf.get(key)));
+ }
+ }
+ }
+
+ public void setConfig(String key, String value) {
+ synchronized (lock) {
+ if (config == null) {
+ config = new java.util.Hashtable();
+ }
+ config.put(key, value);
+ }
+ }
+
+ public String getConfig(String key) {
+ Object foo = null;
+ if (config != null) {
+ foo = config.get(key);
+ if (foo instanceof String) return (String) foo;
+ }
+ foo = jsch.getConfig(key);
+ if (foo instanceof String) return (String) foo;
+ return null;
+ }
+
+ public void setSocketFactory(SocketFactory sfactory) {
+ socket_factory = sfactory;
+ }
+
+ public boolean isConnected() {
+ return isConnected;
+ }
+
+ public int getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(int timeout) throws JSchException {
+ if (socket == null) {
+ if (timeout < 0) {
+ throw new JSchException("invalid timeout value");
+ }
+ this.timeout = timeout;
+ return;
+ }
+ try {
+ socket.setSoTimeout(timeout);
+ this.timeout = timeout;
+ }
+ catch (Exception e) {
+ if (e instanceof Throwable)
+ throw new JSchException(e.toString(), (Throwable) e);
+ throw new JSchException(e.toString());
+ }
+ }
+
+ public String getServerVersion() {
+ return Util.byte2str(V_S);
+ }
+
+ public String getClientVersion() {
+ return Util.byte2str(V_C);
+ }
+
+ public void setClientVersion(String cv) {
+ V_C = Util.str2byte(cv);
+ }
+
+ public void sendIgnore() throws Exception {
+ Buffer buf = new Buffer();
+ Packet packet = new Packet(buf);
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_IGNORE);
+ write(packet);
+ }
+
+ private static final byte[] keepalivemsg = Util.str2byte("keepalive@jcraft.com");
+
+ public void sendKeepAliveMsg() throws Exception {
+ Buffer buf = new Buffer();
+ Packet packet = new Packet(buf);
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_GLOBAL_REQUEST);
+ buf.putString(keepalivemsg);
+ buf.putByte((byte) 1);
+ write(packet);
+ }
+
+ private static final byte[] nomoresessions = Util.str2byte("no-more-sessions@openssh.com");
+
+ public void noMoreSessionChannels() throws Exception {
+ Buffer buf = new Buffer();
+ Packet packet = new Packet(buf);
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_GLOBAL_REQUEST);
+ buf.putString(nomoresessions);
+ buf.putByte((byte) 0);
+ write(packet);
+ }
+
+ private HostKey hostkey = null;
+
+ public HostKey getHostKey() {
+ return hostkey;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public String getUserName() {
+ return username;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setHostKeyAlias(String hostKeyAlias) {
+ this.hostKeyAlias = hostKeyAlias;
+ }
+
+ public String getHostKeyAlias() {
+ return hostKeyAlias;
+ }
+
+ /**
+ * Sets the interval to send a keep-alive message. If zero is
+ * specified, any keep-alive message must not be sent. The default interval
+ * is zero.
+ *
+ * @param interval the specified interval, in milliseconds.
+ * @see #getServerAliveInterval()
+ */
+ public void setServerAliveInterval(int interval) throws JSchException {
+ setTimeout(interval);
+ this.serverAliveInterval = interval;
+ }
+
+ /**
+ * Returns setting for the interval to send a keep-alive message.
+ *
+ * @see #setServerAliveInterval(int)
+ */
+ public int getServerAliveInterval() {
+ return this.serverAliveInterval;
+ }
+
+ /**
+ * Sets the number of keep-alive messages which may be sent without
+ * receiving any messages back from the server. If this threshold is
+ * reached while keep-alive messages are being sent, the connection will
+ * be disconnected. The default value is one.
+ *
+ * @param count the specified count
+ * @see #getServerAliveCountMax()
+ */
+ public void setServerAliveCountMax(int count) {
+ this.serverAliveCountMax = count;
+ }
+
+ /**
+ * Returns setting for the threshold to send keep-alive messages.
+ *
+ * @see #setServerAliveCountMax(int)
+ */
+ public int getServerAliveCountMax() {
+ return this.serverAliveCountMax;
+ }
+
+ public void setDaemonThread(boolean enable) {
+ this.daemon_thread = enable;
+ }
+
+ private String[] checkCiphers(String ciphers) {
+ if (ciphers == null || ciphers.length() == 0)
+ return null;
+
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ JSch.getLogger().log(Logger.INFO,
+ "CheckCiphers: " + ciphers);
+ }
+
+ String cipherc2s = getConfig("cipher.c2s");
+ String ciphers2c = getConfig("cipher.s2c");
+
+ Vector result = new Vector();
+ String[] _ciphers = Util.split(ciphers, ",");
+ for (int i = 0; i < _ciphers.length; i++) {
+ String cipher = _ciphers[i];
+ if (ciphers2c.indexOf(cipher) == -1 && cipherc2s.indexOf(cipher) == -1)
+ continue;
+ if (!checkCipher(getConfig(cipher))) {
+ result.addElement(cipher);
+ }
+ }
+ if (result.size() == 0)
+ return null;
+ String[] foo = new String[result.size()];
+ System.arraycopy(result.toArray(), 0, foo, 0, result.size());
+
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ for (int i = 0; i < foo.length; i++) {
+ JSch.getLogger().log(Logger.INFO,
+ foo[i] + " is not available.");
+ }
+ }
+
+ return foo;
+ }
+
+ static boolean checkCipher(String cipher) {
+ try {
+ Class c = Class.forName(cipher);
+ Cipher _c = (Cipher) (c.newInstance());
+ _c.init(Cipher.ENCRYPT_MODE,
+ new byte[_c.getBlockSize()],
+ new byte[_c.getIVSize()]);
+ return true;
+ }
+ catch (Exception e) {
+ return false;
+ }
+ }
+
+ private String[] checkKexes(String kexes) {
+ if (kexes == null || kexes.length() == 0)
+ return null;
+
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ JSch.getLogger().log(Logger.INFO,
+ "CheckKexes: " + kexes);
+ }
+
+ java.util.Vector result = new java.util.Vector();
+ String[] _kexes = Util.split(kexes, ",");
+ for (int i = 0; i < _kexes.length; i++) {
+ if (!checkKex(this, getConfig(_kexes[i]))) {
+ result.addElement(_kexes[i]);
+ }
+ }
+ if (result.size() == 0)
+ return null;
+ String[] foo = new String[result.size()];
+ System.arraycopy(result.toArray(), 0, foo, 0, result.size());
+
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ for (int i = 0; i < foo.length; i++) {
+ JSch.getLogger().log(Logger.INFO,
+ foo[i] + " is not available.");
+ }
+ }
+
+ return foo;
+ }
+
+ static boolean checkKex(Session s, String kex) {
+ try {
+ Class c = Class.forName(kex);
+ KeyExchange _c = (KeyExchange) (c.newInstance());
+ _c.init(s, null, null, null, null);
+ return true;
+ }
+ catch (Exception e) {
+ return false;
+ }
+ }
+
+ private String[] checkSignatures(String sigs) {
+ if (sigs == null || sigs.length() == 0)
+ return null;
+
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ JSch.getLogger().log(Logger.INFO,
+ "CheckSignatures: " + sigs);
+ }
+
+ java.util.Vector result = new java.util.Vector();
+ String[] _sigs = Util.split(sigs, ",");
+ for (int i = 0; i < _sigs.length; i++) {
+ try {
+ Class c = Class.forName((String) jsch.getConfig(_sigs[i]));
+ final Signature sig = (Signature) (c.newInstance());
+ sig.init();
+ }
+ catch (Exception e) {
+ result.addElement(_sigs[i]);
+ }
+ }
+ if (result.size() == 0)
+ return null;
+ String[] foo = new String[result.size()];
+ System.arraycopy(result.toArray(), 0, foo, 0, result.size());
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ for (int i = 0; i < foo.length; i++) {
+ JSch.getLogger().log(Logger.INFO,
+ foo[i] + " is not available.");
+ }
+ }
+ return foo;
+ }
+
+ /**
+ * Sets the identityRepository, which will be referred
+ * in the public key authentication. The default value is null
.
+ *
+ * @param identityRepository
+ * @see #getIdentityRepository()
+ */
+ public void setIdentityRepository(IdentityRepository identityRepository) {
+ this.identityRepository = identityRepository;
+ }
+
+ /**
+ * Gets the identityRepository.
+ * If this.identityRepository is null
,
+ * JSch#getIdentityRepository() will be invoked.
+ *
+ * @see JSch#getIdentityRepository()
+ */
+ IdentityRepository getIdentityRepository() {
+ if (identityRepository == null)
+ return jsch.getIdentityRepository();
+ return identityRepository;
+ }
+
+ /**
+ * Sets the hostkeyRepository, which will be referred in checking host keys.
+ *
+ * @param hostkeyRepository
+ * @see #getHostKeyRepository()
+ */
+ public void setHostKeyRepository(HostKeyRepository hostkeyRepository) {
+ this.hostkeyRepository = hostkeyRepository;
+ }
+
+ /**
+ * Gets the hostkeyRepository.
+ * If this.hostkeyRepository is null
,
+ * JSch#getHostKeyRepository() will be invoked.
+ *
+ * @see JSch#getHostKeyRepository()
+ */
+ public HostKeyRepository getHostKeyRepository() {
+ if (hostkeyRepository == null)
+ return jsch.getHostKeyRepository();
+ return hostkeyRepository;
+ }
+
+ /*
+ // setProxyCommand("ssh -l user2 host2 -o 'ProxyCommand ssh user1@host1 nc host2 22' nc %h %p")
+ public void setProxyCommand(String command){
+ setProxy(new ProxyCommand(command));
+ }
+
+ class ProxyCommand implements Proxy {
+ String command;
+ Process p = null;
+ InputStream in = null;
+ OutputStream out = null;
+ ProxyCommand(String command){
+ this.command = command;
+ }
+ public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws Exception {
+ String _command = command.replace("%h", host);
+ _command = _command.replace("%p", new Integer(port).toString());
+ p = Runtime.getRuntime().exec(_command);
+ in = p.getInputStream();
+ out = p.getOutputStream();
+ }
+ public Socket getSocket() { return null; }
+ public InputStream getInputStream() { return in; }
+ public OutputStream getOutputStream() { return out; }
+ public void close() {
+ try{
+ if(p!=null){
+ p.getErrorStream().close();
+ p.getOutputStream().close();
+ p.getInputStream().close();
+ p.destroy();
+ p=null;
+ }
+ }
+ catch(IOException e){
+ }
+ }
+ }
+ */
+
+ private void applyConfig() throws JSchException {
+ ConfigRepository configRepository = jsch.getConfigRepository();
+ if (configRepository == null) {
+ return;
+ }
+
+ ConfigRepository.Config config =
+ configRepository.getConfig(org_host);
+
+ String value = null;
+
+ value = config.getUser();
+ if (value != null)
+ username = value;
+
+ value = config.getHostname();
+ if (value != null)
+ host = value;
+
+ int port = config.getPort();
+ if (port != -1)
+ this.port = port;
+
+ checkConfig(config, "kex");
+ checkConfig(config, "server_host_key");
+
+ checkConfig(config, "cipher.c2s");
+ checkConfig(config, "cipher.s2c");
+ checkConfig(config, "mac.c2s");
+ checkConfig(config, "mac.s2c");
+ checkConfig(config, "compression.c2s");
+ checkConfig(config, "compression.s2c");
+ checkConfig(config, "compression_level");
+
+ checkConfig(config, "StrictHostKeyChecking");
+ checkConfig(config, "HashKnownHosts");
+ checkConfig(config, "PreferredAuthentications");
+ checkConfig(config, "MaxAuthTries");
+ checkConfig(config, "ClearAllForwardings");
+
+ value = config.getValue("HostKeyAlias");
+ if (value != null)
+ this.setHostKeyAlias(value);
+
+ value = config.getValue("UserKnownHostsFile");
+ if (value != null) {
+ KnownHosts kh = new KnownHosts(jsch);
+ kh.setKnownHosts(value);
+ this.setHostKeyRepository(kh);
+ }
+
+ String[] values = config.getValues("IdentityFile");
+ if (values != null) {
+ String[] global =
+ configRepository.getConfig("").getValues("IdentityFile");
+ if (global != null) {
+ for (int i = 0; i < global.length; i++) {
+ jsch.addIdentity(global[i]);
+ }
+ }
+ else {
+ global = new String[0];
+ }
+ if (values.length - global.length > 0) {
+ IdentityRepository.Wrapper ir =
+ new IdentityRepository.Wrapper(jsch.getIdentityRepository(), true);
+ for (int i = 0; i < values.length; i++) {
+ String ifile = values[i];
+ for (int j = 0; j < global.length; j++) {
+ if (!ifile.equals(global[j]))
+ continue;
+ ifile = null;
+ break;
+ }
+ if (ifile == null)
+ continue;
+ Identity identity =
+ IdentityFile.newInstance(ifile, null, jsch);
+ ir.add(identity);
+ }
+ this.setIdentityRepository(ir);
+ }
+ }
+
+ value = config.getValue("ServerAliveInterval");
+ if (value != null) {
+ try {
+ this.setServerAliveInterval(Integer.parseInt(value));
+ }
+ catch (NumberFormatException e) {
+ }
+ }
+
+ value = config.getValue("ConnectTimeout");
+ if (value != null) {
+ try {
+ setTimeout(Integer.parseInt(value));
+ }
+ catch (NumberFormatException e) {
+ }
+ }
+
+ value = config.getValue("MaxAuthTries");
+ if (value != null) {
+ setConfig("MaxAuthTries", value);
+ }
+
+ value = config.getValue("ClearAllForwardings");
+ if (value != null) {
+ setConfig("ClearAllForwardings", value);
+ }
+
+ }
+
+ private void applyConfigChannel(ChannelSession channel) throws JSchException {
+ ConfigRepository configRepository = jsch.getConfigRepository();
+ if (configRepository == null) {
+ return;
+ }
+
+ ConfigRepository.Config config =
+ configRepository.getConfig(org_host);
+
+ String value = null;
+
+ value = config.getValue("ForwardAgent");
+ if (value != null) {
+ channel.setAgentForwarding(value.equals("yes"));
+ }
+
+ value = config.getValue("RequestTTY");
+ if (value != null) {
+ channel.setPty(value.equals("yes"));
+ }
+ }
+
+ private void requestPortForwarding() throws JSchException {
+
+ if (getConfig("ClearAllForwardings").equals("yes"))
+ return;
+
+ ConfigRepository configRepository = jsch.getConfigRepository();
+ if (configRepository == null) {
+ return;
+ }
+
+ ConfigRepository.Config config =
+ configRepository.getConfig(org_host);
+
+ String[] values = config.getValues("LocalForward");
+ if (values != null) {
+ for (int i = 0; i < values.length; i++) {
+ setPortForwardingL(values[i]);
+ }
+ }
+
+ values = config.getValues("RemoteForward");
+ if (values != null) {
+ for (int i = 0; i < values.length; i++) {
+ setPortForwardingR(values[i]);
+ }
+ }
+ }
+
+ private void checkConfig(ConfigRepository.Config config, String key) {
+ String value = config.getValue(key);
+ if (value != null)
+ this.setConfig(key, value);
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/SftpATTRS.java b/src/main/java/com/jcraft/jsch/SftpATTRS.java
new file mode 100644
index 0000000..d1143e8
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/SftpATTRS.java
@@ -0,0 +1,341 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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.Date;
+
+/*
+ uint32 flags
+ uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
+ uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
+ uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
+ string extended_type
+ string extended_data
+ ... more extended data (extended_type - extended_data pairs),
+ so that number of pairs equals extended_count
+*/
+public class SftpATTRS {
+
+ static final int S_ISUID = 04000; // set user ID on execution
+ static final int S_ISGID = 02000; // set group ID on execution
+ static final int S_ISVTX = 01000; // sticky bit ****** NOT DOCUMENTED *****
+
+ static final int S_IRUSR = 00400; // read by owner
+ static final int S_IWUSR = 00200; // write by owner
+ static final int S_IXUSR = 00100; // execute/search by owner
+ static final int S_IREAD = 00400; // read by owner
+ static final int S_IWRITE = 00200; // write by owner
+ static final int S_IEXEC = 00100; // execute/search by owner
+
+ static final int S_IRGRP = 00040; // read by group
+ static final int S_IWGRP = 00020; // write by group
+ static final int S_IXGRP = 00010; // execute/search by group
+
+ static final int S_IROTH = 00004; // read by others
+ static final int S_IWOTH = 00002; // write by others
+ static final int S_IXOTH = 00001; // execute/search by others
+
+ private static final int pmask = 0xFFF;
+
+ public String getPermissionsString() {
+ StringBuffer buf = new StringBuffer(10);
+
+ if (isDir()) buf.append('d');
+ else if (isLink()) buf.append('l');
+ else buf.append('-');
+
+ if ((permissions & S_IRUSR) != 0) buf.append('r');
+ else buf.append('-');
+
+ if ((permissions & S_IWUSR) != 0) buf.append('w');
+ else buf.append('-');
+
+ if ((permissions & S_ISUID) != 0) buf.append('s');
+ else if ((permissions & S_IXUSR) != 0) buf.append('x');
+ else buf.append('-');
+
+ if ((permissions & S_IRGRP) != 0) buf.append('r');
+ else buf.append('-');
+
+ if ((permissions & S_IWGRP) != 0) buf.append('w');
+ else buf.append('-');
+
+ if ((permissions & S_ISGID) != 0) buf.append('s');
+ else if ((permissions & S_IXGRP) != 0) buf.append('x');
+ else buf.append('-');
+
+ if ((permissions & S_IROTH) != 0) buf.append('r');
+ else buf.append('-');
+
+ if ((permissions & S_IWOTH) != 0) buf.append('w');
+ else buf.append('-');
+
+ if ((permissions & S_IXOTH) != 0) buf.append('x');
+ else buf.append('-');
+ return (buf.toString());
+ }
+
+ public String getAtimeString() {
+ Date date = new Date(((long) atime) * 1000L);
+ return (date.toString());
+ }
+
+ public String getMtimeString() {
+ Date date = new Date(((long) mtime) * 1000L);
+ return (date.toString());
+ }
+
+ public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
+ public static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
+ public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
+ public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008;
+ public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
+
+ static final int S_IFMT = 0xf000;
+ static final int S_IFIFO = 0x1000;
+ static final int S_IFCHR = 0x2000;
+ static final int S_IFDIR = 0x4000;
+ static final int S_IFBLK = 0x6000;
+ static final int S_IFREG = 0x8000;
+ static final int S_IFLNK = 0xa000;
+ static final int S_IFSOCK = 0xc000;
+
+ int flags = 0;
+ long size;
+ int uid;
+ int gid;
+ int permissions;
+ int atime;
+ int mtime;
+ String[] extended = null;
+
+ private SftpATTRS() {
+ }
+
+ static SftpATTRS getATTR(Buffer buf) {
+ SftpATTRS attr = new SftpATTRS();
+ attr.flags = buf.getInt();
+ if ((attr.flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+ attr.size = buf.getLong();
+ }
+ if ((attr.flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
+ attr.uid = buf.getInt();
+ attr.gid = buf.getInt();
+ }
+ if ((attr.flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+ attr.permissions = buf.getInt();
+ }
+ if ((attr.flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+ attr.atime = buf.getInt();
+ }
+ if ((attr.flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+ attr.mtime = buf.getInt();
+ }
+ if ((attr.flags & SSH_FILEXFER_ATTR_EXTENDED) != 0) {
+ int count = buf.getInt();
+ if (count > 0) {
+ attr.extended = new String[count * 2];
+ for (int i = 0; i < count; i++) {
+ attr.extended[i * 2] = Util.byte2str(buf.getString());
+ attr.extended[i * 2 + 1] = Util.byte2str(buf.getString());
+ }
+ }
+ }
+ return attr;
+ }
+
+ int length() {
+ int len = 4;
+
+ if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+ len += 8;
+ }
+ if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
+ len += 8;
+ }
+ if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+ len += 4;
+ }
+ if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+ len += 8;
+ }
+ if ((flags & SSH_FILEXFER_ATTR_EXTENDED) != 0) {
+ len += 4;
+ int count = extended.length / 2;
+ if (count > 0) {
+ for (int i = 0; i < count; i++) {
+ len += 4;
+ len += extended[i * 2].length();
+ len += 4;
+ len += extended[i * 2 + 1].length();
+ }
+ }
+ }
+ return len;
+ }
+
+ void dump(Buffer buf) {
+ buf.putInt(flags);
+ if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {
+ buf.putLong(size);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) {
+ buf.putInt(uid);
+ buf.putInt(gid);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) {
+ buf.putInt(permissions);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+ buf.putInt(atime);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+ buf.putInt(mtime);
+ }
+ if ((flags & SSH_FILEXFER_ATTR_EXTENDED) != 0) {
+ int count = extended.length / 2;
+ if (count > 0) {
+ for (int i = 0; i < count; i++) {
+ buf.putString(Util.str2byte(extended[i * 2]));
+ buf.putString(Util.str2byte(extended[i * 2 + 1]));
+ }
+ }
+ }
+ }
+
+ void setFLAGS(int flags) {
+ this.flags = flags;
+ }
+
+ public void setSIZE(long size) {
+ flags |= SSH_FILEXFER_ATTR_SIZE;
+ this.size = size;
+ }
+
+ public void setUIDGID(int uid, int gid) {
+ flags |= SSH_FILEXFER_ATTR_UIDGID;
+ this.uid = uid;
+ this.gid = gid;
+ }
+
+ public void setACMODTIME(int atime, int mtime) {
+ flags |= SSH_FILEXFER_ATTR_ACMODTIME;
+ this.atime = atime;
+ this.mtime = mtime;
+ }
+
+ public void setPERMISSIONS(int permissions) {
+ flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
+ permissions = (this.permissions & ~pmask) | (permissions & pmask);
+ this.permissions = permissions;
+ }
+
+ private boolean isType(int mask) {
+ return (flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0 &&
+ (permissions & S_IFMT) == mask;
+ }
+
+ public boolean isReg() {
+ return isType(S_IFREG);
+ }
+
+ public boolean isDir() {
+ return isType(S_IFDIR);
+ }
+
+ public boolean isChr() {
+ return isType(S_IFCHR);
+ }
+
+ public boolean isBlk() {
+ return isType(S_IFBLK);
+ }
+
+ public boolean isFifo() {
+ return isType(S_IFIFO);
+ }
+
+ public boolean isLink() {
+ return isType(S_IFLNK);
+ }
+
+ public boolean isSock() {
+ return isType(S_IFSOCK);
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public int getUId() {
+ return uid;
+ }
+
+ public int getGId() {
+ return gid;
+ }
+
+ public int getPermissions() {
+ return permissions;
+ }
+
+ public int getATime() {
+ return atime;
+ }
+
+ public int getMTime() {
+ return mtime;
+ }
+
+ public String[] getExtended() {
+ return extended;
+ }
+
+ public String toString() {
+ return (getPermissionsString() + " " + getUId() + " " + getGId() + " " + getSize() + " " + getMtimeString());
+ }
+ /*
+ public String toString(){
+ return (((flags&SSH_FILEXFER_ATTR_SIZE)!=0) ? ("size:"+size+" ") : "")+
+ (((flags&SSH_FILEXFER_ATTR_UIDGID)!=0) ? ("uid:"+uid+",gid:"+gid+" ") : "")+
+ (((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0) ? ("permissions:0x"+Integer.toHexString(permissions)+" ") : "")+
+ (((flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0) ? ("atime:"+atime+",mtime:"+mtime+" ") : "")+
+ (((flags&SSH_FILEXFER_ATTR_EXTENDED)!=0) ? ("extended:?"+" ") : "");
+ }
+ */
+}
diff --git a/src/main/java/com/jcraft/jsch/SftpException.java b/src/main/java/com/jcraft/jsch/SftpException.java
new file mode 100644
index 0000000..75ea0f4
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/SftpException.java
@@ -0,0 +1,55 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class SftpException extends Exception {
+ //private static final long serialVersionUID=-5616888495583253811L;
+ public int id;
+ private Throwable cause = null;
+
+ public SftpException(int id, String message) {
+ super(message);
+ this.id = id;
+ }
+
+ public SftpException(int id, String message, Throwable e) {
+ super(message);
+ this.id = id;
+ this.cause = e;
+ }
+
+ public String toString() {
+ return id + ": " + getMessage();
+ }
+
+ public Throwable getCause() {
+ return this.cause;
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/SftpProgressMonitor.java b/src/main/java/com/jcraft/jsch/SftpProgressMonitor.java
new file mode 100644
index 0000000..fa71e51
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/SftpProgressMonitor.java
@@ -0,0 +1,42 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface SftpProgressMonitor {
+ public static final int PUT = 0;
+ public static final int GET = 1;
+ public static final long UNKNOWN_SIZE = -1L;
+
+ void init(int op, String src, String dest, long max);
+
+ boolean count(long count);
+
+ void end();
+}
diff --git a/src/main/java/com/jcraft/jsch/SftpStatVFS.java b/src/main/java/com/jcraft/jsch/SftpStatVFS.java
new file mode 100644
index 0000000..c0a2184
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/SftpStatVFS.java
@@ -0,0 +1,151 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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 SftpStatVFS {
+
+ /*
+ It seems data is serializsed according to sys/statvfs.h; for example,
+ http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/statvfs.h.html
+ */
+
+ private long bsize;
+ private long frsize;
+ private long blocks;
+ private long bfree;
+ private long bavail;
+ private long files;
+ private long ffree;
+ private long favail;
+ private long fsid;
+ private long flag;
+ private long namemax;
+
+ int flags = 0;
+ long size;
+ int uid;
+ int gid;
+ int permissions;
+ int atime;
+ int mtime;
+ String[] extended = null;
+
+ private SftpStatVFS() {
+ }
+
+ static SftpStatVFS getStatVFS(Buffer buf) {
+ SftpStatVFS statvfs = new SftpStatVFS();
+
+ statvfs.bsize = buf.getLong();
+ statvfs.frsize = buf.getLong();
+ statvfs.blocks = buf.getLong();
+ statvfs.bfree = buf.getLong();
+ statvfs.bavail = buf.getLong();
+ statvfs.files = buf.getLong();
+ statvfs.ffree = buf.getLong();
+ statvfs.favail = buf.getLong();
+ statvfs.fsid = buf.getLong();
+ int flag = (int) buf.getLong();
+ statvfs.namemax = buf.getLong();
+
+ statvfs.flag =
+ (flag & 1/*SSH2_FXE_STATVFS_ST_RDONLY*/) != 0 ? 1/*ST_RDONLY*/ : 0;
+ statvfs.flag |=
+ (flag & 2/*SSH2_FXE_STATVFS_ST_NOSUID*/) != 0 ? 2/*ST_NOSUID*/ : 0;
+
+ return statvfs;
+ }
+
+ public long getBlockSize() {
+ return bsize;
+ }
+
+ public long getFragmentSize() {
+ return frsize;
+ }
+
+ public long getBlocks() {
+ return blocks;
+ }
+
+ public long getFreeBlocks() {
+ return bfree;
+ }
+
+ public long getAvailBlocks() {
+ return bavail;
+ }
+
+ public long getINodes() {
+ return files;
+ }
+
+ public long getFreeINodes() {
+ return ffree;
+ }
+
+ public long getAvailINodes() {
+ return favail;
+ }
+
+ public long getFileSystemID() {
+ return fsid;
+ }
+
+ public long getMountFlag() {
+ return flag;
+ }
+
+ public long getMaximumFilenameLength() {
+ return namemax;
+ }
+
+ public long getSize() {
+ return getFragmentSize() * getBlocks() / 1024;
+ }
+
+ public long getUsed() {
+ return getFragmentSize() * (getBlocks() - getFreeBlocks()) / 1024;
+ }
+
+ public long getAvailForNonRoot() {
+ return getFragmentSize() * getAvailBlocks() / 1024;
+ }
+
+ public long getAvail() {
+ return getFragmentSize() * getFreeBlocks() / 1024;
+ }
+
+ public int getCapacity() {
+ return (int) (100 * (getBlocks() - getFreeBlocks()) / getBlocks());
+ }
+
+// public String toString() { return ""; }
+}
diff --git a/src/main/java/com/jcraft/jsch/Signature.java b/src/main/java/com/jcraft/jsch/Signature.java
new file mode 100644
index 0000000..c3fb8bb
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/Signature.java
@@ -0,0 +1,40 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2012-2016 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 Signature {
+ void init() throws Exception;
+
+ void update(byte[] H) throws Exception;
+
+ boolean verify(byte[] sig) throws Exception;
+
+ byte[] sign() throws Exception;
+}
diff --git a/src/main/java/com/jcraft/jsch/SignatureDSA.java b/src/main/java/com/jcraft/jsch/SignatureDSA.java
new file mode 100644
index 0000000..9529054
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/SignatureDSA.java
@@ -0,0 +1,36 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface SignatureDSA extends Signature {
+ void setPubKey(byte[] y, byte[] p, byte[] q, byte[] g) throws Exception;
+
+ void setPrvKey(byte[] x, byte[] p, byte[] q, byte[] g) throws Exception;
+}
diff --git a/src/main/java/com/jcraft/jsch/SignatureECDSA.java b/src/main/java/com/jcraft/jsch/SignatureECDSA.java
new file mode 100644
index 0000000..da12573
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/SignatureECDSA.java
@@ -0,0 +1,36 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2015-2016 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 SignatureECDSA extends Signature {
+ void setPubKey(byte[] r, byte[] s) throws Exception;
+
+ void setPrvKey(byte[] s) throws Exception;
+}
diff --git a/src/main/java/com/jcraft/jsch/SignatureRSA.java b/src/main/java/com/jcraft/jsch/SignatureRSA.java
new file mode 100644
index 0000000..6c194fc
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/SignatureRSA.java
@@ -0,0 +1,36 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface SignatureRSA extends Signature {
+ void setPubKey(byte[] e, byte[] n) throws Exception;
+
+ void setPrvKey(byte[] d, byte[] n) throws Exception;
+}
diff --git a/src/main/java/com/jcraft/jsch/SocketFactory.java b/src/main/java/com/jcraft/jsch/SocketFactory.java
new file mode 100644
index 0000000..ac47678
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/SocketFactory.java
@@ -0,0 +1,45 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+public interface SocketFactory {
+ public Socket createSocket(String host, int port) throws IOException,
+ UnknownHostException;
+
+ public InputStream getInputStream(Socket socket) throws IOException;
+
+ public OutputStream getOutputStream(Socket socket) throws IOException;
+}
diff --git a/src/main/java/com/jcraft/jsch/UIKeyboardInteractive.java b/src/main/java/com/jcraft/jsch/UIKeyboardInteractive.java
new file mode 100644
index 0000000..7251348
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/UIKeyboardInteractive.java
@@ -0,0 +1,38 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface UIKeyboardInteractive {
+ String[] promptKeyboardInteractive(String destination,
+ String name,
+ String instruction,
+ String[] prompt,
+ boolean[] echo);
+}
diff --git a/src/main/java/com/jcraft/jsch/UserAuth.java b/src/main/java/com/jcraft/jsch/UserAuth.java
new file mode 100644
index 0000000..94d4f73
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/UserAuth.java
@@ -0,0 +1,53 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public abstract class UserAuth {
+ protected static final int SSH_MSG_USERAUTH_REQUEST = 50;
+ protected static final int SSH_MSG_USERAUTH_FAILURE = 51;
+ protected static final int SSH_MSG_USERAUTH_SUCCESS = 52;
+ protected static final int SSH_MSG_USERAUTH_BANNER = 53;
+ protected static final int SSH_MSG_USERAUTH_INFO_REQUEST = 60;
+ protected static final int SSH_MSG_USERAUTH_INFO_RESPONSE = 61;
+ protected static final int SSH_MSG_USERAUTH_PK_OK = 60;
+
+ protected UserInfo userinfo;
+ protected Packet packet;
+ protected Buffer buf;
+ protected String username;
+
+ public boolean start(Session session) throws Exception {
+ this.userinfo = session.getUserInfo();
+ this.packet = session.packet;
+ this.buf = packet.getBuffer();
+ this.username = session.getUserName();
+ return true;
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java b/src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java
new file mode 100644
index 0000000..4d207b0
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java
@@ -0,0 +1,235 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2006-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING
+NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public class UserAuthGSSAPIWithMIC extends UserAuth {
+ private static final int SSH_MSG_USERAUTH_GSSAPI_RESPONSE = 60;
+ private static final int SSH_MSG_USERAUTH_GSSAPI_TOKEN = 61;
+ private static final int SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE = 63;
+ private static final int SSH_MSG_USERAUTH_GSSAPI_ERROR = 64;
+ private static final int SSH_MSG_USERAUTH_GSSAPI_ERRTOK = 65;
+ private static final int SSH_MSG_USERAUTH_GSSAPI_MIC = 66;
+
+ private static final byte[][] supported_oid = {
+ // OID 1.2.840.113554.1.2.2 in DER
+ {(byte) 0x6, (byte) 0x9, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+ (byte) 0x86, (byte) 0xf7, (byte) 0x12, (byte) 0x1, (byte) 0x2,
+ (byte) 0x2}
+ };
+
+ private static final String[] supported_method = {
+ "gssapi-with-mic.krb5"
+ };
+
+ public boolean start(Session session) throws Exception {
+ super.start(session);
+
+ byte[] _username = Util.str2byte(username);
+
+ packet.reset();
+
+ // byte SSH_MSG_USERAUTH_REQUEST(50)
+ // string user name(in ISO-10646 UTF-8 encoding)
+ // string service name(in US-ASCII)
+ // string "gssapi"(US-ASCII)
+ // uint32 n, the number of OIDs client supports
+ // string[n] mechanism OIDS
+ buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
+ buf.putString(_username);
+ buf.putString(Util.str2byte("ssh-connection"));
+ buf.putString(Util.str2byte("gssapi-with-mic"));
+ buf.putInt(supported_oid.length);
+ for (int i = 0; i < supported_oid.length; i++) {
+ buf.putString(supported_oid[i]);
+ }
+ session.write(packet);
+
+ String method = null;
+ int command;
+ while (true) {
+ buf = session.read(buf);
+ command = buf.getCommand() & 0xff;
+
+ if (command == SSH_MSG_USERAUTH_FAILURE) {
+ return false;
+ }
+
+ if (command == SSH_MSG_USERAUTH_GSSAPI_RESPONSE) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] message = buf.getString();
+
+ for (int i = 0; i < supported_oid.length; i++) {
+ if (Util.array_equals(message, supported_oid[i])) {
+ method = supported_method[i];
+ break;
+ }
+ }
+
+ if (method == null) {
+ return false;
+ }
+
+ break; // success
+ }
+
+ if (command == SSH_MSG_USERAUTH_BANNER) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] _message = buf.getString();
+ byte[] lang = buf.getString();
+ String message = Util.byte2str(_message);
+ if (userinfo != null) {
+ userinfo.showMessage(message);
+ }
+ continue;
+ }
+ return false;
+ }
+
+ GSSContext context = null;
+ try {
+ Class c = Class.forName(session.getConfig(method));
+ context = (GSSContext) (c.newInstance());
+ }
+ catch (Exception e) {
+ return false;
+ }
+
+ try {
+ context.create(username, session.host);
+ }
+ catch (JSchException e) {
+ return false;
+ }
+
+ byte[] token = new byte[0];
+
+ while (!context.isEstablished()) {
+ try {
+ token = context.init(token, 0, token.length);
+ }
+ catch (JSchException e) {
+ // TODO
+ // ERRTOK should be sent?
+ // byte SSH_MSG_USERAUTH_GSSAPI_ERRTOK
+ // string error token
+ return false;
+ }
+
+ if (token != null) {
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_USERAUTH_GSSAPI_TOKEN);
+ buf.putString(token);
+ session.write(packet);
+ }
+
+ if (!context.isEstablished()) {
+ buf = session.read(buf);
+ command = buf.getCommand() & 0xff;
+ if (command == SSH_MSG_USERAUTH_GSSAPI_ERROR) {
+ // uint32 major_status
+ // uint32 minor_status
+ // string message
+ // string language tag
+
+ buf = session.read(buf);
+ command = buf.getCommand() & 0xff;
+ //return false;
+ }
+ else if (command == SSH_MSG_USERAUTH_GSSAPI_ERRTOK) {
+ // string error token
+
+ buf = session.read(buf);
+ command = buf.getCommand() & 0xff;
+ //return false;
+ }
+
+ if (command == SSH_MSG_USERAUTH_FAILURE) {
+ return false;
+ }
+
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ token = buf.getString();
+ }
+ }
+
+ Buffer mbuf = new Buffer();
+ // string session identifier
+ // byte SSH_MSG_USERAUTH_REQUEST
+ // string user name
+ // string service
+ // string "gssapi-with-mic"
+ mbuf.putString(session.getSessionId());
+ mbuf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
+ mbuf.putString(_username);
+ mbuf.putString(Util.str2byte("ssh-connection"));
+ mbuf.putString(Util.str2byte("gssapi-with-mic"));
+
+ byte[] mic = context.getMIC(mbuf.buffer, 0, mbuf.getLength());
+
+ if (mic == null) {
+ return false;
+ }
+
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_USERAUTH_GSSAPI_MIC);
+ buf.putString(mic);
+ session.write(packet);
+
+ context.dispose();
+
+ buf = session.read(buf);
+ command = buf.getCommand() & 0xff;
+
+ if (command == SSH_MSG_USERAUTH_SUCCESS) {
+ return true;
+ }
+ else if (command == SSH_MSG_USERAUTH_FAILURE) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] foo = buf.getString();
+ int partial_success = buf.getByte();
+ //System.err.println(new String(foo)+
+ // " partial_success:"+(partial_success!=0));
+ if (partial_success != 0) {
+ throw new JSchPartialAuthException(Util.byte2str(foo));
+ }
+ }
+ return false;
+ }
+}
+
+
diff --git a/src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java b/src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java
new file mode 100644
index 0000000..bfafad2
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java
@@ -0,0 +1,209 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class UserAuthKeyboardInteractive extends UserAuth {
+ public boolean start(Session session) throws Exception {
+ super.start(session);
+
+ if (userinfo != null && !(userinfo instanceof UIKeyboardInteractive)) {
+ return false;
+ }
+
+ String dest = username + "@" + session.host;
+ if (session.port != 22) {
+ dest += (":" + session.port);
+ }
+ byte[] password = session.password;
+
+ boolean cancel = false;
+
+ byte[] _username = null;
+ _username = Util.str2byte(username);
+
+ while (true) {
+
+ if (session.auth_failures >= session.max_auth_tries) {
+ return false;
+ }
+
+ // send
+ // byte SSH_MSG_USERAUTH_REQUEST(50)
+ // string user name (ISO-10646 UTF-8, as defined in [RFC-2279])
+ // string service name (US-ASCII) "ssh-userauth" ? "ssh-connection"
+ // string "keyboard-interactive" (US-ASCII)
+ // string language tag (as defined in [RFC-3066])
+ // string submethods (ISO-10646 UTF-8)
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
+ buf.putString(_username);
+ buf.putString(Util.str2byte("ssh-connection"));
+ //buf.putString("ssh-userauth".getBytes());
+ buf.putString(Util.str2byte("keyboard-interactive"));
+ buf.putString(Util.empty);
+ buf.putString(Util.empty);
+ session.write(packet);
+
+ boolean firsttime = true;
+ loop:
+ while (true) {
+ buf = session.read(buf);
+ int command = buf.getCommand() & 0xff;
+
+ if (command == SSH_MSG_USERAUTH_SUCCESS) {
+ return true;
+ }
+ if (command == SSH_MSG_USERAUTH_BANNER) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] _message = buf.getString();
+ byte[] lang = buf.getString();
+ String message = Util.byte2str(_message);
+ if (userinfo != null) {
+ userinfo.showMessage(message);
+ }
+ continue loop;
+ }
+ if (command == SSH_MSG_USERAUTH_FAILURE) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] foo = buf.getString();
+ int partial_success = buf.getByte();
+// System.err.println(new String(foo)+
+// " partial_success:"+(partial_success!=0));
+
+ if (partial_success != 0) {
+ throw new JSchPartialAuthException(Util.byte2str(foo));
+ }
+
+ if (firsttime) {
+ return false;
+ //throw new JSchException("USERAUTH KI is not supported");
+ //cancel=true; // ??
+ }
+ session.auth_failures++;
+ break;
+ }
+ if (command == SSH_MSG_USERAUTH_INFO_REQUEST) {
+ firsttime = false;
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ String name = Util.byte2str(buf.getString());
+ String instruction = Util.byte2str(buf.getString());
+ String languate_tag = Util.byte2str(buf.getString());
+ int num = buf.getInt();
+ String[] prompt = new String[num];
+ boolean[] echo = new boolean[num];
+ for (int i = 0; i < num; i++) {
+ prompt[i] = Util.byte2str(buf.getString());
+ echo[i] = (buf.getByte() != 0);
+ }
+
+ byte[][] response = null;
+
+ if (password != null &&
+ prompt.length == 1 &&
+ !echo[0] &&
+ prompt[0].toLowerCase().indexOf("password:") >= 0) {
+ response = new byte[1][];
+ response[0] = password;
+ password = null;
+ }
+ else if (num > 0
+ || (name.length() > 0 || instruction.length() > 0)
+ ) {
+ if (userinfo != null) {
+ UIKeyboardInteractive kbi = (UIKeyboardInteractive) userinfo;
+ String[] _response = kbi.promptKeyboardInteractive(dest,
+ name,
+ instruction,
+ prompt,
+ echo);
+ if (_response != null) {
+ response = new byte[_response.length][];
+ for (int i = 0; i < _response.length; i++) {
+ response[i] = Util.str2byte(_response[i]);
+ }
+ }
+ }
+ }
+
+ // byte SSH_MSG_USERAUTH_INFO_RESPONSE(61)
+ // int num-responses
+ // string response[1] (ISO-10646 UTF-8)
+ // ...
+ // string response[num-responses] (ISO-10646 UTF-8)
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_USERAUTH_INFO_RESPONSE);
+ if (num > 0 &&
+ (response == null || // cancel
+ num != response.length)) {
+
+ if (response == null) {
+ // working around the bug in OpenSSH ;-<
+ buf.putInt(num);
+ for (int i = 0; i < num; i++) {
+ buf.putString(Util.empty);
+ }
+ }
+ else {
+ buf.putInt(0);
+ }
+
+ if (response == null)
+ cancel = true;
+ }
+ else {
+ buf.putInt(num);
+ for (int i = 0; i < num; i++) {
+ buf.putString(response[i]);
+ }
+ }
+ session.write(packet);
+ /*
+ if(cancel)
+ break;
+ */
+ continue loop;
+ }
+ //throw new JSchException("USERAUTH fail ("+command+")");
+ return false;
+ }
+ if (cancel) {
+ throw new JSchAuthCancelException("keyboard-interactive");
+ //break;
+ }
+ }
+ //return false;
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/UserAuthNone.java b/src/main/java/com/jcraft/jsch/UserAuthNone.java
new file mode 100644
index 0000000..1e8b945
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/UserAuthNone.java
@@ -0,0 +1,134 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class UserAuthNone extends UserAuth {
+ private static final int SSH_MSG_SERVICE_ACCEPT = 6;
+ private String methods = null;
+
+ public boolean start(Session session) throws Exception {
+ super.start(session);
+
+
+ // send
+ // byte SSH_MSG_SERVICE_REQUEST(5)
+ // string service name "ssh-userauth"
+ packet.reset();
+ buf.putByte((byte) Session.SSH_MSG_SERVICE_REQUEST);
+ buf.putString(Util.str2byte("ssh-userauth"));
+ session.write(packet);
+
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ JSch.getLogger().log(Logger.INFO,
+ "SSH_MSG_SERVICE_REQUEST sent");
+ }
+
+ // receive
+ // byte SSH_MSG_SERVICE_ACCEPT(6)
+ // string service name
+ buf = session.read(buf);
+ int command = buf.getCommand();
+
+ boolean result = (command == SSH_MSG_SERVICE_ACCEPT);
+
+ if (JSch.getLogger().isEnabled(Logger.INFO)) {
+ JSch.getLogger().log(Logger.INFO,
+ "SSH_MSG_SERVICE_ACCEPT received");
+ }
+ if (!result)
+ return false;
+
+ byte[] _username = null;
+ _username = Util.str2byte(username);
+
+ // send
+ // byte SSH_MSG_USERAUTH_REQUEST(50)
+ // string user name
+ // string service name ("ssh-connection")
+ // string "none"
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
+ buf.putString(_username);
+ buf.putString(Util.str2byte("ssh-connection"));
+ buf.putString(Util.str2byte("none"));
+ session.write(packet);
+
+ loop:
+ while (true) {
+ buf = session.read(buf);
+ command = buf.getCommand() & 0xff;
+
+ if (command == SSH_MSG_USERAUTH_SUCCESS) {
+ return true;
+ }
+ if (command == SSH_MSG_USERAUTH_BANNER) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] _message = buf.getString();
+ byte[] lang = buf.getString();
+ String message = Util.byte2str(_message);
+ if (userinfo != null) {
+ try {
+ userinfo.showMessage(message);
+ }
+ catch (RuntimeException ee) {
+ }
+ }
+ continue loop;
+ }
+ if (command == SSH_MSG_USERAUTH_FAILURE) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] foo = buf.getString();
+ int partial_success = buf.getByte();
+ methods = Util.byte2str(foo);
+//System.err.println("UserAuthNONE: "+methods+
+// " partial_success:"+(partial_success!=0));
+// if(partial_success!=0){
+// throw new JSchPartialAuthException(new String(foo));
+// }
+
+ break;
+ }
+ else {
+// System.err.println("USERAUTH fail ("+command+")");
+ throw new JSchException("USERAUTH fail (" + command + ")");
+ }
+ }
+ //throw new JSchException("USERAUTH fail");
+ return false;
+ }
+
+ String getMethods() {
+ return methods;
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/UserAuthPassword.java b/src/main/java/com/jcraft/jsch/UserAuthPassword.java
new file mode 100644
index 0000000..8ffc6ef
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/UserAuthPassword.java
@@ -0,0 +1,199 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+class UserAuthPassword extends UserAuth {
+ private final int SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60;
+
+ public boolean start(Session session) throws Exception {
+ super.start(session);
+
+ byte[] password = session.password;
+ String dest = username + "@" + session.host;
+ if (session.port != 22) {
+ dest += (":" + session.port);
+ }
+
+ try {
+
+ while (true) {
+
+ if (session.auth_failures >= session.max_auth_tries) {
+ return false;
+ }
+
+ if (password == null) {
+ if (userinfo == null) {
+ //throw new JSchException("USERAUTH fail");
+ return false;
+ }
+ if (!userinfo.promptPassword("Password for " + dest)) {
+ throw new JSchAuthCancelException("password");
+ //break;
+ }
+
+ String _password = userinfo.getPassword();
+ if (_password == null) {
+ throw new JSchAuthCancelException("password");
+ //break;
+ }
+ password = Util.str2byte(_password);
+ }
+
+ byte[] _username = null;
+ _username = Util.str2byte(username);
+
+ // send
+ // byte SSH_MSG_USERAUTH_REQUEST(50)
+ // string user name
+ // string service name ("ssh-connection")
+ // string "password"
+ // boolen FALSE
+ // string plaintext password (ISO-10646 UTF-8)
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
+ buf.putString(_username);
+ buf.putString(Util.str2byte("ssh-connection"));
+ buf.putString(Util.str2byte("password"));
+ buf.putByte((byte) 0);
+ buf.putString(password);
+ session.write(packet);
+
+ loop:
+ while (true) {
+ buf = session.read(buf);
+ int command = buf.getCommand() & 0xff;
+
+ if (command == SSH_MSG_USERAUTH_SUCCESS) {
+ return true;
+ }
+ if (command == SSH_MSG_USERAUTH_BANNER) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] _message = buf.getString();
+ byte[] lang = buf.getString();
+ String message = Util.byte2str(_message);
+ if (userinfo != null) {
+ userinfo.showMessage(message);
+ }
+ continue loop;
+ }
+ if (command == SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] instruction = buf.getString();
+ byte[] tag = buf.getString();
+ if (userinfo == null ||
+ !(userinfo instanceof UIKeyboardInteractive)) {
+ if (userinfo != null) {
+ userinfo.showMessage("Password must be changed.");
+ }
+ return false;
+ }
+
+ UIKeyboardInteractive kbi = (UIKeyboardInteractive) userinfo;
+ String[] response;
+ String name = "Password Change Required";
+ String[] prompt = {"New Password: "};
+ boolean[] echo = {false};
+ response = kbi.promptKeyboardInteractive(dest,
+ name,
+ Util.byte2str(instruction),
+ prompt,
+ echo);
+ if (response == null) {
+ throw new JSchAuthCancelException("password");
+ }
+
+ byte[] newpassword = Util.str2byte(response[0]);
+
+ // send
+ // byte SSH_MSG_USERAUTH_REQUEST(50)
+ // string user name
+ // string service name ("ssh-connection")
+ // string "password"
+ // boolen TRUE
+ // string plaintext old password (ISO-10646 UTF-8)
+ // string plaintext new password (ISO-10646 UTF-8)
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
+ buf.putString(_username);
+ buf.putString(Util.str2byte("ssh-connection"));
+ buf.putString(Util.str2byte("password"));
+ buf.putByte((byte) 1);
+ buf.putString(password);
+ buf.putString(newpassword);
+ Util.bzero(newpassword);
+ response = null;
+ session.write(packet);
+ continue loop;
+ }
+ if (command == SSH_MSG_USERAUTH_FAILURE) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] foo = buf.getString();
+ int partial_success = buf.getByte();
+ //System.err.println(new String(foo)+
+ // " partial_success:"+(partial_success!=0));
+ if (partial_success != 0) {
+ throw new JSchPartialAuthException(Util.byte2str(foo));
+ }
+ session.auth_failures++;
+ break;
+ }
+ else {
+ //System.err.println("USERAUTH fail ("+buf.getCommand()+")");
+// throw new JSchException("USERAUTH fail ("+buf.getCommand()+")");
+ return false;
+ }
+ }
+
+ if (password != null) {
+ Util.bzero(password);
+ password = null;
+ }
+
+ }
+
+ }
+ finally {
+ if (password != null) {
+ Util.bzero(password);
+ password = null;
+ }
+ }
+
+ //throw new JSchException("USERAUTH fail");
+ //return false;
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java b/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java
new file mode 100644
index 0000000..b8768c6
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java
@@ -0,0 +1,240 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+import java.util.Vector;
+
+class UserAuthPublicKey extends UserAuth {
+
+ public boolean start(Session session) throws Exception {
+ super.start(session);
+
+ Vector identities = session.getIdentityRepository().getIdentities();
+
+ byte[] passphrase = null;
+ byte[] _username = null;
+
+ int command;
+
+ synchronized (identities) {
+ if (identities.size() <= 0) {
+ return false;
+ }
+
+ _username = Util.str2byte(username);
+
+ for (int i = 0; i < identities.size(); i++) {
+
+ if (session.auth_failures >= session.max_auth_tries) {
+ return false;
+ }
+
+ Identity identity = (Identity) (identities.elementAt(i));
+ byte[] pubkeyblob = identity.getPublicKeyBlob();
+
+ if (pubkeyblob != null) {
+ // send
+ // byte SSH_MSG_USERAUTH_REQUEST(50)
+ // string user name
+ // string service name ("ssh-connection")
+ // string "publickey"
+ // boolen FALSE
+ // string public key algorithm name
+ // string public key blob
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
+ buf.putString(_username);
+ buf.putString(Util.str2byte("ssh-connection"));
+ buf.putString(Util.str2byte("publickey"));
+ buf.putByte((byte) 0);
+ buf.putString(Util.str2byte(identity.getAlgName()));
+ buf.putString(pubkeyblob);
+ session.write(packet);
+
+ loop1:
+ while (true) {
+ buf = session.read(buf);
+ command = buf.getCommand() & 0xff;
+
+ if (command == SSH_MSG_USERAUTH_PK_OK) {
+ break;
+ }
+ else if (command == SSH_MSG_USERAUTH_FAILURE) {
+ break;
+ }
+ else if (command == SSH_MSG_USERAUTH_BANNER) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] _message = buf.getString();
+ byte[] lang = buf.getString();
+ String message = Util.byte2str(_message);
+ if (userinfo != null) {
+ userinfo.showMessage(message);
+ }
+ continue loop1;
+ }
+ else {
+ //System.err.println("USERAUTH fail ("+command+")");
+ //throw new JSchException("USERAUTH fail ("+command+")");
+ break;
+ }
+ }
+
+ if (command != SSH_MSG_USERAUTH_PK_OK) {
+ continue;
+ }
+ }
+
+//System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted());
+
+ int count = 5;
+ while (true) {
+ if ((identity.isEncrypted() && passphrase == null)) {
+ if (userinfo == null) throw new JSchException("USERAUTH fail");
+ if (identity.isEncrypted() &&
+ !userinfo.promptPassphrase("Passphrase for " + identity.getName())) {
+ throw new JSchAuthCancelException("publickey");
+ //throw new JSchException("USERAUTH cancel");
+ //break;
+ }
+ String _passphrase = userinfo.getPassphrase();
+ if (_passphrase != null) {
+ passphrase = Util.str2byte(_passphrase);
+ }
+ }
+
+ if (!identity.isEncrypted() || passphrase != null) {
+ if (identity.setPassphrase(passphrase)) {
+ if (passphrase != null &&
+ (session.getIdentityRepository() instanceof IdentityRepository.Wrapper)) {
+ ((IdentityRepository.Wrapper) session.getIdentityRepository()).check();
+ }
+ break;
+ }
+ }
+ Util.bzero(passphrase);
+ passphrase = null;
+ count--;
+ if (count == 0) break;
+ }
+
+ Util.bzero(passphrase);
+ passphrase = null;
+//System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted());
+
+ if (identity.isEncrypted()) continue;
+ if (pubkeyblob == null) pubkeyblob = identity.getPublicKeyBlob();
+
+//System.err.println("UserAuthPublicKey: pubkeyblob="+pubkeyblob);
+
+ if (pubkeyblob == null) continue;
+
+ // send
+ // byte SSH_MSG_USERAUTH_REQUEST(50)
+ // string user name
+ // string service name ("ssh-connection")
+ // string "publickey"
+ // boolen TRUE
+ // string public key algorithm name
+ // string public key blob
+ // string signature
+ packet.reset();
+ buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
+ buf.putString(_username);
+ buf.putString(Util.str2byte("ssh-connection"));
+ buf.putString(Util.str2byte("publickey"));
+ buf.putByte((byte) 1);
+ buf.putString(Util.str2byte(identity.getAlgName()));
+ buf.putString(pubkeyblob);
+
+// byte[] tmp=new byte[buf.index-5];
+// System.arraycopy(buf.buffer, 5, tmp, 0, tmp.length);
+// buf.putString(signature);
+
+ byte[] sid = session.getSessionId();
+ int sidlen = sid.length;
+ byte[] tmp = new byte[4 + sidlen + buf.index - 5];
+ tmp[0] = (byte) (sidlen >>> 24);
+ tmp[1] = (byte) (sidlen >>> 16);
+ tmp[2] = (byte) (sidlen >>> 8);
+ tmp[3] = (byte) (sidlen);
+ System.arraycopy(sid, 0, tmp, 4, sidlen);
+ System.arraycopy(buf.buffer, 5, tmp, 4 + sidlen, buf.index - 5);
+ byte[] signature = identity.getSignature(tmp);
+ if (signature == null) { // for example, too long key length.
+ break;
+ }
+ buf.putString(signature);
+ session.write(packet);
+
+ loop2:
+ while (true) {
+ buf = session.read(buf);
+ command = buf.getCommand() & 0xff;
+
+ if (command == SSH_MSG_USERAUTH_SUCCESS) {
+ return true;
+ }
+ else if (command == SSH_MSG_USERAUTH_BANNER) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] _message = buf.getString();
+ byte[] lang = buf.getString();
+ String message = Util.byte2str(_message);
+ if (userinfo != null) {
+ userinfo.showMessage(message);
+ }
+ continue loop2;
+ }
+ else if (command == SSH_MSG_USERAUTH_FAILURE) {
+ buf.getInt();
+ buf.getByte();
+ buf.getByte();
+ byte[] foo = buf.getString();
+ int partial_success = buf.getByte();
+ //System.err.println(new String(foo)+
+ // " partial_success:"+(partial_success!=0));
+ if (partial_success != 0) {
+ throw new JSchPartialAuthException(Util.byte2str(foo));
+ }
+ session.auth_failures++;
+ break;
+ }
+ //System.err.println("USERAUTH fail ("+command+")");
+ //throw new JSchException("USERAUTH fail ("+command+")");
+ break;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/jcraft/jsch/UserInfo.java b/src/main/java/com/jcraft/jsch/UserInfo.java
new file mode 100644
index 0000000..d780f93
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/UserInfo.java
@@ -0,0 +1,44 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.jcraft.jsch;
+
+public interface UserInfo {
+ String getPassphrase();
+
+ String getPassword();
+
+ boolean promptPassword(String message);
+
+ boolean promptPassphrase(String message);
+
+ boolean promptYesNo(String message);
+
+ void showMessage(String message);
+}
diff --git a/src/main/java/com/jcraft/jsch/Util.java b/src/main/java/com/jcraft/jsch/Util.java
new file mode 100644
index 0000000..137f2e0
--- /dev/null
+++ b/src/main/java/com/jcraft/jsch/Util.java
@@ -0,0 +1,554 @@
+/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
+/*
+Copyright (c) 2002-2016 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.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.Socket;
+
+class Util {
+
+ private static final byte[] b64 = Util.str2byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
+
+ private static byte val(byte foo) {
+ if (foo == '=') return 0;
+ for (int j = 0; j < b64.length; j++) {
+ if (foo == b64[j]) return (byte) j;
+ }
+ return 0;
+ }
+
+ static byte[] fromBase64(byte[] buf, int start, int length) throws JSchException {
+ try {
+ byte[] foo = new byte[length];
+ int j = 0;
+ for (int i = start; i < start + length; i += 4) {
+ foo[j] = (byte) ((val(buf[i]) << 2) | ((val(buf[i + 1]) & 0x30) >>> 4));
+ if (buf[i + 2] == (byte) '=') {
+ j++;
+ break;
+ }
+ foo[j + 1] = (byte) (((val(buf[i + 1]) & 0x0f) << 4) | ((val(buf[i + 2]) & 0x3c) >>> 2));
+ if (buf[i + 3] == (byte) '=') {
+ j += 2;
+ break;
+ }
+ foo[j + 2] = (byte) (((val(buf[i + 2]) & 0x03) << 6) | (val(buf[i + 3]) & 0x3f));
+ j += 3;
+ }
+ byte[] bar = new byte[j];
+ System.arraycopy(foo, 0, bar, 0, j);
+ return bar;
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ throw new JSchException("fromBase64: invalid base64 data", e);
+ }
+ }
+
+ static byte[] toBase64(byte[] buf, int start, int length) {
+
+ byte[] tmp = new byte[length * 2];
+ int i, j, k;
+
+ int foo = (length / 3) * 3 + start;
+ i = 0;
+ for (j = start; j < foo; j += 3) {
+ k = (buf[j] >>> 2) & 0x3f;
+ tmp[i++] = b64[k];
+ k = (buf[j] & 0x03) << 4 | (buf[j + 1] >>> 4) & 0x0f;
+ tmp[i++] = b64[k];
+ k = (buf[j + 1] & 0x0f) << 2 | (buf[j + 2] >>> 6) & 0x03;
+ tmp[i++] = b64[k];
+ k = buf[j + 2] & 0x3f;
+ tmp[i++] = b64[k];
+ }
+
+ foo = (start + length) - foo;
+ if (foo == 1) {
+ k = (buf[j] >>> 2) & 0x3f;
+ tmp[i++] = b64[k];
+ k = ((buf[j] & 0x03) << 4) & 0x3f;
+ tmp[i++] = b64[k];
+ tmp[i++] = (byte) '=';
+ tmp[i++] = (byte) '=';
+ }
+ else if (foo == 2) {
+ k = (buf[j] >>> 2) & 0x3f;
+ tmp[i++] = b64[k];
+ k = (buf[j] & 0x03) << 4 | (buf[j + 1] >>> 4) & 0x0f;
+ tmp[i++] = b64[k];
+ k = ((buf[j + 1] & 0x0f) << 2) & 0x3f;
+ tmp[i++] = b64[k];
+ tmp[i++] = (byte) '=';
+ }
+ byte[] bar = new byte[i];
+ System.arraycopy(tmp, 0, bar, 0, i);
+ return bar;
+
+// return sun.misc.BASE64Encoder().encode(buf);
+ }
+
+ static String[] split(String foo, String split) {
+ if (foo == null)
+ return null;
+ byte[] buf = Util.str2byte(foo);
+ java.util.Vector bar = new java.util.Vector();
+ int start = 0;
+ int index;
+ while (true) {
+ index = foo.indexOf(split, start);
+ if (index >= 0) {
+ bar.addElement(Util.byte2str(buf, start, index - start));
+ start = index + 1;
+ continue;
+ }
+ bar.addElement(Util.byte2str(buf, start, buf.length - start));
+ break;
+ }
+ String[] result = new String[bar.size()];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = (String) (bar.elementAt(i));
+ }
+ return result;
+ }
+
+ static boolean glob(byte[] pattern, byte[] name) {
+ return glob0(pattern, 0, name, 0);
+ }
+
+ static private boolean glob0(byte[] pattern, int pattern_index,
+ byte[] name, int name_index) {
+ if (name.length > 0 && name[0] == '.') {
+ if (pattern.length > 0 && pattern[0] == '.') {
+ if (pattern.length == 2 && pattern[1] == '*') return true;
+ return glob(pattern, pattern_index + 1, name, name_index + 1);
+ }
+ return false;
+ }
+ return glob(pattern, pattern_index, name, name_index);
+ }
+
+ static private boolean glob(byte[] pattern, int pattern_index,
+ byte[] name, int name_index) {
+ //System.err.println("glob: "+new String(pattern)+", "+pattern_index+" "+new String(name)+", "+name_index);
+
+ int patternlen = pattern.length;
+ if (patternlen == 0)
+ return false;
+
+ int namelen = name.length;
+ int i = pattern_index;
+ int j = name_index;
+
+ while (i < patternlen && j < namelen) {
+ if (pattern[i] == '\\') {
+ if (i + 1 == patternlen)
+ return false;
+ i++;
+ if (pattern[i] != name[j])
+ return false;
+ i += skipUTF8Char(pattern[i]);
+ j += skipUTF8Char(name[j]);
+ continue;
+ }
+
+ if (pattern[i] == '*') {
+ while (i < patternlen) {
+ if (pattern[i] == '*') {
+ i++;
+ continue;
+ }
+ break;
+ }
+ if (patternlen == i)
+ return true;
+
+ byte foo = pattern[i];
+ if (foo == '?') {
+ while (j < namelen) {
+ if (glob(pattern, i, name, j)) {
+ return true;
+ }
+ j += skipUTF8Char(name[j]);
+ }
+ return false;
+ }
+ else if (foo == '\\') {
+ if (i + 1 == patternlen)
+ return false;
+ i++;
+ foo = pattern[i];
+ while (j < namelen) {
+ if (foo == name[j]) {
+ if (glob(pattern, i + skipUTF8Char(foo),
+ name, j + skipUTF8Char(name[j]))) {
+ return true;
+ }
+ }
+ j += skipUTF8Char(name[j]);
+ }
+ return false;
+ }
+
+ while (j < namelen) {
+ if (foo == name[j]) {
+ if (glob(pattern, i, name, j)) {
+ return true;
+ }
+ }
+ j += skipUTF8Char(name[j]);
+ }
+ return false;
+ }
+
+ if (pattern[i] == '?') {
+ i++;
+ j += skipUTF8Char(name[j]);
+ continue;
+ }
+
+ if (pattern[i] != name[j])
+ return false;
+
+ i += skipUTF8Char(pattern[i]);
+ j += skipUTF8Char(name[j]);
+
+ if (!(j < namelen)) { // name is end
+ if (!(i < patternlen)) { // pattern is end
+ return true;
+ }
+ if (pattern[i] == '*') {
+ break;
+ }
+ }
+ continue;
+ }
+
+ if (i == patternlen && j == namelen)
+ return true;
+
+ if (!(j < namelen) && // name is end
+ pattern[i] == '*') {
+ boolean ok = true;
+ while (i < patternlen) {
+ if (pattern[i++] != '*') {
+ ok = false;
+ break;
+ }
+ }
+ return ok;
+ }
+
+ return false;
+ }
+
+ static String quote(String path) {
+ byte[] _path = str2byte(path);
+ int count = 0;
+ for (int i = 0; i < _path.length; i++) {
+ byte b = _path[i];
+ if (b == '\\' || b == '?' || b == '*')
+ count++;
+ }
+ if (count == 0)
+ return path;
+ byte[] _path2 = new byte[_path.length + count];
+ for (int i = 0, j = 0; i < _path.length; i++) {
+ byte b = _path[i];
+ if (b == '\\' || b == '?' || b == '*') {
+ _path2[j++] = '\\';
+ }
+ _path2[j++] = b;
+ }
+ return byte2str(_path2);
+ }
+
+ static String unquote(String path) {
+ byte[] foo = str2byte(path);
+ byte[] bar = unquote(foo);
+ if (foo.length == bar.length)
+ return path;
+ return byte2str(bar);
+ }
+
+ static byte[] unquote(byte[] path) {
+ int pathlen = path.length;
+ int i = 0;
+ while (i < pathlen) {
+ if (path[i] == '\\') {
+ if (i + 1 == pathlen)
+ break;
+ System.arraycopy(path, i + 1, path, i, path.length - (i + 1));
+ pathlen--;
+ i++;
+ continue;
+ }
+ i++;
+ }
+ if (pathlen == path.length)
+ return path;
+ byte[] foo = new byte[pathlen];
+ System.arraycopy(path, 0, foo, 0, pathlen);
+ return foo;
+ }
+
+ private static String[] chars = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"
+ };
+
+ static String getFingerPrint(HASH hash, byte[] data) {
+ try {
+ hash.init();
+ hash.update(data, 0, data.length);
+ byte[] foo = hash.digest();
+ StringBuffer sb = new StringBuffer();
+ int bar;
+ for (int i = 0; i < foo.length; i++) {
+ bar = foo[i] & 0xff;
+ sb.append(chars[(bar >>> 4) & 0xf]);
+ sb.append(chars[(bar) & 0xf]);
+ if (i + 1 < foo.length)
+ sb.append(":");
+ }
+ return sb.toString();
+ }
+ catch (Exception e) {
+ return "???";
+ }
+ }
+
+ static boolean array_equals(byte[] foo, byte bar[]) {
+ int i = foo.length;
+ if (i != bar.length) return false;
+ for (int j = 0; j < i; j++) {
+ if (foo[j] != bar[j]) return false;
+ }
+ //try{while(true){i--; if(foo[i]!=bar[i])return false;}}catch(Exception e){}
+ return true;
+ }
+
+ static Socket createSocket(String host, int port, int timeout) throws JSchException {
+ Socket socket = null;
+ if (timeout == 0) {
+ try {
+ socket = new Socket(host, port);
+ return socket;
+ }
+ catch (Exception e) {
+ String message = e.toString();
+ if (e instanceof Throwable)
+ throw new JSchException(message, (Throwable) e);
+ throw new JSchException(message);
+ }
+ }
+ final String _host = host;
+ final int _port = port;
+ final Socket[] sockp = new Socket[1];
+ final Exception[] ee = new Exception[1];
+ String message = "";
+ Thread tmp = new Thread(new Runnable() {
+ public void run() {
+ sockp[0] = null;
+ try {
+ sockp[0] = new Socket(_host, _port);
+ }
+ catch (Exception e) {
+ ee[0] = e;
+ if (sockp[0] != null && sockp[0].isConnected()) {
+ try {
+ sockp[0].close();
+ }
+ catch (Exception eee) {
+ }
+ }
+ sockp[0] = null;
+ }
+ }
+ });
+ tmp.setName("Opening Socket " + host);
+ tmp.start();
+ try {
+ tmp.join(timeout);
+ message = "timeout: ";
+ }
+ catch (java.lang.InterruptedException eee) {
+ }
+ if (sockp[0] != null && sockp[0].isConnected()) {
+ socket = sockp[0];
+ }
+ else {
+ message += "socket is not established";
+ if (ee[0] != null) {
+ message = ee[0].toString();
+ }
+ tmp.interrupt();
+ tmp = null;
+ throw new JSchException(message, ee[0]);
+ }
+ return socket;
+ }
+
+ static byte[] str2byte(String str, String encoding) {
+ if (str == null)
+ return null;
+ try {
+ return str.getBytes(encoding);
+ }
+ catch (java.io.UnsupportedEncodingException e) {
+ return str.getBytes();
+ }
+ }
+
+ static byte[] str2byte(String str) {
+ return str2byte(str, "UTF-8");
+ }
+
+ static String byte2str(byte[] str, String encoding) {
+ return byte2str(str, 0, str.length, encoding);
+ }
+
+ static String byte2str(byte[] str, int s, int l, String encoding) {
+ try {
+ return new String(str, s, l, encoding);
+ }
+ catch (java.io.UnsupportedEncodingException e) {
+ return new String(str, s, l);
+ }
+ }
+
+ static String byte2str(byte[] str) {
+ return byte2str(str, 0, str.length, "UTF-8");
+ }
+
+ static String byte2str(byte[] str, int s, int l) {
+ return byte2str(str, s, l, "UTF-8");
+ }
+
+ static String toHex(byte[] str) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < str.length; i++) {
+ String foo = Integer.toHexString(str[i] & 0xff);
+ sb.append("0x" + (foo.length() == 1 ? "0" : "") + foo);
+ if (i + 1 < str.length)
+ sb.append(":");
+ }
+ return sb.toString();
+ }
+
+ static final byte[] empty = str2byte("");
+
+ /*
+ static byte[] char2byte(char[] foo){
+ int len=0;
+ for(int i=0; i