ChannelSftp.java
/*
* Copyright (c) 2002-2018 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.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Hashtable;
import java.util.Vector;
public class ChannelSftp extends ChannelSession {
private static final int LOCAL_MAXIMUM_PACKET_SIZE = 32 * 1024;
private static 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 Hashtable<String, String> 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 = File.separator;
private static final char file_separatorc = File.separatorChar;
private static boolean fs_is_bs = (byte) File.separatorChar == '\\';
private String cwd;
private String home;
private String lcwd;
private Charset fEncoding = StandardCharsets.UTF_8;
private boolean fEncoding_is_utf8 = true;
private boolean useWriteFlushWorkaround = 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 void setUseWriteFlushWorkaround(boolean useWriteFlushWorkaround) {
this.useWriteFlushWorkaround = useWriteFlushWorkaround;
}
public boolean getUseWriteFlushWorkaround() {
return useWriteFlushWorkaround;
}
public ChannelSftp() {
super();
lwsize_max = LOCAL_WINDOW_SIZE_MAX;
lwsize = LOCAL_WINDOW_SIZE_MAX;
lmpsize = LOCAL_MAXIMUM_PACKET_SIZE;
}
@Override
void init() {}
@Override
public void start() throws JSchException {
try {
PipedOutputStream pos = new PipedOutputStream();
io.setOutputStream(pos);
PipedInputStream pis = new MyPipedInputStream(pos, rq.size() * 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 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;
throw new JSchException(e.toString(), e);
}
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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 <code>src</code> file to <code>dst</code> file. The <code>mode</code> should be
* <code>OVERWRITE</code>, <code>RESUME</code> or <code>APPEND</code>.
*
* @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<String> 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 = v.elementAt(0);
}
boolean isRemoteDir = isRemoteDir(dst);
v = glob_local(src);
vsize = v.size();
StringBuilder dstsb = null;
if (isRemoteDir) {
if (!dst.endsWith("/")) {
dst += "/";
}
dstsb = new StringBuilder(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 = 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);
}
}
try (InputStream fis = new FileInputStream(_src)) {
_put(fis, _dst, monitor, mode);
}
}
} catch (Exception e) {
if (e instanceof SftpException)
throw (SftpException) e;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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 <code>src</code> to <code>dst</code> file. The
* <code>mode</code> should be <code>OVERWRITE</code>, <code>RESUME</code> or <code>APPEND</code>.
*
* @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<String> 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 = 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;
}
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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;
int buffer_margin = session.getBufferMargin();
if (!dontcopy) { // This case will not work anymore.
data = new byte[obuf.buffer.length - (5 + 13 + 21 + handle.length + 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 - 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) {
if (getSession().getLogger().isEnabled(Logger.ERROR)) {
getSession().getLogger().log(Logger.ERROR,
"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 - 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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 <code>dst</code> file. The <code>mode</code> should be
* <code>OVERWRITE</code>, <code>RESUME</code> or <code>APPEND</code>.
*
* @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();
@Override
public void write(byte[] d) throws IOException {
write(d, 0, d.length);
}
@Override
public void write(byte[] d, int s, int len) throws IOException {
if (init) {
startid = seq;
_ackid = seq;
init = false;
}
if (isClosed) {
throw new IOException("stream already closed");
}
try {
int _len = len;
while (_len > 0) {
if (useWriteFlushWorkaround && rwsize < 21 + handle.length + _len + 4) {
flush();
}
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(), e);
}
}
byte[] _data = new byte[1];
@Override
public void write(int foo) throws IOException {
_data[0] = (byte) foo;
write(_data, 0, 1);
}
@Override
public void flush() throws 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(), e);
}
}
}
@Override
public void close() throws 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(), e);
}
isClosed = true;
}
};
return out;
} catch (Exception e) {
if (e instanceof SftpException)
throw (SftpException) e;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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<String> 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();
StringBuilder dstsb = null;
if (isDstDir) {
if (!dst.endsWith(file_separator)) {
dst += file_separator;
}
dstsb = new StringBuilder(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 = 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 File(dst)).getCanonicalPath();
String _dstc = (new 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());
}
}
_dstExist = _dstFile.exists();
try (OutputStream fos = mode == OVERWRITE ? new FileOutputStream(_dst)
: new FileOutputStream(_dst, true) /* append */) {
// System.err.println("_get: "+_src+", "+_dst);
_get(_src, fos, monitor, mode, new File(_dst).length());
}
}
} 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
private class RequestQueue {
class OutOfOrderException extends Exception {
private static final long serialVersionUID = -1L;
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.
*/
@Deprecated
public InputStream get(String src, int mode) throws SftpException {
return get(src, null, 0L);
}
/**
* @deprecated This method will be deleted in the future.
*/
@Deprecated
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();
InputStream in = new 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;
@Override
public int read() throws IOException {
if (closed)
return -1;
int i = read(_data, 0, 1);
if (i == -1) {
return -1;
} else {
return _data[0] & 0xff;
}
}
@Override
public int read(byte[] d) throws IOException {
if (closed)
return -1;
return read(d, 0, d.length);
}
@Override
public int read(byte[] d, int s, int len) throws 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(), e);
}
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; // ??
}
@Override
public void close() throws IOException {
if (closed)
return;
closed = true;
if (monitor != null)
monitor.end();
rq.cancel(header, buf);
try {
_sendCLOSE(handle, header);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException(e.toString(), e);
}
}
};
return in;
} catch (Exception e) {
if (e instanceof SftpException)
throw (SftpException) e;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
public Vector<LsEntry> ls(String path) throws SftpException {
final Vector<LsEntry> v = new Vector<>();
LsEntrySelector selector = new LsEntrySelector() {
@Override
public int select(LsEntry entry) {
v.addElement(entry);
return CONTINUE;
}
};
ls(path, selector);
return v;
}
/**
* List files specified by the remote <code>path</code>. Each files and directories will be passed
* to <code>LsEntrySelector#select(LsEntry)</code> method, and if that method returns
* <code>LsEntrySelector#BREAK</code>, 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;
Vector<LsEntry> v = new 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, StandardCharsets.UTF_8);
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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<String> v = glob_remote(newpath);
int vsize = v.size();
if (vsize >= 2) {
throw new SftpException(SSH_FX_FAILURE, v.toString());
}
if (vsize == 1) {
newpath = 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
public void rm(String path) throws SftpException {
try {
((MyPipedInputStream) io_in).updateReadSide();
path = remoteAbsolutePath(path);
Vector<String> v = glob_remote(path);
int vsize = v.size();
Header header = new Header();
for (int j = 0; j < vsize; j++) {
path = 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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<String> v = glob_remote(path);
int vsize = v.size();
for (int j = 0; j < vsize; j++) {
path = 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
public void chown(int uid, String path) throws SftpException {
try {
((MyPipedInputStream) io_in).updateReadSide();
path = remoteAbsolutePath(path);
Vector<String> v = glob_remote(path);
int vsize = v.size();
for (int j = 0; j < vsize; j++) {
path = 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
public void chmod(int permissions, String path) throws SftpException {
try {
((MyPipedInputStream) io_in).updateReadSide();
path = remoteAbsolutePath(path);
Vector<String> v = glob_remote(path);
int vsize = v.size();
for (int j = 0; j < vsize; j++) {
path = 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
public void setMtime(String path, int mtime) throws SftpException {
try {
((MyPipedInputStream) io_in).updateReadSide();
path = remoteAbsolutePath(path);
Vector<String> v = glob_remote(path);
int vsize = v.size();
for (int j = 0; j < vsize; j++) {
path = 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
public void rmdir(String path) throws SftpException {
try {
((MyPipedInputStream) io_in).updateReadSide();
path = remoteAbsolutePath(path);
Vector<String> v = glob_remote(path);
int vsize = v.size();
Header header = new Header();
for (int j = 0; j < vsize; j++) {
path = 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
// 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
// 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
// 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
// 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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<String> v = glob_remote(path);
int vsize = v.size();
for (int j = 0; j < vsize; j++) {
path = v.elementAt(j);
_setStat(path, attr);
}
} catch (Exception e) {
if (e instanceof SftpException)
throw (SftpException) e;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
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();
Session _session = getSession();
int buffer_margin = _session.getBufferMargin();
if (obuf.buffer.length < obuf.index + 13 + 21 + handle.length + length + buffer_margin) {
_length = obuf.buffer.length - (obuf.index + 13 + 21 + handle.length + 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);
}
_session.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<String> glob_remote(String _path) throws Exception {
Vector<String> 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, StandardCharsets.UTF_8);
}
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<String> glob_local(String _path) throws Exception {
// System.err.println("glob_local: "+_path);
Vector<String> v = new Vector<>();
byte[] path = Util.str2byte(_path, StandardCharsets.UTF_8);
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, StandardCharsets.UTF_8))).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], StandardCharsets.UTF_8))) {
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, StandardCharsets.UTF_8));
} else {
throw new SftpException(i, "Failure");
}
}
private static boolean isLocalAbsolutePath(String path) {
return (new File(path)).isAbsolute();
}
@Override
public void disconnect() {
super.disconnect();
}
private boolean isPattern(String path, byte[][] utf8) {
byte[] _path = Util.str2byte(path, StandardCharsets.UTF_8);
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;
}
}
static 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<String> v = glob_remote(path);
if (v.size() != 1) {
throw new SftpException(SSH_FX_FAILURE, path + " is not unique: " + v.toString());
}
return v.elementAt(0);
}
public int getServerVersion() throws SftpException {
if (!isConnected()) {
throw new SftpException(SSH_FX_FAILURE, "The channel is not connected.");
}
return server_version;
}
@Deprecated
public void setFilenameEncoding(String encoding) throws SftpException {
try {
setFilenameEncoding(Charset.forName(encoding));
} catch (Exception e) {
if (e instanceof SftpException)
throw (SftpException) e;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
public void setFilenameEncoding(Charset encoding) {
fEncoding = encoding;
fEncoding_is_utf8 = fEncoding.equals(StandardCharsets.UTF_8);
}
public String getExtension(String key) {
if (extensions == null)
return null;
return 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;
throw new SftpException(SSH_FX_FAILURE, e.toString(), e);
}
}
public static class LsEntry implements Comparable<LsEntry> {
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;
};
@Override
public String toString() {
return longname;
}
@Override
public int compareTo(LsEntry o) {
return filename.compareTo(o.getFilename());
}
}
/**
* This interface will be passed as an argument for <code>ls</code> 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 <code>select</code> method will be invoked in <code>ls</code> method for each file entry.
* If this method returns BREAK, <code>ls</code> 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);
}
}