/**
* Copyright 2003-2016 SSHTOOLS Limited. All Rights Reserved.
*
* For product documentation visit https://www.sshtools.com/
*
* This file is part of J2SSH Maverick.
*
* J2SSH Maverick is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* J2SSH Maverick is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with J2SSH Maverick. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sshtools.sftp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;
import com.sshtools.events.Event;
import com.sshtools.events.EventServiceImplementation;
import com.sshtools.events.J2SSHEventCodes;
import com.sshtools.logging.Log;
import com.sshtools.ssh.ChannelOpenException;
import com.sshtools.ssh.Client;
import com.sshtools.ssh.SshClient;
import com.sshtools.ssh.SshException;
import com.sshtools.ssh.SshIOException;
import com.sshtools.ssh.SshSession;
import com.sshtools.ssh2.Ssh2Session;
import com.sshtools.util.EOLProcessor;
import com.sshtools.util.IOUtil;
import com.sshtools.util.UnsignedInteger32;
import com.sshtools.util.UnsignedInteger64;
/**
* <p>
* Implements a Secure File Transfer (SFTP) client.
* </p>
*
* @author Lee David Painter
*/
public class SftpClient implements Client {
SftpSubsystemChannel sftp;
String cwd;
String lcwd;
private int blocksize = 4096;
private int asyncRequests = 100;
private int buffersize = -1;
// Default permissions is determined by default_permissions ^ umask
int umask = 0022;
/*
* public static final int TYPE_REGULAR = 1; public static final int
* TYPE_DIRECTORY = 2; public static final int TYPE_SYMLINK = 3; public
* static final int TYPE_SPECIAL = 4; public static final int TYPE_UNKNOWN =
* 5;
*/
/**
* Instructs the client to use a binary transfer mode when used with {@link
* setTransferMode(int)}
*/
public static final int MODE_BINARY = 1;
/**
* Instructs the client to use a text transfer mode when used with {@link
* setTransferMode(int)}.
*/
public static final int MODE_TEXT = 2;
/**
* <p>
* Specifies that the remote server is using \r\n as its newline convention
* when used with {@link setRemoteEOL(int)}
* </p>
*/
public static final int EOL_CRLF = EOLProcessor.TEXT_CRLF;
/**
* <p>
* Specifies that the remote server is using \n as its newline convention
* when used with {@link setRemoteEOL(int)}
* </p>
*/
public static final int EOL_LF = EOLProcessor.TEXT_LF;
/**
* <p>
* Specifies that the remote server is using \r as its newline convention
* when used with {@link setRemoteEOL(int)}
* </p>
*/
public static final int EOL_CR = EOLProcessor.TEXT_CR;
private int eolMode = EOL_CRLF;
private int transferMode = MODE_BINARY;
public SftpClient(SshClient ssh) throws SftpStatusException, SshException,
ChannelOpenException {
this(ssh, SftpSubsystemChannel.MAX_VERSION);
}
public SftpClient(SshSession session) throws SftpStatusException,
SshException {
this(session, SftpSubsystemChannel.MAX_VERSION);
}
public SftpClient(SshSession session, int Max_Version)
throws SftpStatusException, SshException {
initSftp(session, Max_Version);
}
/**
* <p>
* Constructs the SFTP client with a given channel event listener.
* </p>
*
* @param ssh
* the <code>SshClient</code> instance
* @param Max_Version
* the maximum SFTP protocol version to use
*
* @throws SftpStatusException
* @throws SshException
* @throws ChannelOpenException
*/
public SftpClient(SshClient ssh, int Max_Version)
throws SftpStatusException, SshException, ChannelOpenException {
SshSession session = ssh.openSessionChannel();
/**
* Start the SFTP server
*/
Ssh2Session ssh2 = (Ssh2Session) session;
if (!ssh2.startSubsystem("sftp")) {
if (Log.isDebugEnabled()) {
Log.debug(this,
"The SFTP subsystem failed to start, attempting to execute provider "
+ ssh.getContext().getSFTPProvider());
}
// We could not start the subsystem try to fallback to the
// provider specified by the user
if (!ssh2.executeCommand(ssh.getContext().getSFTPProvider())) {
ssh2.close();
throw new SshException(
"Failed to start SFTP subsystem or SFTP provider "
+ ssh.getContext().getSFTPProvider(),
SshException.CHANNEL_FAILURE);
}
}
initSftp(session, Max_Version);
}
private void initSftp(SshSession session, int Max_Version)
throws SftpStatusException, SshException {
sftp = new SftpSubsystemChannel(session, Max_Version);
try {
sftp.initialize();
} catch (UnsupportedEncodingException ex) {
}
// Get the users default directory
cwd = sftp.getDefaultDirectory();
String homeDir = "";
try {
homeDir = System.getProperty("user.home");
} catch (SecurityException e) {
// ignore
}
lcwd = homeDir;
EventServiceImplementation.getInstance().fireEvent(
new Event(this, J2SSHEventCodes.EVENT_SFTP_SESSION_STARTED,
true));
}
/**
* Sets the block size used when transferring files, defaults to the
* optimized setting of 32768. You should not increase this value as the
* remote server may not be able to support higher blocksizes.
*
* @param blocksize
*/
public void setBlockSize(int blocksize) {
if (blocksize < 512) {
throw new IllegalArgumentException(
"Block size must be greater than 512");
}
this.blocksize = blocksize;
}
/**
* Returns the instance of the SftpSubsystemChannel used by this class
*
* @return the SftpSubsystemChannel instance
*/
public SftpSubsystemChannel getSubsystemChannel() {
return sftp;
}
/**
* <p>
* Sets the transfer mode for current operations. The valid modes are:<br>
* <br>
* {@link #MODE_BINARY} - Files are transferred in binary mode and no
* processing of text files is performed (default mode).<br>
* <br>
* {@link #MODE_TEXT} - For servers supporting version 4+ of the SFTP
* protocol files are transferred in text mode. For earlier protocol
* versions the files are transfered in binary mode but the client performs
* processing of text; if files are written to the remote server the client
* ensures that the line endings conform to the remote EOL mode set using
* {@link setRemoteEOL(int)}. For files retrieved from the server the EOL
* policy is based upon System policy as defined by the "line.seperator"
* system property.
* </p>
*
* @param transferMode
* int
*/
public void setTransferMode(int transferMode) {
if (transferMode != MODE_BINARY && transferMode != MODE_TEXT)
throw new IllegalArgumentException(
"Mode can only be either binary or text");
this.transferMode = transferMode;
if (Log.isDebugEnabled()) {
Log.debug(this, "Transfer mode set to "
+ (transferMode == MODE_BINARY ? "binary" : "text"));
}
}
/**
* <p>
* When connected to servers running SFTP version 3 (or less) the remote EOL
* type needs to be explicitly set because there is no reliable way for the
* client to determine the type of EOL for text files. In versions 4+ a
* mechanism is provided and this setting is overridden.
* </p>
*
* <p>
* Valid values for this method are {@link EOL_CRLF} (default),
* {@link EOL_CR}, and {@link EOL_LF}.
* </p>
*
* @param eolMode
* int
*/
public void setRemoteEOL(int eolMode) {
this.eolMode = eolMode;
if (Log.isDebugEnabled()) {
Log.debug(this, "Remote EOL set to "
+ (eolMode == EOL_CRLF ? "CRLF" : (eolMode == EOL_CR ? "CR"
: "LF")));
}
}
/**
*
* @return int
*/
public int getTransferMode() {
return transferMode;
}
/**
* Set the size of the buffer which is used to read from the local file
* system. This setting is used to optimize the writing of files by allowing
* for a large chunk of data to be read in one operation from a local file.
* The previous version simply read each block of data before sending
* however this decreased performance, this version now reads the file into
* a temporary buffer in order to reduce the number of local filesystem
* reads. This increases performance and so this setting should be set to
* the highest value possible. The default setting is negative which means
* the entire file will be read into a temporary buffer.
*
* @param buffersize
*/
public void setBufferSize(int buffersize) {
this.buffersize = buffersize;
if (Log.isDebugEnabled()) {
Log.debug(this, "Buffer size set to " + buffersize);
}
}
/**
* Set the maximum number of asynchronous requests that are outstanding at
* any one time. This setting is used to optimize the reading and writing of
* files to/from the remote file system when using the get and put methods.
* The default for this setting is 100.
*
* @param asyncRequests
*/
public void setMaxAsyncRequests(int asyncRequests) {
if (asyncRequests < 1) {
throw new IllegalArgumentException(
"Maximum asynchronous requests must be greater or equal to 1");
}
this.asyncRequests = asyncRequests;
if (Log.isDebugEnabled()) {
Log.debug(this, "Max async requests set to "
+ asyncRequests);
}
}
/**
* Sets the umask used by this client. <blockquote>
*
* <pre>
* To give yourself full permissions for both files and directories and
* prevent the group and other users from having access:
*
* umask(077);
*
* This subtracts 077 from the system defaults for files and directories
* 666 and 777. Giving a default access permissions for your files of
* 600 (rw-------) and for directories of 700 (rwx------).
*
* To give all access permissions to the group and allow other users read
* and execute permission:
*
* umask(002);
*
* This subtracts 002 from the system defaults to give a default access permission
* for your files of 664 (rw-rw-r--) and for your directories of 775 (rwxrwxr-x).
*
* To give the group and other users all access except write access:
*
* umask(022);
*
* This subtracts 022 from the system defaults to give a default access permission
* for your files of 644 (rw-r--r--) and for your directories of 755 (rwxr-xr-x).
* </pre>
*
* </blockquote>
*
* @param umask
* @return the previous umask value
*/
public int umask(int umask) {
int old = this.umask;
this.umask = umask;
if (Log.isDebugEnabled()) {
Log.debug(this, "umask " + umask);
}
return old;
}
public SftpFile openFile(String fileName) throws SftpStatusException,
SshException {
if (transferMode == MODE_TEXT && sftp.getVersion() > 3) {
return sftp.openFile(resolveRemotePath(fileName),
SftpSubsystemChannel.OPEN_READ
| SftpSubsystemChannel.OPEN_TEXT);
}
return sftp.openFile(resolveRemotePath(fileName),
SftpSubsystemChannel.OPEN_READ);
}
/**
* <p>
* Changes the working directory on the remote server, or the user's default
* directory if <code>null</code> or any empty string is provided as the
* directory path. The user's default directory is typically their home
* directory but is dependent upon server implementation.
* </p>
*
* @param dir
* the new working directory
*
* @throws IOException
* if an IO error occurs or the file does not exist
* @throws SftpStatusException
* @throws SshException
*/
public void cd(String dir) throws SftpStatusException, SshException {
String actual;
if (dir == null || dir.equals("")) {
actual = sftp.getDefaultDirectory();
} else {
actual = resolveRemotePath(dir);
actual = sftp.getAbsolutePath(actual);
}
if (!actual.equals("")) {
SftpFileAttributes attr = sftp.getAttributes(actual);
if (!attr.isDirectory()) {
throw new SftpStatusException(
SftpStatusException.SSH_FX_FAILURE, dir
+ " is not a directory");
}
}
if (Log.isDebugEnabled()) {
Log.debug(this, "Changing dir from " + cwd + " to "
+ (actual.equals("") ? "user default dir" : actual));
}
cwd = actual;
}
// protected void finalize() throws Throwable {
//
// if(sftp!=null)
// sftp.close();
// sftp = null;
// super.finalize();
//
// }
/**
* <p>
* Get the default directory (or HOME directory)
* </p>
*
* @return String
*
* @throws SftpStatusException
* @throws SshException
*/
public String getDefaultDirectory() throws SftpStatusException,
SshException {
return sftp.getDefaultDirectory();
}
/**
* Change the working directory to the parent directory
*
* @throws SftpStatusException
* @throws SshException
*/
public void cdup() throws SftpStatusException, SshException {
SftpFile cd = sftp.getFile(cwd);
SftpFile parent = cd.getParent();
if (parent != null)
cwd = parent.getAbsolutePath();
}
private File resolveLocalPath(String path) {
File f = new File(path);
if (!f.isAbsolute()) {
f = new File(lcwd, path);
}
return f;
}
private boolean isWindowsRoot(String path) {
path = path.trim();
// true if path>2 and starts with a letter followed by a ':' followed by
// '/' or '\\'
return path.length() > 2
&& (((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') || (path
.charAt(0) >= 'A' && path.charAt(0) <= 'Z'))
&& path.charAt(1) == ':' && path.charAt(2) == '/' || path
.charAt(2) == '\\');
}
/**
* some devices have unusual file system roots such as "flash:", customRoots
* contains these. If a device uses roots like this, and folder traversal on
* the device is required then it must have its root stored in customRoots
*/
private Vector<String> customRoots = new Vector<String>();
/**
* Add a custom file system root path such as "flash:"
*
* @param rootPath
*/
public void addCustomRoot(String rootPath) {
customRoots.addElement(rootPath);
}
/**
* Remove a custom file system root path such as "flash:"
*
* @param rootPath
*/
public void removeCustomRoot(String rootPath) {
customRoots.removeElement(rootPath);
}
/**
* Tests whether path starts with a custom file system root.
*
* @param path
* @return <em>true</em> if path starts with an element of customRoots,
* <em>false</em> otherwise
*/
private boolean startsWithCustomRoot(String path) {
for (Enumeration<String> it = customRoots.elements(); it != null
&& it.hasMoreElements();) {
if (path.startsWith(it.nextElement())) {
return true;
}
}
return false;
}
/**
* returns the canonical form of path, if path doesn't start with one of
* '/';cwd;a customRoot; or is a WindowsRoot then prepend cwd to path
*
* @param path
* @return canonical form of path
* @throws SftpStatusException
*/
private String resolveRemotePath(String path) throws SftpStatusException {
verifyConnection();
String actual;
if (!path.startsWith("/") && !path.startsWith(cwd)
&& !isWindowsRoot(path) && !startsWithCustomRoot(path)) {
actual = cwd + (cwd.endsWith("/") ? "" : "/") + path;
} else {
actual = path;
}
if (!actual.equals("/") && actual.endsWith("/")) {
return actual.substring(0, actual.length() - 1);
} else {
return actual;
}
}
private void verifyConnection() throws SftpStatusException {
if (sftp.isClosed()) {
throw new SftpStatusException(
SftpStatusException.SSH_FX_CONNECTION_LOST,
"The SFTP connection has been closed");
}
}
/**
* <p>
* Creates a new directory on the remote server. This method will throw an
* exception if the directory already exists. To create directories and
* disregard any errors use the <code>mkdirs</code> method.
* </p>
*
* @param dir
* the name of the new directory
*
* @throws SftpStatusException
* @throws SshException
*/
public void mkdir(String dir) throws SftpStatusException, SshException {
String actual = resolveRemotePath(dir);
if (Log.isDebugEnabled()) {
Log.debug(this, "Creating dir " + dir);
}
try {
sftp.getAttributes(actual);
} catch (SftpStatusException ex) {
// only create the directory if catch an exception with code file
// not found
SftpFileAttributes newattrs = new SftpFileAttributes(sftp,
SftpFileAttributes.SSH_FILEXFER_TYPE_DIRECTORY);
newattrs.setPermissions(new UnsignedInteger32(0777 ^ umask));
sftp.makeDirectory(actual, newattrs);
return;
}
if (Log.isDebugEnabled()) {
Log.debug(this, "A file/folder with name " + dir
+ " already exists!");
}
throw new SftpStatusException(SftpStatusException.SSH_FX_FAILURE,
"File already exists named " + dir);
}
/**
* <p>
* Create a directory or set of directories. This method will not fail even
* if the directories exist. It is advisable to test whether the directory
* exists before attempting an operation by using <a
* href="#stat(java.lang.String)">stat</a> to return the directories
* attributes.
* </p>
*
* @param dir
* the path of directories to create.
*/
public void mkdirs(String dir) throws SftpStatusException, SshException {
StringTokenizer tokens = new StringTokenizer(dir, "/");
String path = dir.startsWith("/") ? "/" : "";
while (tokens.hasMoreElements()) {
path += (String) tokens.nextElement();
try {
stat(path);
} catch (SftpStatusException ex) {
try {
mkdir(path);
} catch (SftpStatusException ex2) {
if (ex2.getStatus() == SftpStatusException.SSH_FX_PERMISSION_DENIED)
throw ex2;
}
}
path += "/";
}
}
/**
* Determine whether the file object is pointing to a symbolic link that is
* pointing to a directory.
*
* @return boolean
*/
public boolean isDirectoryOrLinkedDirectory(SftpFile file)
throws SftpStatusException, SshException {
return file.isDirectory()
|| (file.isLink() && stat(file.getAbsolutePath()).isDirectory());
}
/**
* <p>
* Returns the absolute path name of the current remote working directory.
* </p>
*
* @return the absolute path of the remote working directory.
*/
public String pwd() {
return cwd;
}
/**
* <p>
* List the contents of the current remote working directory.
* </p>
*
* <p>
* Returns a list of <a
* href="../../maverick/ssh2/SftpFile.html">SftpFile</a> instances for the
* current working directory.
* </p>
*
* @return a list of SftpFile for the current working directory
*
* @throws SftpStatusException
* @throws SshException
*
*/
public SftpFile[] ls() throws SftpStatusException, SshException {
return ls(cwd);
}
/**
* <p>
* List the contents remote directory.
* </p>
*
* <p>
* Returns a list of <a
* href="../../maverick/ssh2/SftpFile.html">SftpFile</a> instances for the
* remote directory.
* </p>
*
* @param path
* the path on the remote server to list
*
* @return a list of SftpFile for the remote directory
*
* @throws SftpStatusException
* @throws SshException
*/
public SftpFile[] ls(String path) throws SftpStatusException, SshException {
String actual = resolveRemotePath(path);
if (Log.isDebugEnabled()) {
Log.debug(this, "Listing files for " + actual);
}
SftpFile file = sftp.openDirectory(actual);
Vector<SftpFile> children = new Vector<SftpFile>();
while (sftp.listChildren(file, children) > -1) {
;
}
file.close();
SftpFile[] files = new SftpFile[children.size()];
int index = 0;
for (Enumeration<SftpFile> e = children.elements(); e.hasMoreElements();) {
files[index++] = e.nextElement();
}
return files;
}
/**
* <p>
* Changes the local working directory.
* </p>
*
* @param path
* the path to the new working directory
*
* @throws SftpStatusException
*/
public void lcd(String path) throws SftpStatusException {
File actual;
if (!isLocalAbsolutePath(path)) {
actual = new File(lcwd, path);
} else {
actual = new File(path);
}
if (!actual.isDirectory()) {
throw new SftpStatusException(SftpStatusException.SSH_FX_FAILURE,
path + " is not a directory");
}
try {
lcwd = actual.getCanonicalPath();
} catch (IOException ex) {
throw new SftpStatusException(SftpStatusException.SSH_FX_FAILURE,
"Failed to canonicalize path " + path);
}
}
private static boolean isLocalAbsolutePath(String path) {
return (new File(path)).isAbsolute();
}
/**
* <p>
* Returns the absolute path to the local working directory.
* </p>
*
* @return the absolute path of the local working directory.
*/
public String lpwd() {
return lcwd;
}
/**
* <p>
* Download the remote file to the local computer.
* </p>
*
* @param path
* the path to the remote file
* @param progress
*
* @return the downloaded file's attributes
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String path, FileTransferProgress progress)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
return get(path, progress, false);
}
/**
* <p>
* Download the remote file to the local computer.
* </p>
*
* @param path
* the path to the remote file
* @param progress
* @param resume
* attempt to resume a interrupted download
*
* @return the downloaded file's attributes
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String path, FileTransferProgress progress,
boolean resume) throws FileNotFoundException, SftpStatusException,
SshException, TransferCancelledException {
String localfile;
if (path.lastIndexOf("/") > -1) {
localfile = path.substring(path.lastIndexOf("/") + 1);
} else {
localfile = path;
}
return get(path, localfile, progress, resume);
}
/**
* <p>
* Download the remote file to the local computer
*
* @param path
* the path to the remote file
* @param resume
* attempt to resume an interrupted download
*
* @return the downloaded file's attributes
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String path, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
return get(path, (FileTransferProgress) null, resume);
}
/**
* <p>
* Download the remote file to the local computer
*
* @param path
* the path to the remote file
*
* @return the downloaded file's attributes
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String path) throws FileNotFoundException,
SftpStatusException, SshException, TransferCancelledException {
return get(path, (FileTransferProgress) null);
}
// private void transferFile(InputStream in, OutputStream out,
// FileTransferProgress progress) throws SftpStatusException,
// SshException, TransferCancelledException {
// try {
// long bytesSoFar = 0;
// byte[] buffer = new byte[blocksize];
// int read;
//
// while ((read = in.read(buffer)) > -1) {
// if ((progress != null) && progress.isCancelled()) {
// throw new TransferCancelledException();
// }
//
// if (read > 0) {
// out.write(buffer, 0, read);
// // out.flush();
// bytesSoFar += read;
//
// if (progress != null) {
// progress.progressed(bytesSoFar);
// }
// }
// }
// } catch (IOException ex) {
// throw new SftpStatusException(SftpStatusException.SSH_FX_FAILURE,
// "IO Error during data transfer: " + ex.getMessage());
// } finally {
// try {
// in.close();
// } catch (Throwable t) {
// }
//
// try {
// out.close();
// } catch (Throwable ex) {
// }
// }
// }
/**
* <p>
* Download the remote file to the local computer. If the paths provided are
* not absolute the current working directory is used.
* </p>
*
* @param remote
* the path/name of the remote file
* @param local
* the path/name to place the file on the local computer
* @param progress
*
* @return the downloaded file's attributes
*
* @throws SftpStatusException
* @throws FileNotFoundException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String remote, String local,
FileTransferProgress progress) throws FileNotFoundException,
SftpStatusException, SshException, TransferCancelledException {
return get(remote, local, progress, false);
}
/**
* <p>
* Download the remote file to the local computer. If the paths provided are
* not absolute the current working directory is used.
* </p>
*
* @param remote
* the path/name of the remote file
* @param local
* the path/name to place the file on the local computer
* @param progress
* @param resume
* attempt to resume an interrupted download
*
* @return the downloaded file's attributes
*
* @throws SftpStatusException
* @throws FileNotFoundException
* @throws SshException
* @throws TransferCancelledException
*/
@SuppressWarnings("resource")
public SftpFileAttributes get(String remote, String local,
FileTransferProgress progress, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
// Moved here to ensure that stream is closed in finally
OutputStream out = null;
SftpFileAttributes attrs = null;
// Perform local file operations first, then if it throws an exception
// the server hasn't been unnecessarily loaded.
File localPath = resolveLocalPath(local);
if (!localPath.exists()) {
File parent = new File(localPath.getParent());
parent.mkdirs();
}
if (localPath.isDirectory()) {
int idx;
if ((idx = remote.lastIndexOf('/')) > -1) {
localPath = new File(localPath, remote.substring(idx));
} else {
localPath = new File(localPath, remote);
}
}
// Check that file exists before we create a file
stat(remote);
long position = 0;
try {
// if resuming and the local file exists, then open as random access
// file and seek to end of the file ready to continue writing
if (resume && localPath.exists()) {
position = localPath.length();
RandomAccessFile file = new RandomAccessFile(localPath, "rw");
file.seek(position);
out = new RandomAccessFileOutputStream(file);
} else {
out = new FileOutputStream(localPath);
}
if (transferMode == MODE_TEXT) {
// Default text mode handling for versions 3- of the SFTP
// protocol
int inputStyle = eolMode;
int outputStyle = EOLProcessor.TEXT_SYSTEM;
byte[] nl = null;
if (sftp.getVersion() <= 3
&& sftp.getExtension("newline@vandyke.com") != null) {
nl = sftp.getExtension("newline@vandyke.com");
} else if (sftp.getVersion() > 3) {
nl = sftp.getCanonicalNewline();
}
// Setup text mode correctly if were using version 4+ of the
// SFTP protocol
if (nl != null) {
switch (nl.length) {
case 1:
if (nl[0] == '\r')
inputStyle = EOLProcessor.TEXT_CR;
else if (nl[0] == '\n')
inputStyle = EOLProcessor.TEXT_LF;
else
throw new SftpStatusException(
SftpStatusException.INVALID_HANDLE,
"Unsupported text mode: invalid newline character");
break;
case 2:
if (nl[0] == '\r' && nl[1] == '\n')
inputStyle = EOLProcessor.TEXT_CRLF;
else
throw new SftpStatusException(
SftpStatusException.INVALID_HANDLE,
"Unsupported text mode: invalid newline characters");
break;
default:
throw new SftpStatusException(
SftpStatusException.INVALID_HANDLE,
"Unsupported text mode: newline length > 2");
}
}
out = EOLProcessor.createOutputStream(inputStyle, outputStyle,
out);
}
attrs = get(remote, out, progress, position);
return attrs;
} catch (IOException ex) {
throw new SftpStatusException(SftpStatusException.SSH_FX_FAILURE,
"Failed to open outputstream to " + local);
} finally {
try {
if (out != null)
out.close();
// Try to set the last modified time on file using reflection so
// that
// the class is compatible with JDK 1.1
if (attrs != null) {
Method m = localPath.getClass().getMethod(
"setLastModified", new Class[] { long.class });
m.invoke(localPath, new Object[] { new Long(attrs
.getModifiedTime().longValue() * 1000) });
}
} catch (Throwable ex) {
// NOTE: should we ignore this?
}
}
}
/**
* Download the remote file into the local file.
*
* @param remote
* @param local
* @param resume
* attempt to resume an interrupted download
*
* @return the downloaded file's attributes
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String remote, String local, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
return get(remote, local, null, resume);
}
/**
* Download the remote file into the local file.
*
* @param remote
* @param local
*
* @return the downloaded file's attributes
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String remote, String local)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
return get(remote, local, false);
}
/**
* <p>
* Download the remote file writing it to the specified
* <code>OutputStream</code>. The OutputStream is closed by this method even
* if the operation fails.
* </p>
*
* @param remote
* the path/name of the remote file
* @param local
* the OutputStream to write
* @param progress
*
* @return the downloaded file's attributes
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String remote, OutputStream local,
FileTransferProgress progress) throws SftpStatusException,
SshException, TransferCancelledException {
return get(remote, local, progress, 0);
}
/**
* make local copies of some of the variables, then call getfilematches,
* which calls get on each file that matches the regexp remote.
*
* @param remote
* @param local
* @param progress
* @param position
* @return SftpFile[]
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
/*
* public SftpFile[] getFiles(String remote, OutputStream local,
* FileTransferProgress progress, long position) throws
* FileNotFoundException, SftpStatusException, SshException,
* TransferCancelledException { this.local = local; this.position =
* position; return getFileMatches(remote, progress, true); }
*/
/**
* make local copies of some of the variables, then call getfilematches,
* which calls get on each file that matches the regexp remote.
*
* @param remote
* @param local
* @param progress
* @param resume
* @return SftpFile[]
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
/*
* public SftpFile[] getFiles(String remote, String local,
* FileTransferProgress progress) throws FileNotFoundException,
* SftpStatusException, SshException, TransferCancelledException { return
* getFileMatches(remote, progress); }
*/
/**
* constants for setting the regular expression syntax.
*/
public static final int NoSyntax = 0;
public static final int GlobSyntax = 1;
public static final int Perl5Syntax = 2;
/**
* default regular expression syntax is to not perform regular expression
* matching on getFiles() and putFiles()
*/
private int RegExpSyntax = GlobSyntax;
/**
* sets the type of regular expression matching to perform on gets and puts
*
* @param syntax
* , NoSyntax for no regular expression matching, GlobSyntax for
* GlobSyntax, Perl5Syntax for Perl5Syntax
*/
public void setRegularExpressionSyntax(int syntax) {
RegExpSyntax = syntax;
}
/**
* Called by getFileMatches() to do regular expression pattern matching on
* the files in 'remote''s parent directory.
*
* @param remote
* @return SftpFile[]
* @throws SftpStatusException
* @throws SshException
*/
public SftpFile[] matchRemoteFiles(String remote)
throws SftpStatusException, SshException {
String actualDir;
String actualSearch;
int fileSeparatorIndex;
if ((fileSeparatorIndex = remote.lastIndexOf("/")) > -1) {
actualDir = remote.substring(0, fileSeparatorIndex);
actualSearch = remote.length() > fileSeparatorIndex + 1 ? remote
.substring(fileSeparatorIndex + 1) : "";
} else {
actualDir = cwd;
actualSearch = remote;
}
SftpFile[] files;
RegularExpressionMatching matcher;
switch (RegExpSyntax) {
case GlobSyntax:
matcher = new GlobRegExpMatching();
files = ls(actualDir);
break;
case Perl5Syntax:
matcher = new Perl5RegExpMatching();
files = ls(actualDir);
break;
default:
matcher = new NoRegExpMatching();
files = new SftpFile[1];
String actual = resolveRemotePath(remote);
files[0] = getSubsystemChannel().getFile(actual);
}
return matcher.matchFilesWithPattern(files, actualSearch);
}
/**
* If RegExpSyntax is set to GlobSyntax or Perl5Syntax then it pattern
* matches the files in the remote directory using "remote" as a glob or
* perl5 Regular Expression. For each matching file get() is called to copy
* the file to the local directory.
*
* <p>
* If RegExpSyntax is set to NoSyntax then "remote" is treated as a filepath
* instead of a regular expression
* </p>
*
* @param remote
* @param progress
* @param streamOrFile
* @return SftpFile[] of SftpFile's that have been retrieved
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
private SftpFile[] getFileMatches(String remote, String local,
FileTransferProgress progress, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
// match with files using remote as regular expression.
SftpFile[] matchedFiles = matchRemoteFiles(remote);
Vector<SftpFile> retrievedFiles = new Vector<SftpFile>();
// call get for each matched file, append the files attributes to a
// vector to be returned at the end
// call the correct get method depending on the get method that called
// this
for (int i = 0; i < matchedFiles.length; i++) {
get(matchedFiles[i].getAbsolutePath(), local, progress, resume);
retrievedFiles.addElement(matchedFiles[i]);
}
// return (SftpFile[]) retrievedFiles.toArray(new SftpFile[0]);
SftpFile[] retrievedSftpFiles = new SftpFile[retrievedFiles.size()];
retrievedFiles.copyInto(retrievedSftpFiles);
return retrievedSftpFiles;
}
/**
* Called by putFileMatches() to do regular expression pattern matching on
* the files in 'local''s parent directory.
*
* @param local
* @return String[]
* @throws SftpStatusException
* @throws SshException
*/
private String[] matchLocalFiles(String local) throws SftpStatusException,
SshException {
// Resolve the search path as it may not be CWD
String actualDir;
String actualSearch;
int fileSeparatorIndex;
if ((fileSeparatorIndex = local.lastIndexOf(System
.getProperty("file.separator"))) > -1
|| (fileSeparatorIndex = local.lastIndexOf('/')) > -1) {
actualDir = resolveLocalPath(local.substring(0, fileSeparatorIndex))
.getAbsolutePath();
actualSearch = (fileSeparatorIndex < local.length() - 1) ? local
.substring(fileSeparatorIndex + 1) : "";
} else {
actualDir = lcwd;
actualSearch = local;
}
File f;
RegularExpressionMatching matcher;
File[] files;
switch (RegExpSyntax) {
case GlobSyntax:
f = new File(actualDir);
matcher = new GlobRegExpMatching();
files = listFiles(f);
break;
case Perl5Syntax:
f = new File(actualDir);
matcher = new Perl5RegExpMatching();
files = listFiles(f);
break;
default:
matcher = new NoRegExpMatching();
files = new File[1];
files[0] = new File(local);
}
return matcher.matchFileNamesWithPattern(files, actualSearch);
}
private File[] listFiles(File f) {
String parentDir = f.getAbsolutePath();
String[] fileNames = f.list();
File[] files = new File[fileNames.length];
for (int i = 0; i < fileNames.length; i++) {
files[i] = new File(parentDir, fileNames[i]);
}
return files;
}
/**
* If RegExpSyntax is set to GlobSyntax or Perl5Syntax then it pattern
* matches the files in the local directory using "local" as a glob or perl5
* Regular Expression. For each matching file put() is called to copy the
* file to the remote directory.
*
* <p>
* If RegExpSyntax is set to NoSyntax then "local" is treated as a filepath
* instead of a regular expression.
* </p>
*
* @param local
* @param progress
* @param streamOrFile
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
private void putFileMatches(String local, String remote,
FileTransferProgress progress, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
String remotePath = resolveRemotePath(remote);
// Remote must be a valid remote directory
SftpFileAttributes attrs = null;
try {
attrs = stat(remotePath);
} catch (SftpStatusException ex) {
throw new SftpStatusException(
ex.getStatus(),
"Remote path '"
+ remote
+ "' does not exist. It must be a valid directory and must already exist!");
}
if (!attrs.isDirectory())
throw new SftpStatusException(
SftpStatusException.SSH_FX_NO_SUCH_PATH, "Remote path '"
+ remote + "' is not a directory!");
String[] matchedFiles = matchLocalFiles(local);
// call put for each matched file
// call the correct put method depending on the put method that called
// this
for (int i = 0; i < matchedFiles.length; i++) {
// use file exists once added rather than try catch
try {
put(matchedFiles[i], remotePath, progress, resume);
} catch (SftpStatusException ex) {
throw new SftpStatusException(ex.getStatus(), "Failed to put "
+ matchedFiles[i] + " to " + remote + " ["
+ ex.getMessage() + "]");
}
}
}
/**
* <p>
* Download the remote file writing it to the specified
* <code>OutputStream</code>. The OutputStream is closed by this method even
* if the operation fails.
* </p>
*
* @param remote
* the path/name of the remote file
* @param local
* the OutputStream to write
* @param progress
* @param position
* the position within the file to start reading from
*
* @return the downloaded file's attributes
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String remote, OutputStream local,
FileTransferProgress progress, long position)
throws SftpStatusException, SshException,
TransferCancelledException {
String remotePath = resolveRemotePath(remote);
SftpFileAttributes attrs = sftp.getAttributes(remotePath);
if (position > attrs.getSize().longValue()) {
throw new SftpStatusException(
SftpStatusException.INVALID_RESUME_STATE,
"The local file size is greater than the remote file");
}
if (progress != null) {
progress.started(attrs.getSize().longValue() - position, remotePath);
}
SftpFile file;
if (transferMode == MODE_TEXT && sftp.getVersion() > 3) {
file = sftp.openFile(remotePath, SftpSubsystemChannel.OPEN_READ
| SftpSubsystemChannel.OPEN_TEXT);
} else {
file = sftp.openFile(remotePath, SftpSubsystemChannel.OPEN_READ);
}
try {
sftp.performOptimizedRead(file.getHandle(), attrs.getSize()
.longValue(), blocksize, local, asyncRequests, progress,
position);
} catch (TransferCancelledException tce) {
throw tce;
} finally {
try {
local.close();
} catch (Throwable t) {
}
try {
sftp.closeFile(file);
} catch (SftpStatusException ex) {
}
}
if (progress != null) {
progress.completed();
}
return attrs;
}
/**
* Create an InputStream for reading a remote file.
*
* @param remotefile
* @param position
* @return InputStream
* @throws SftpStatusException
* @throws SshException
*/
public InputStream getInputStream(String remotefile, long position)
throws SftpStatusException, SshException {
String remotePath = resolveRemotePath(remotefile);
sftp.getAttributes(remotePath);
return new SftpFileInputStream(sftp.openFile(remotePath,
SftpSubsystemChannel.OPEN_READ), position);
}
/**
* Create an InputStream for reading a remote file.
*
* @param remotefile
* @return InputStream
* @throws SftpStatusException
* @throws SshException
*/
public InputStream getInputStream(String remotefile)
throws SftpStatusException, SshException {
return getInputStream(remotefile, 0);
}
/**
* Download the remote file into an OutputStream.
*
* @param remote
* @param local
* @param position
* the position from which to start reading the remote file
*
* @return the downloaded file's attributes
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String remote, OutputStream local,
long position) throws SftpStatusException, SshException,
TransferCancelledException {
return get(remote, local, null, position);
}
/**
* Download the remote file into an OutputStream.
*
* @param remote
* @param local
*
* @return the downloaded file's attributes
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFileAttributes get(String remote, OutputStream local)
throws SftpStatusException, SshException,
TransferCancelledException {
return get(remote, local, null, 0);
}
/**
* <p>
* Returns the state of the SFTP client. The client is closed if the
* underlying session channel is closed. Invoking the <code>quit</code>
* method of this object will close the underlying session channel.
* </p>
*
* @return true if the client is still connected, otherwise false
*/
public boolean isClosed() {
return sftp.isClosed();
}
/**
* <p>
* Upload a file to the remote computer.
* </p>
*
* @param local
* the path/name of the local file
* @param progress
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void put(String local, FileTransferProgress progress, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
File f = new File(local);
put(local, f.getName(), progress, resume);
}
/**
* <p>
* Upload a file to the remote computer.
* </p>
*
* @param local
* the path/name of the local file
* @param progress
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void put(String local, FileTransferProgress progress)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
put(local, progress, false);
}
/**
* Upload a file to the remote computer
*
* @param local
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void put(String local) throws FileNotFoundException,
SftpStatusException, SshException, TransferCancelledException {
put(local, false);
}
/**
* Upload a file to the remote computer
*
* @param local
* @param resume
* attempt to resume after an interrupted transfer
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void put(String local, boolean resume) throws FileNotFoundException,
SftpStatusException, SshException, TransferCancelledException {
put(local, (FileTransferProgress) null, resume);
}
/**
* <p>
* Upload a file to the remote computer. If the paths provided are not
* absolute the current working directory is used.
* </p>
*
* @param local
* the path/name of the local file
* @param remote
* the path/name of the destination file
* @param progress
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void put(String local, String remote, FileTransferProgress progress)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
put(local, remote, progress, false);
}
/**
* <p>
* Upload a file to the remote computer. If the paths provided are not
* absolute the current working directory is used.
* </p>
*
* @param local
* the path/name of the local file
* @param remote
* the path/name of the destination file
* @param progress
* @param resume
* attempt to resume after an interrupted transfer
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void put(String local, String remote, FileTransferProgress progress,
boolean resume) throws FileNotFoundException, SftpStatusException,
SshException, TransferCancelledException {
File localPath = resolveLocalPath(local);
InputStream in = new FileInputStream(localPath);
// File f = new File(local);
long position = 0;
try {
SftpFileAttributes attrs = stat(remote);
if (attrs.isDirectory()) {
remote += (remote.endsWith("/") ? "" : "/")
+ localPath.getName();
attrs = stat(remote);
}
if (resume) {
if (localPath.length() <= attrs.getSize().longValue()) {
try {
in.close();
} catch (IOException e) {
}
throw new SftpStatusException(
SftpStatusException.INVALID_RESUME_STATE,
"The remote file size is greater than the local file");
}
try {
position = attrs.getSize().longValue();
in.skip(position);
} catch (IOException ex) {
try {
in.close();
} catch (IOException e) {
}
throw new SftpStatusException(
SftpStatusException.SSH_FX_NO_SUCH_FILE,
ex.getMessage());
}
}
} catch (SftpStatusException ex) {
// file didnt exist so dont need to do above
}
put(in, remote, progress, position);
}
/**
* Upload a file to the remote computer
*
* @param local
* @param remote
* @param resume
* attempt to resume after an interrupted transfer
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void put(String local, String remote, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
put(local, remote, null, resume);
}
/**
* Upload a file to the remote computer
*
* @param local
* @param remote
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void put(String local, String remote) throws FileNotFoundException,
SftpStatusException, SshException, TransferCancelledException {
put(local, remote, null, false);
}
/**
* <p>
* Upload a file to the remote computer reading from the specified <code>
* InputStream</code>. The InputStream is closed, even if the operation
* fails.
* </p>
*
* @param in
* the InputStream being read
* @param remote
* the path/name of the destination file
* @param progress
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public void put(InputStream in, String remote, FileTransferProgress progress)
throws SftpStatusException, SshException,
TransferCancelledException {
put(in, remote, progress, 0);
}
public void put(InputStream in, String remote,
FileTransferProgress progress, long position)
throws SftpStatusException, SshException,
TransferCancelledException {
String remotePath = resolveRemotePath(remote);
SftpFileAttributes attrs = null;
SftpFile file;
if (transferMode == MODE_TEXT) {
// Default text mode handling for versions 3- of the SFTP protocol
int inputStyle = EOLProcessor.TEXT_SYSTEM;
int outputStyle = eolMode;
byte[] nl = null;
if (sftp.getVersion() <= 3
&& sftp.getExtension("newline@vandyke.com") != null) {
nl = sftp.getExtension("newline@vandyke.com");
} else if (sftp.getVersion() > 3) {
nl = sftp.getCanonicalNewline();
}
// Setup text mode correctly if were using version 4+ of the
// SFTP protocol
if (nl != null) {
switch (nl.length) {
case 1:
if (nl[0] == '\r')
outputStyle = EOLProcessor.TEXT_CR;
else if (nl[0] == '\n')
outputStyle = EOLProcessor.TEXT_LF;
else
throw new SftpStatusException(
SftpStatusException.INVALID_HANDLE,
"Unsupported text mode: invalid newline character");
break;
case 2:
if (nl[0] == '\r' && nl[1] == '\n')
outputStyle = EOLProcessor.TEXT_CRLF;
else
throw new SftpStatusException(
SftpStatusException.INVALID_HANDLE,
"Unsupported text mode: invalid newline characters");
break;
default:
throw new SftpStatusException(
SftpStatusException.INVALID_HANDLE,
"Unsupported text mode: newline length > 2");
}
}
try {
in = EOLProcessor
.createInputStream(inputStyle, outputStyle, in);
} catch (IOException ex) {
throw new SshException(
"Failed to create EOL processing stream",
SshException.INTERNAL_ERROR);
}
}
attrs = new SftpFileAttributes(sftp,
SftpFileAttributes.SSH_FILEXFER_TYPE_REGULAR);
attrs.setPermissions(new UnsignedInteger32(0666 ^ umask));
if (position > 0) {
if (transferMode == MODE_TEXT && sftp.getVersion() > 3) {
throw new SftpStatusException(
SftpStatusException.SSH_FX_OP_UNSUPPORTED,
"Resume on text mode files is not supported");
}
file = sftp.openFile(remotePath, SftpSubsystemChannel.OPEN_APPEND
| SftpSubsystemChannel.OPEN_WRITE, attrs);
} else {
if (transferMode == MODE_TEXT && sftp.getVersion() > 3) {
file = sftp.openFile(remotePath,
SftpSubsystemChannel.OPEN_CREATE
| SftpSubsystemChannel.OPEN_TRUNCATE
| SftpSubsystemChannel.OPEN_WRITE
| SftpSubsystemChannel.OPEN_TEXT, attrs);
} else {
file = sftp.openFile(remotePath,
SftpSubsystemChannel.OPEN_CREATE
| SftpSubsystemChannel.OPEN_TRUNCATE
| SftpSubsystemChannel.OPEN_WRITE, attrs);
}
}
if (progress != null) {
try {
progress.started(in.available(), remotePath);
} catch (IOException ex1) {
throw new SshException("Failed to determine local file size",
SshException.INTERNAL_ERROR);
}
}
try {
sftp.performOptimizedWrite(file.getHandle(), blocksize,
asyncRequests, in, buffersize, progress, position);
} finally {
try {
in.close();
} catch (Throwable t) {
}
sftp.closeFile(file);
}
if (progress != null) {
progress.completed();
}
}
/**
* Create an OutputStream for writing to a remote file.
*
* @param remotefile
* @return OutputStream
* @throws SftpStatusException
* @throws SshException
*/
public OutputStream getOutputStream(String remotefile)
throws SftpStatusException, SshException {
String remotePath = resolveRemotePath(remotefile);
return new SftpFileOutputStream(sftp.openFile(remotePath,
SftpSubsystemChannel.OPEN_CREATE
| SftpSubsystemChannel.OPEN_TRUNCATE
| SftpSubsystemChannel.OPEN_WRITE));
}
/**
* Upload the contents of an InputStream to the remote computer.
*
* @param in
* @param remote
* @param position
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public void put(InputStream in, String remote, long position)
throws SftpStatusException, SshException,
TransferCancelledException {
put(in, remote, null, position);
}
/**
* Upload the contents of an InputStream to the remote computer.
*
* @param in
* @param remote
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public void put(InputStream in, String remote) throws SftpStatusException,
SshException, TransferCancelledException {
put(in, remote, null, 0);
}
/**
* <p>
* Sets the user ID to owner for the file or directory.
* </p>
*
* @param uid
* numeric user id of the new owner
* @param path
* the path to the remote file/directory
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*
*/
public void chown(String uid, String path) throws SftpStatusException,
SshException {
String actual = resolveRemotePath(path);
SftpFileAttributes attrs = sftp.getAttributes(actual);
attrs.setUID(uid);
sftp.setAttributes(actual, attrs);
}
/**
* <p>
* Sets the group ID for the file or directory.
* </p>
*
* @param gid
* the numeric group id for the new group
* @param path
* the path to the remote file/directory
*
* @throws SftpStatusException
* @throws SshException
*/
public void chgrp(String gid, String path) throws SftpStatusException,
SshException {
String actual = resolveRemotePath(path);
SftpFileAttributes attrs = sftp.getAttributes(actual);
attrs.setGID(gid);
sftp.setAttributes(actual, attrs);
}
/**
* <p>
* Changes the access permissions or modes of the specified file or
* directory.
* </p>
*
* <p>
* Modes determine who can read, change or execute a file.
* </p>
* <blockquote>
*
* <pre>
* Absolute modes are octal numbers specifying the complete list of
* attributes for the files; you specify attributes by OR'ing together
* these bits.
*
* 0400 Individual read
* 0200 Individual write
* 0100 Individual execute (or list directory)
* 0040 Group read
* 0020 Group write
* 0010 Group execute
* 0004 Other read
* 0002 Other write
* 0001 Other execute
* </pre>
*
* </blockquote>
*
* @param permissions
* the absolute mode of the file/directory
* @param path
* the path to the file/directory on the remote server
*
* @throws SftpStatusException
* @throws SshException
*/
public void chmod(int permissions, String path) throws SftpStatusException,
SshException {
String actual = resolveRemotePath(path);
sftp.changePermissions(actual, permissions);
}
/**
* Sets the umask for this client.<br>
* <blockquote>
*
* <pre>
* To give yourself full permissions for both files and directories and
* prevent the group and other users from having access:
*
* umask("077");
*
* This subtracts 077 from the system defaults for files and directories
* 666 and 777. Giving a default access permissions for your files of
* 600 (rw-------) and for directories of 700 (rwx------).
*
* To give all access permissions to the group and allow other users read
* and execute permission:
*
* umask("002");
*
* This subtracts 002 from the system defaults to give a default access permission
* for your files of 664 (rw-rw-r--) and for your directories of 775 (rwxrwxr-x).
*
* To give the group and other users all access except write access:
*
* umask("022");
*
* This subtracts 022 from the system defaults to give a default access permission
* for your files of 644 (rw-r--r--) and for your directories of 755 (rwxr-xr-x).
* </pre>
*
* </blockquote>
*
* @param umask
* @throws SshException
*/
public void umask(String umask) throws SshException {
try {
this.umask = Integer.parseInt(umask, 8);
} catch (NumberFormatException ex) {
throw new SshException(
"umask must be 4 digit octal number e.g. 0022",
SshException.BAD_API_USAGE);
}
}
/**
* <p>
* Rename a file on the remote computer.
* </p>
*
* @param oldpath
* the old path
* @param newpath
* the new path
*
* @throws SftpStatusException
* @throws SshException
*/
public void rename(String oldpath, String newpath)
throws SftpStatusException, SshException {
String from = resolveRemotePath(oldpath);
String to = resolveRemotePath(newpath);
SftpFileAttributes attrs = null;
try {
attrs = sftp.getAttributes(to);
} catch (SftpStatusException ex) {
sftp.renameFile(from, to);
return;
}
if (attrs != null && attrs.isDirectory()) {
sftp.renameFile(from, to);
} else {
throw new SftpStatusException(
SftpStatusException.SSH_FX_FILE_ALREADY_EXISTS, newpath
+ " already exists on the remote filesystem");
}
}
/**
* <p>
* Remove a file or directory from the remote computer.
* </p>
*
* @param path
* the path of the remote file/directory
*
* @throws SftpStatusException
* @throws SshException
*/
public void rm(String path) throws SftpStatusException, SshException {
String actual = resolveRemotePath(path);
SftpFileAttributes attrs = sftp.getAttributes(actual);
if (attrs.isDirectory()) {
sftp.removeDirectory(actual);
} else {
sftp.removeFile(actual);
}
}
/**
* Remove a file or directory on the remote computer with options to force
* deletion of existing files and recursion.
*
* @param path
* @param force
* @param recurse
*
* @throws SftpStatusException
* @throws SshException
*/
public void rm(String path, boolean force, boolean recurse)
throws SftpStatusException, SshException {
String actual = resolveRemotePath(path);
SftpFileAttributes attrs = null;
attrs = sftp.getAttributes(actual);
SftpFile file;
if (attrs.isDirectory()) {
SftpFile[] list = ls(path);
if (!force && (list.length > 0)) {
throw new SftpStatusException(
SftpStatusException.SSH_FX_FAILURE,
"You cannot delete non-empty directory, use force=true to overide");
}
for (int i = 0; i < list.length; i++) {
file = list[i];
if (file.isDirectory() && !file.getFilename().equals(".")
&& !file.getFilename().equals("..")) {
if (recurse) {
rm(file.getAbsolutePath(), force, recurse);
} else {
throw new SftpStatusException(
SftpStatusException.SSH_FX_FAILURE,
"Directory has contents, cannot delete without recurse=true");
}
} else if (file.isFile()) {
sftp.removeFile(file.getAbsolutePath());
}
}
sftp.removeDirectory(actual);
} else {
sftp.removeFile(actual);
}
}
/**
* <p>
* Create a symbolic link on the remote computer.
* </p>
*
* @param path
* the path to the existing file
* @param link
* the new link
*
* @throws SftpStatusException
* @throws SshException
*/
public void symlink(String path, String link) throws SftpStatusException,
SshException {
String actualPath = resolveRemotePath(path);
String actualLink = resolveRemotePath(link);
sftp.createSymbolicLink(actualLink, actualPath);
}
/**
* <p>
* Returns the attributes of the file from the remote computer.
* </p>
*
* @param path
* the path of the file on the remote computer
*
* @return the attributes
*
* @throws SftpStatusException
* @throws SshException
*/
public SftpFileAttributes stat(String path) throws SftpStatusException,
SshException {
String actual = resolveRemotePath(path);
return sftp.getAttributes(actual);
}
/**
* <p>
* Returns the attributes of a link on the remote computer.
* </p>
*
* @param path
* the path of the file on the remote computer
*
* @return the attributes
*
* @throws SftpStatusException
* @throws SshException
*/
public SftpFileAttributes statLink(String path) throws SftpStatusException,
SshException {
String actual = resolveRemotePath(path);
return sftp.getLinkAttributes(actual);
}
/**
* Get the absolute path for a file.
*
* @param path
*
* @return String
*
* @throws SftpStatusException
* @throws SshException
*/
public String getAbsolutePath(String path) throws SftpStatusException,
SshException {
String actual = resolveRemotePath(path);
return sftp.getAbsolutePath(actual);
}
/**
* <p>
* Close the SFTP client.
* </p>
*
*/
public void quit() throws SshException {
try {
sftp.close();
} catch (SshIOException ex) {
throw ex.getRealException();
} catch (IOException ex1) {
throw new SshException(ex1.getMessage(),
SshException.CHANNEL_FAILURE);
}
}
/**
* <p>
* Close the SFTP client.
* </p>
*
*/
public void exit() throws SshException {
try {
sftp.close();
} catch (SshIOException ex) {
throw ex.getRealException();
} catch (IOException ex1) {
throw new SshException(ex1.getMessage(),
SshException.CHANNEL_FAILURE);
}
}
/**
* Copy the contents of a local directory into a remote directory.
*
* @param localdir
* the path to the local directory
* @param remotedir
* the remote directory which will receive the contents
* @param recurse
* recurse through child folders
* @param sync
* synchronize the directories by removing files on the remote
* server that do not exist locally
* @param commit
* actually perform the operation. If <tt>false</tt> a <a
* href="DirectoryOperation.html">DirectoryOperation</a> will be
* returned so that the operation can be evaluated and no actual
* files will be created/transfered.
* @param progress
*
* @return DirectoryOperation
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public DirectoryOperation copyLocalDirectory(String localdir,
String remotedir, boolean recurse, boolean sync, boolean commit,
FileTransferProgress progress) throws FileNotFoundException,
SftpStatusException, SshException, TransferCancelledException {
DirectoryOperation op = new DirectoryOperation();
// Record the previous
// String pwd = pwd();
// String lpwd = lpwd();
File local = resolveLocalPath(localdir);
remotedir = resolveRemotePath(remotedir);
remotedir += (remotedir.endsWith("/") ? "" : "/");
// Setup the remote directory if were committing
if (commit) {
try {
sftp.getAttributes(remotedir);
} catch (SftpStatusException ex) {
mkdirs(remotedir);
}
}
// List the local files and verify against the remote server
String[] ls = local.list();
File source;
if (ls != null) {
for (int i = 0; i < ls.length; i++) {
source = new File(local, ls[i]);
if (source.isDirectory() && !source.getName().equals(".")
&& !source.getName().equals("..")) {
if (recurse) {
// File f = new File(local, source.getName());
op.addDirectoryOperation(
copyLocalDirectory(source.getAbsolutePath(),
remotedir + source.getName(), recurse,
sync, commit, progress), source);
}
} else if (source.isFile()) {
boolean newFile = false;
boolean unchangedFile = false;
try {
SftpFileAttributes attrs = sftp.getAttributes(remotedir
+ source.getName());
unchangedFile = ((source.length() == attrs.getSize()
.longValue()) && ((source.lastModified() / 1000) == attrs
.getModifiedTime().longValue()));
} catch (SftpStatusException ex) {
newFile = true;
}
try {
if (commit && !unchangedFile) { // BPS - Added
// !unChangedFile test.
// Why would want to
// copy that has been
// determined to be
// unchanged?
put(source.getAbsolutePath(),
remotedir + source.getName(), progress);
SftpFileAttributes attrs = sftp
.getAttributes(remotedir + source.getName());
attrs.setTimes(
new UnsignedInteger64(
source.lastModified() / 1000),
new UnsignedInteger64(
source.lastModified() / 1000));
sftp.setAttributes(remotedir + source.getName(),
attrs);
}
if (unchangedFile) {
op.addUnchangedFile(source);
} else if (!newFile) {
op.addUpdatedFile(source);
} else {
op.addNewFile(source);
}
} catch (SftpStatusException ex) {
op.addFailedTransfer(source, ex);
}
}
}
}
if (sync) {
// List the contents of the new remote directory and remove any
// files/directories that were not updated
try {
SftpFile[] files = ls(remotedir);
SftpFile file;
File f;
for (int i = 0; i < files.length; i++) {
file = files[i];
// Create a local file object to test for its existence
f = new File(local, file.getFilename());
if (!op.containsFile(f) && !file.getFilename().equals(".")
&& !file.getFilename().equals("..")) {
op.addDeletedFile(file);
if (commit) {
if (file.isDirectory()) {
// Recurse through the directory, deleting stuff
recurseMarkForDeletion(file, op);
if (commit) {
rm(file.getAbsolutePath(), true, true);
}
} else if (file.isFile()) {
rm(file.getAbsolutePath());
}
}
}
}
} catch (SftpStatusException ex2) {
// Ignore since if it does not exist we cant delete it
}
}
// Return the operation details
return op;
}
private void recurseMarkForDeletion(SftpFile file, DirectoryOperation op)
throws SftpStatusException, SshException {
SftpFile[] list = ls(file.getAbsolutePath());
op.addDeletedFile(file);
for (int i = 0; i < list.length; i++) {
file = list[i];
if (file.isDirectory() && !file.getFilename().equals(".")
&& !file.getFilename().equals("..")) {
recurseMarkForDeletion(file, op);
} else if (file.isFile()) {
op.addDeletedFile(file);
}
}
}
private void recurseMarkForDeletion(File file, DirectoryOperation op)
throws SftpStatusException, SshException {
String[] list = file.list();
op.addDeletedFile(file);
if (list != null) {
for (int i = 0; i < list.length; i++) {
file = new File(list[i]);
if (file.isDirectory() && !file.getName().equals(".")
&& !file.getName().equals("..")) {
recurseMarkForDeletion(file, op);
} else if (file.isFile()) {
op.addDeletedFile(file);
}
}
}
}
/**
* Format a String with the details of the file. <blockquote>
*
* <pre>
* -rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer
* </pre>
*
* </blockquote>
*
* @param file
* @throws SftpStatusException
* @throws SshException
* @return String
*/
public static String formatLongname(SftpFile file)
throws SftpStatusException, SshException {
return formatLongname(file.getAttributes(), file.getFilename());
}
/**
* Format a String with the details of the file. <blockquote>
*
* <pre>
* -rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer
* </pre>
*
* </blockquote>
*
* @param attrs
* @param filename
* @return String
*/
public static String formatLongname(SftpFileAttributes attrs,
String filename) {
StringBuffer str = new StringBuffer();
str.append(pad(10 - attrs.getPermissionsString().length())
+ attrs.getPermissionsString());
str.append(" 1 ");
str.append(attrs.getUID() + pad(8 - attrs.getUID().length())); // uid
str.append(" ");
str.append(attrs.getGID() + pad(8 - attrs.getGID().length())); // gid
str.append(" ");
str.append(pad(8 - attrs.getSize().toString().length())
+ attrs.getSize().toString());
str.append(" ");
str.append(pad(12 - getModTimeString(attrs.getModifiedTime()).length())
+ getModTimeString(attrs.getModifiedTime()));
str.append(" ");
str.append(filename);
return str.toString();
}
private static String getModTimeString(UnsignedInteger64 mtime) {
if (mtime == null) {
return "";
}
SimpleDateFormat df;
long mt = (mtime.longValue() * 1000L);
long now = System.currentTimeMillis();
if ((now - mt) > (6 * 30 * 24 * 60 * 60 * 1000L)) {
df = new SimpleDateFormat("MMM dd yyyy");
} else {
df = new SimpleDateFormat("MMM dd hh:mm");
}
return df.format(new Date(mt));
}
private static String pad(int num) {
StringBuffer strBuf = new StringBuffer("");
if (num > 0) {
for (int i = 0; i < num; i++) {
strBuf.append(" ");
}
}
return strBuf.toString();
}
/**
* Copy the contents of a remote directory to a local directory
*
* @param remotedir
* the remote directory whose contents will be copied.
* @param localdir
* the local directory to where the contents will be copied
* @param recurse
* recurse into child folders
* @param sync
* synchronized the directories by removing files and directories
* that do not exist on the remote server.
* @param commit
* actually perform the operation. If <tt>false</tt> the
* operation will be processed and a <a
* href="DirectoryOperation.html">DirectoryOperation</a> will be
* returned without actually transfering any files.
* @param progress
*
* @return DirectoryOperation
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public DirectoryOperation copyRemoteDirectory(String remotedir,
String localdir, boolean recurse, boolean sync, boolean commit,
FileTransferProgress progress) throws FileNotFoundException,
SftpStatusException, SshException, TransferCancelledException {
// Create an operation object to hold the information
DirectoryOperation op = new DirectoryOperation();
// Record the previous working directoies
String pwd = pwd();
// String lpwd = lpwd();
cd(remotedir);
// Setup the local cwd
String base = remotedir;
if (base.endsWith("/"))
base = base.substring(0, base.length() - 1);
int idx = base.lastIndexOf('/');
if (idx != -1) {
base = base.substring(idx + 1);
}
File local = new File(localdir);
if (!local.isAbsolute()) {
local = new File(lpwd(), localdir);
}
if (!local.exists() && commit) {
local.mkdir();
}
SftpFile[] files = ls();
SftpFile file;
File f;
for (int i = 0; i < files.length; i++) {
file = files[i];
if (file.isDirectory() && !file.getFilename().equals(".")
&& !file.getFilename().equals("..")) {
if (recurse) {
f = new File(local, file.getFilename());
op.addDirectoryOperation(
copyRemoteDirectory(
file.getFilename(),
local.getAbsolutePath() + "/"
+ file.getFilename(), recurse,
sync, commit, progress), f);
}
} else if (file.isFile()) {
f = new File(local, file.getFilename());
if (f.exists()
&& (f.length() == file.getAttributes().getSize()
.longValue())
&& ((f.lastModified() / 1000) == file.getAttributes()
.getModifiedTime().longValue())) {
if (commit) {
op.addUnchangedFile(f);
} else {
op.addUnchangedFile(file);
}
continue;
}
try {
if (f.exists()) {
if (commit) {
op.addUpdatedFile(f);
} else {
op.addUpdatedFile(file);
}
} else {
if (commit) {
op.addNewFile(f);
} else {
op.addNewFile(file);
}
}
if (commit) {
// Get the file
get(file.getFilename(), f.getAbsolutePath(), progress);
}
} catch (SftpStatusException ex) {
op.addFailedTransfer(f, ex);
}
}
}
if (sync) {
// List the contents of the new local directory and remove any
// files/directories that were not updated
String[] contents = local.list();
File f2;
if (contents != null) {
for (int i = 0; i < contents.length; i++) {
f2 = new File(local, contents[i]);
if (!op.containsFile(f2)) {
op.addDeletedFile(f2);
if (f2.isDirectory() && !f2.getName().equals(".")
&& !f2.getName().equals("..")) {
recurseMarkForDeletion(f2, op);
if (commit) {
IOUtil.recurseDeleteDirectory(f2);
}
} else if (commit) {
f2.delete();
}
}
}
}
}
cd(pwd);
return op;
}
/**
* <p>
* Download the remote files to the local computer
* </p>
*
* <p>
* When RegExpSyntax is set to NoSyntax the getFiles() methods act
* identically to the get() methods except for a different return type.
* </p>
*
* <p>
* When RegExpSyntax is set to GlobSyntax or Perl5Syntax, getFiles() treats
* 'remote' as a regular expression, and gets all the files in 'remote''s
* parent directory that match the pattern. The default parent directory of
* remote is the remote cwd unless 'remote' contains file seperators(/).
* </p>
*
* <p>
* Examples can be found in SftpConnect.java
*
* <p>
* Code Example: <blockquote>
*
* <pre>
* // change reg exp syntax from default SftpClient.NoSyntax (no reg exp matching)
* // to SftpClient.GlobSyntax
* sftp.setRegularExpressionSyntax(SftpClient.GlobSyntax);
* // get all .doc files with 'rfc' in their names, in the 'docs/unsorted/' folder
* // relative to the remote cwd, and copy them to the local cwd.
* sftp.getFiles("docs/unsorted/*rfc*.doc");
* </pre>
*
* </blockquote>
* </p>
*
* @param remote
* the regular expression path to the remote file
*
* @return the downloaded files' attributes
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFile[] getFiles(String remote) throws FileNotFoundException,
SftpStatusException, SshException, TransferCancelledException {
return getFiles(remote, (FileTransferProgress) null);
}
/**
* <p>
* Download the remote files to the local computer
*
* @param remote
* the regular expression path to the remote file
* @param resume
* attempt to resume an interrupted download
*
* @return the downloaded files' attributes
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFile[] getFiles(String remote, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
return getFiles(remote, (FileTransferProgress) null, resume);
}
/**
* <p>
* Download the remote files to the local computer.
* </p>
*
* @param remote
* the regular expression path to the remote file
* @param progress
*
* @return SftpFile[]
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFile[] getFiles(String remote, FileTransferProgress progress)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
return getFiles(remote, progress, false);
}
/**
* <p>
* Download the remote files to the local computer.
* </p>
*
* @param remote
* the regular expression path to the remote file
* @param progress
* @param resume
* attempt to resume a interrupted download
*
* @return SftpFile[]
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFile[] getFiles(String remote, FileTransferProgress progress,
boolean resume) throws FileNotFoundException, SftpStatusException,
SshException, TransferCancelledException {
return getFiles(remote, lcwd, progress, resume);
}
/**
* Download the remote files into an OutputStream.
*
* @param remote
* @param local
*
* @return SftpFile[]
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
/*
* public SftpFile[] getFiles(String remote, OutputStream local) throws
* FileNotFoundException, SftpStatusException, SshException,
* TransferCancelledException { return getFiles(remote, local, null, 0); }
*/
/**
* <p>
* Download the remote files writing it to the specified
* <code>OutputStream</code>. The OutputStream is closed by this mehtod even
* if the operation fails.
* </p>
*
* @param remote
* the regular expression path/name of the remote file
* @param local
* the OutputStream to write
* @param progress
*
* @return SftpFile[]
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
/*
* public SftpFile[] getFiles(String remote, OutputStream local,
* FileTransferProgress progress) throws FileNotFoundException,
* SftpStatusException, SshException, TransferCancelledException { return
* getFiles(remote, local, progress, 0); }
*/
/**
* Download the remote files into an OutputStream.
*
* @param remote
* @param local
* @param position
* the position from which to start reading the remote file
*
* @return SftpFile[]
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
/*
* public SftpFile[] getFiles(String remote, OutputStream local, long
* position) throws FileNotFoundException, SftpStatusException,
* SshException, TransferCancelledException { return getFiles(remote, local,
* null, position); }
*/
/**
* Download the remote files into the local file.
*
* @param remote
* @param local
*
* @return SftpFile[]
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFile[] getFiles(String remote, String local)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
return getFiles(remote, local, false);
}
/**
* Download the remote files into the local file.
*
* @param remote
* @param local
* @param resume
* attempt to resume an interrupted download
*
* @return SftpFile[]
*
* @throws FileNotFoundException
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFile[] getFiles(String remote, String local, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
return getFiles(remote, local, null, resume);
}
/**
* <p>
* Download the remote file to the local computer. If the paths provided are
* not absolute the current working directory is used.
* </p>
*
* @param remote
* the regular expression path/name of the remote files
* @param local
* the path/name to place the file on the local computer
* @param progress
*
* @return SftpFile[]
*
* @throws SftpStatusException
* @throws FileNotFoundException
* @throws SshException
* @throws TransferCancelledException
*/
public SftpFile[] getFiles(String remote, String local,
FileTransferProgress progress, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
return getFileMatches(remote, local, progress, resume);
}
/**
* <p>
* Upload the contents of an InputStream to the remote computer.
* </p>
*
* @param in
* @param remote
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
/*
* public void putFiles(InputStream in, String remote) throws
* FileNotFoundException, SftpStatusException, SshException,
* TransferCancelledException { putFiles(in, remote, null, 0); }
*/
/**
* <p>
* Upload files to the remote computer reading from the specified <code>
* InputStream</code>. The InputStream is closed, even if the operation
* fails.
* </p>
*
* @param in
* the InputStream being read
* @param remote
* the path/name of the destination file
* @param progress
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
/*
* public void putFiles(InputStream in, String remote, FileTransferProgress
* progress) throws FileNotFoundException, SftpStatusException,
* SshException, TransferCancelledException { putFiles(in, remote, progress,
* 0); }
*
* public void putFiles(InputStream in, String remote, FileTransferProgress
* progress, long position) throws FileNotFoundException,
* SftpStatusException, SshException, TransferCancelledException {
* this.localI = in; this.position = position; // putFileMatches(remote,
* progress, true); put(remote, progress, true);
*
* }
*/
/**
* Upload the contents of an InputStream to the remote computer.
*
* @param in
* @param remote
* @param position
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
*/
/*
* public void putFiles(InputStream in, String remote, long position) throws
* FileNotFoundException, SftpStatusException, SshException,
* TransferCancelledException { putFiles(in, remote, null, position); }
*/
/**
* <p>
* Upload the contents of an InputStream to the remote computer.
* </p>
*
* <p>
* When RegExpSyntax is set to NoSyntax the putFiles() methods act
* identically to the put() methods except for a different return type.
* </p>
*
* <p>
* When RegExpSyntax is set to GlobSyntax or Perl5Syntax, putFiles() treats
* 'local' as a regular expression, and gets all the files in 'local''s
* parent directory that match the pattern. The default parent directory of
* local is the local cwd unless 'local' contains file seperators.
* </p>
*
* <p>
* Examples can be found in SftpConnect.java
*
* <p>
* Code Example: <blockquote>
*
* <pre>
* // change reg exp syntax from default SftpClient.NoSyntax (no reg exp matching)
* // to SftpClient.GlobSyntax
* sftp.setRegularExpressionSyntax(SftpClient.GlobSyntax);
* // put all .doc files with 'rfc' in their names, in the 'docs/unsorted/' folder
* // relative to the local cwd, and copy them to the remote cwd.
* sftp.putFiles("docs/unsorted/*rfc*.doc");
* </pre>
*
* </blockquote>
* </p>
*
* @param local
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void putFiles(String local) throws FileNotFoundException,
SftpStatusException, SshException, TransferCancelledException {
putFiles(local, false);
}
/**
* Upload files to the remote computer
*
* @param local
* @param resume
* attempt to resume after an interrupted transfer
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void putFiles(String local, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
putFiles(local, (FileTransferProgress) null, resume);
}
/**
* <p>
* Upload files to the remote computer
* </p>
*
* @param local
* the regular expression path/name of the local files
* @param progress
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void putFiles(String local, FileTransferProgress progress)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
putFiles(local, progress, false);
}
/**
* <p>
* Upload files to the remote computer
* </p>
*
* @param local
* the regular expression path/name of the local files
* @param progress
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void putFiles(String local, FileTransferProgress progress,
boolean resume) throws FileNotFoundException, SftpStatusException,
SshException, TransferCancelledException {
putFiles(local, pwd(), progress, resume);
}
/**
* Upload files to the remote computer
*
* @param local
* @param remote
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void putFiles(String local, String remote)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
putFiles(local, remote, null, false);
}
/**
* Upload files to the remote computer
*
* @param local
* @param remote
* @param resume
* attempt to resume after an interrupted transfer
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void putFiles(String local, String remote, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
putFiles(local, remote, null, resume);
}
/**
* <p>
* Upload files to the remote computer. If the paths provided are not
* absolute the current working directory is used.
* </p>
*
* @param local
* the regular expression path/name of the local files
* @param remote
* the path/name of the destination file
* @param progress
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void putFiles(String local, String remote,
FileTransferProgress progress) throws FileNotFoundException,
SftpStatusException, SshException, TransferCancelledException {
putFiles(local, remote, progress, false);
}
/**
* make local copies of some of the variables, then call putfilematches,
* which calls "put" on each file that matches the regexp local.
*
* @param local
* the regular expression path/name of the local files
* @param remote
* the path/name of the destination file
* @param progress
* @param resume
* attempt to resume after an interrupted transfer
*
* @throws SftpStatusException
* @throws SshException
* @throws TransferCancelledException
* @throws FileNotFoundException
*/
public void putFiles(String local, String remote,
FileTransferProgress progress, boolean resume)
throws FileNotFoundException, SftpStatusException, SshException,
TransferCancelledException {
putFileMatches(local, remote, progress, resume);
}
/**
* A simple wrapper class to provide an OutputStream to a RandomAccessFile
*
* @author Lee David Painter
*/
static class RandomAccessFileOutputStream extends OutputStream {
RandomAccessFile file;
RandomAccessFileOutputStream(RandomAccessFile file) {
this.file = file;
}
public void write(int b) throws IOException {
file.write(b);
}
public void write(byte[] buf, int off, int len) throws IOException {
file.write(buf, off, len);
}
public void close() throws IOException {
file.close();
}
}
}