0001: /*
0002: * SSHTools - Java SSH2 API
0003: *
0004: * Copyright (C) 2002-2003 Lee David Painter and Contributors.
0005: *
0006: * Contributions made by:
0007: *
0008: * Brett Smith
0009: * Richard Pernavas
0010: * Erwin Bolwidt
0011: *
0012: * This program is free software; you can redistribute it and/or
0013: * modify it under the terms of the GNU General Public License
0014: * as published by the Free Software Foundation; either version 2
0015: * of the License, or (at your option) any later version.
0016: *
0017: * This program is distributed in the hope that it will be useful,
0018: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0020: * GNU General Public License for more details.
0021: *
0022: * You should have received a copy of the GNU General Public License
0023: * along with this program; if not, write to the Free Software
0024: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0025: */
0026: package com.sshtools.j2ssh;
0027:
0028: import com.sshtools.j2ssh.connection.*;
0029: import com.sshtools.j2ssh.io.*;
0030: import com.sshtools.j2ssh.sftp.*;
0031:
0032: import java.io.File;
0033: import java.io.FileInputStream;
0034: import java.io.FileNotFoundException;
0035: import java.io.FileOutputStream;
0036: import java.io.IOException;
0037: import java.io.InputStream;
0038: import java.io.OutputStream;
0039:
0040: import java.util.Iterator;
0041: import java.util.List;
0042: import java.util.StringTokenizer;
0043: import java.util.Vector;
0044:
0045: /**
0046: * <p>
0047: * Implements a Secure File Transfer (SFTP) client.
0048: * </p>
0049: *
0050: * @author Lee David Painter
0051: * @version $Revision: 1.44 $
0052: *
0053: * @since 0.2.0
0054: */
0055: public class SftpClient {
0056: SftpSubsystemClient sftp;
0057: String cwd;
0058: String lcwd;
0059: private int BLOCKSIZE = 65535;
0060:
0061: // Default permissions is determined by default_permissions ^ umask
0062: int umask = 0022;
0063: int default_permissions = 0777;
0064:
0065: /**
0066: * <p>
0067: * Constructs the SFTP client.
0068: * </p>
0069: *
0070: * @param ssh the <code>SshClient</code> instance
0071: *
0072: * @throws IOException if an IO error occurs
0073: */
0074: SftpClient(SshClient ssh) throws IOException {
0075: this (ssh, null);
0076: }
0077:
0078: /**
0079: * <p>
0080: * Constructs the SFTP client with a given channel event listener.
0081: * </p>
0082: *
0083: * @param ssh the <code>SshClient</code> instance
0084: * @param eventListener an event listener implementation
0085: *
0086: * @throws IOException if an IO error occurs
0087: */
0088: SftpClient(SshClient ssh, ChannelEventListener eventListener)
0089: throws IOException {
0090: if (!ssh.isConnected()) {
0091: throw new IOException("SshClient is not connected");
0092: }
0093:
0094: this .sftp = ssh.openSftpChannel(eventListener);
0095:
0096: // Get the users default directory
0097: cwd = sftp.getDefaultDirectory();
0098: lcwd = System.getProperty("user.home");
0099: }
0100:
0101: /**
0102: * Sets the umask used by this client.
0103: * @param umask
0104: * @return the previous umask value
0105: */
0106: public int umask(int umask) {
0107: int old = umask;
0108: this .umask = umask;
0109:
0110: return old;
0111: }
0112:
0113: /**
0114: * <p>
0115: * Changes the working directory on the remote server.
0116: * </p>
0117: *
0118: * @param dir the new working directory
0119: *
0120: * @throws IOException if an IO error occurs or the file does not exist
0121: * @throws FileNotFoundException
0122: *
0123: * @since 0.2.0
0124: */
0125: public void cd(String dir) throws IOException {
0126: try {
0127: String actual;
0128:
0129: if (dir.equals("")) {
0130: actual = sftp.getDefaultDirectory();
0131: } else {
0132: actual = resolveRemotePath(dir);
0133: actual = sftp.getAbsolutePath(actual);
0134: }
0135:
0136: FileAttributes attr = sftp.getAttributes(actual);
0137:
0138: if (!attr.isDirectory()) {
0139: throw new IOException(dir + " is not a directory");
0140: }
0141:
0142: cwd = actual;
0143: } catch (IOException ex) {
0144: throw new FileNotFoundException(dir + " could not be found");
0145: }
0146: }
0147:
0148: private File resolveLocalPath(String path) throws IOException {
0149: File f = new File(path);
0150:
0151: if (!f.isAbsolute()) {
0152: f = new File(lcwd, path);
0153: }
0154:
0155: return f;
0156: }
0157:
0158: private String resolveRemotePath(String path) throws IOException {
0159: verifyConnection();
0160:
0161: String actual;
0162:
0163: if (!path.startsWith("/")) {
0164: actual = cwd + (cwd.endsWith("/") ? "" : "/") + path;
0165: } else {
0166: actual = path;
0167: }
0168:
0169: return actual;
0170: }
0171:
0172: private void verifyConnection() throws SshException {
0173: if (sftp.isClosed()) {
0174: throw new SshException(
0175: "The SFTP connection has been closed");
0176: }
0177: }
0178:
0179: /**
0180: * <p>
0181: * Creates a new directory on the remote server. This method will throw an
0182: * exception if the directory already exists. To create directories and
0183: * disregard any errors use the <code>mkdirs</code> method.
0184: * </p>
0185: *
0186: * @param dir the name of the new directory
0187: *
0188: * @throws IOException if an IO error occurs or if the directory already
0189: * exists
0190: *
0191: * @since 0.2.0
0192: */
0193: public void mkdir(String dir) throws IOException {
0194: String actual = resolveRemotePath(dir);
0195:
0196: try {
0197: FileAttributes attrs = stat(actual);
0198:
0199: if (!attrs.isDirectory()) {
0200: throw new IOException("File already exists named "
0201: + dir);
0202: }
0203: } catch (IOException ex) {
0204: sftp.makeDirectory(actual);
0205: chmod(default_permissions ^ umask, actual);
0206: }
0207: }
0208:
0209: /**
0210: * <p>
0211: * Create a directory or set of directories. This method will not fail even
0212: * if the directories exist. It is advisable to test whether the directory
0213: * exists before attempting an operation by using the <code>stat</code>
0214: * method to return the directories attributes.
0215: * </p>
0216: *
0217: * @param dir the path of directories to create.
0218: */
0219: public void mkdirs(String dir) {
0220: StringTokenizer tokens = new StringTokenizer(dir, "/");
0221: String path = dir.startsWith("/") ? "/" : "";
0222:
0223: while (tokens.hasMoreElements()) {
0224: path += (String) tokens.nextElement();
0225:
0226: try {
0227: stat(path);
0228: } catch (IOException ex) {
0229: try {
0230: mkdir(path);
0231: } catch (IOException ex2) {
0232: }
0233: }
0234:
0235: path += "/";
0236: }
0237: }
0238:
0239: /**
0240: * <p>
0241: * Returns the absolute path name of the current remote working directory.
0242: * </p>
0243: *
0244: * @return the absolute path of the remote working directory.
0245: *
0246: * @since 0.2.0
0247: */
0248: public String pwd() {
0249: return cwd;
0250: }
0251:
0252: /**
0253: * <p>
0254: * List the contents of the current remote working directory.
0255: * </p>
0256: *
0257: * <p>
0258: * Returns a list of <code>SftpFile</code> instances for the current
0259: * working directory.
0260: * </p>
0261: *
0262: * @return a list of SftpFile for the current working directory
0263: *
0264: * @throws IOException if an IO error occurs
0265: *
0266: * @see com.sshtools.j2ssh.sftp.SftpFile
0267: * @since 0.2.0
0268: */
0269: public List ls() throws IOException {
0270: return ls(cwd);
0271: }
0272:
0273: /**
0274: * <p>
0275: * List the contents remote directory.
0276: * </p>
0277: *
0278: * <p>
0279: * Returns a list of <code>SftpFile</code> instances for the remote
0280: * directory.
0281: * </p>
0282: *
0283: * @param path the path on the remote server to list
0284: *
0285: * @return a list of SftpFile for the remote directory
0286: *
0287: * @throws IOException if an IO error occurs
0288: *
0289: * @see com.sshtools.j2ssh.sftp.SftpFile
0290: * @since 0.2.0
0291: */
0292: public List ls(String path) throws IOException {
0293: String actual = resolveRemotePath(path);
0294: FileAttributes attrs = sftp.getAttributes(actual);
0295:
0296: if (!attrs.isDirectory()) {
0297: throw new IOException(path + " is not a directory");
0298: }
0299:
0300: SftpFile file = sftp.openDirectory(actual);
0301: Vector children = new Vector();
0302:
0303: while (sftp.listChildren(file, children) > -1) {
0304: ;
0305: }
0306:
0307: file.close();
0308:
0309: return children;
0310: }
0311:
0312: /**
0313: * <p>
0314: * Changes the local working directory.
0315: * </p>
0316: *
0317: * @param path the path to the new working directory
0318: *
0319: * @throws IOException if an IO error occurs
0320: *
0321: * @since 0.2.0
0322: */
0323: public void lcd(String path) throws IOException {
0324: File actual;
0325:
0326: if (!isLocalAbsolutePath(path)) {
0327: actual = new File(lcwd, path);
0328: } else {
0329: actual = new File(path);
0330: }
0331:
0332: if (!actual.isDirectory()) {
0333: throw new IOException(path + " is not a directory");
0334: }
0335:
0336: lcwd = actual.getCanonicalPath();
0337: }
0338:
0339: private static boolean isLocalAbsolutePath(String path) {
0340: return (new File(path)).isAbsolute();
0341: }
0342:
0343: /**
0344: * <p>
0345: * Returns the absolute path to the local working directory.
0346: * </p>
0347: *
0348: * @return the absolute path of the local working directory.
0349: *
0350: * @since 0.2.0
0351: */
0352: public String lpwd() {
0353: return lcwd;
0354: }
0355:
0356: /**
0357: * <p>
0358: * Download the remote file to the local computer.
0359: * </p>
0360: *
0361: * @param path the path to the remote file
0362: * @param progress
0363: *
0364: * @return
0365: *
0366: * @throws IOException if an IO error occurs of the file does not exist
0367: * @throws TransferCancelledException
0368: *
0369: * @since 0.2.0
0370: */
0371: public FileAttributes get(String path, FileTransferProgress progress)
0372: throws IOException, TransferCancelledException {
0373: String localfile;
0374:
0375: if (path.lastIndexOf("/") > -1) {
0376: localfile = path.substring(path.lastIndexOf("/") + 1);
0377: } else {
0378: localfile = path;
0379: }
0380:
0381: return get(path, localfile, progress);
0382: }
0383:
0384: /**
0385: *
0386: *
0387: * @param path
0388: *
0389: * @return
0390: *
0391: * @throws IOException
0392: */
0393: public FileAttributes get(String path) throws IOException {
0394: return get(path, (FileTransferProgress) null);
0395: }
0396:
0397: private void transferFile(InputStream in, OutputStream out)
0398: throws IOException, TransferCancelledException {
0399: transferFile(in, out, null);
0400: }
0401:
0402: private void transferFile(InputStream in, OutputStream out,
0403: FileTransferProgress progress) throws IOException,
0404: TransferCancelledException {
0405: try {
0406: long bytesSoFar = 0;
0407: byte[] buffer = new byte[BLOCKSIZE];
0408: int read;
0409:
0410: while ((read = in.read(buffer)) > -1) {
0411: if ((progress != null) && progress.isCancelled()) {
0412: throw new TransferCancelledException();
0413: }
0414:
0415: if (read > 0) {
0416: out.write(buffer, 0, read);
0417:
0418: //out.flush();
0419: bytesSoFar += read;
0420:
0421: if (progress != null) {
0422: progress.progressed(bytesSoFar);
0423: }
0424: }
0425: }
0426: } finally {
0427: try {
0428: in.close();
0429: out.close();
0430: } catch (IOException ex) {
0431: }
0432: }
0433: }
0434:
0435: /**
0436: * <p>
0437: * Download the remote file to the local computer. If the paths provided
0438: * are not absolute the current working directory is used.
0439: * </p>
0440: *
0441: * @param remote the path/name of the remote file
0442: * @param local the path/name to place the file on the local computer
0443: * @param progress
0444: *
0445: * @return
0446: *
0447: * @throws IOException if an IO error occurs or the file does not exist
0448: * @throws TransferCancelledException
0449: *
0450: * @since 0.2.0
0451: */
0452: public FileAttributes get(String remote, String local,
0453: FileTransferProgress progress) throws IOException,
0454: TransferCancelledException {
0455: File localPath = resolveLocalPath(local);
0456:
0457: if (!localPath.exists()) {
0458: localPath.getParentFile().mkdirs();
0459: localPath.createNewFile();
0460: }
0461:
0462: FileOutputStream out = new FileOutputStream(localPath);
0463:
0464: return get(remote, out, progress);
0465: }
0466:
0467: /**
0468: *
0469: *
0470: * @param remote
0471: * @param local
0472: *
0473: * @return
0474: *
0475: * @throws IOException
0476: */
0477: public FileAttributes get(String remote, String local)
0478: throws IOException {
0479: return get(remote, local, null);
0480: }
0481:
0482: /**
0483: * <p>
0484: * Download the remote file writing it to the specified
0485: * <code>OutputStream</code>. The OutputStream is closed by this mehtod
0486: * even if the operation fails.
0487: * </p>
0488: *
0489: * @param remote the path/name of the remote file
0490: * @param local the OutputStream to write
0491: * @param progress
0492: *
0493: * @return
0494: *
0495: * @throws IOException if an IO error occurs or the file does not exist
0496: * @throws TransferCancelledException
0497: *
0498: * @since 0.2.0
0499: */
0500: public FileAttributes get(String remote, OutputStream local,
0501: FileTransferProgress progress) throws IOException,
0502: TransferCancelledException {
0503: String remotePath = resolveRemotePath(remote);
0504: FileAttributes attrs = stat(remotePath);
0505:
0506: if (progress != null) {
0507: progress.started(attrs.getSize().longValue(), remotePath);
0508: }
0509:
0510: SftpFileInputStream in = new SftpFileInputStream(sftp.openFile(
0511: remotePath, SftpSubsystemClient.OPEN_READ));
0512: transferFile(in, local, progress);
0513:
0514: if (progress != null) {
0515: progress.completed();
0516: }
0517:
0518: return attrs;
0519: }
0520:
0521: /**
0522: *
0523: *
0524: * @param remote
0525: * @param local
0526: *
0527: * @return
0528: *
0529: * @throws IOException
0530: */
0531: public FileAttributes get(String remote, OutputStream local)
0532: throws IOException {
0533: return get(remote, local, null);
0534: }
0535:
0536: /**
0537: * <p>
0538: * Returns the state of the SFTP client. The client is closed if the
0539: * underlying session channel is closed. Invoking the <code>quit</code>
0540: * method of this object will close the underlying session channel.
0541: * </p>
0542: *
0543: * @return true if the client is still connected, otherwise false
0544: *
0545: * @since 0.2.0
0546: */
0547: public boolean isClosed() {
0548: return sftp.isClosed();
0549: }
0550:
0551: /**
0552: * <p>
0553: * Upload a file to the remote computer.
0554: * </p>
0555: *
0556: * @param local the path/name of the local file
0557: * @param progress
0558: *
0559: * @return
0560: *
0561: * @throws IOException if an IO error occurs or the file does not exist
0562: * @throws TransferCancelledException
0563: *
0564: * @since 0.2.0
0565: */
0566: public void put(String local, FileTransferProgress progress)
0567: throws IOException, TransferCancelledException {
0568: File f = new File(local);
0569: put(local, f.getName(), progress);
0570: }
0571:
0572: /**
0573: *
0574: *
0575: * @param local
0576: *
0577: * @return
0578: *
0579: * @throws IOException
0580: */
0581: public void put(String local) throws IOException {
0582: put(local, (FileTransferProgress) null);
0583: }
0584:
0585: /**
0586: * <p>
0587: * Upload a file to the remote computer. If the paths provided are not
0588: * absolute the current working directory is used.
0589: * </p>
0590: *
0591: * @param local the path/name of the local file
0592: * @param remote the path/name of the destination file
0593: * @param progress
0594: *
0595: * @return
0596: *
0597: * @throws IOException if an IO error occurs or the file does not exist
0598: * @throws TransferCancelledException
0599: *
0600: * @since 0.2.0
0601: */
0602: public void put(String local, String remote,
0603: FileTransferProgress progress) throws IOException,
0604: TransferCancelledException {
0605: File localPath = resolveLocalPath(local);
0606: FileInputStream in = new FileInputStream(localPath);
0607:
0608: try {
0609: FileAttributes attrs = stat(remote);
0610:
0611: if (attrs.isDirectory()) {
0612: File f = new File(local);
0613: remote += ((remote.endsWith("/") ? "" : "/") + f
0614: .getName());
0615: }
0616: } catch (IOException ex) {
0617: }
0618:
0619: put(in, remote, progress);
0620: }
0621:
0622: /**
0623: *
0624: *
0625: * @param local
0626: * @param remote
0627: *
0628: * @return
0629: *
0630: * @throws IOException
0631: */
0632: public void put(String local, String remote) throws IOException {
0633: put(local, remote, null);
0634: }
0635:
0636: /**
0637: * <p>
0638: * Upload a file to the remote computer reading from the specified <code>
0639: * InputStream</code>. The InputStream is closed, even if the operation
0640: * fails.
0641: * </p>
0642: *
0643: * @param in the InputStream being read
0644: * @param remote the path/name of the destination file
0645: * @param progress
0646: *
0647: * @return
0648: *
0649: * @throws IOException if an IO error occurs
0650: * @throws TransferCancelledException
0651: *
0652: * @since 0.2.0
0653: */
0654: public void put(InputStream in, String remote,
0655: FileTransferProgress progress) throws IOException,
0656: TransferCancelledException {
0657: String remotePath = resolveRemotePath(remote);
0658: SftpFileOutputStream out;
0659: FileAttributes attrs;
0660: boolean newfile = false;
0661:
0662: try {
0663: attrs = stat(remotePath);
0664: out = new SftpFileOutputStream(sftp.openFile(remotePath,
0665: SftpSubsystemClient.OPEN_CREATE
0666: | SftpSubsystemClient.OPEN_TRUNCATE
0667: | SftpSubsystemClient.OPEN_WRITE));
0668: } catch (IOException ex) {
0669: attrs = new FileAttributes();
0670: newfile = true;
0671: attrs.setPermissions(new UnsignedInteger32(
0672: default_permissions ^ umask));
0673: out = new SftpFileOutputStream(sftp.openFile(remotePath,
0674: SftpSubsystemClient.OPEN_CREATE
0675: | SftpSubsystemClient.OPEN_WRITE, attrs));
0676: }
0677:
0678: if (progress != null) {
0679: progress.started(in.available(), remotePath);
0680: }
0681:
0682: transferFile(in, out, progress);
0683:
0684: if (progress != null) {
0685: progress.completed();
0686: }
0687:
0688: // Set the permissions here since at creation they dont always work
0689: if (newfile) {
0690: chmod(default_permissions ^ umask, remotePath);
0691: }
0692: }
0693:
0694: /**
0695: *
0696: *
0697: * @param in
0698: * @param remote
0699: *
0700: * @return
0701: *
0702: * @throws IOException
0703: */
0704: public void put(InputStream in, String remote) throws IOException {
0705: put(in, remote, null);
0706: }
0707:
0708: /**
0709: * <p>
0710: * Sets the user ID to owner for the file or directory.
0711: * </p>
0712: *
0713: * @param uid numeric user id of the new owner
0714: * @param path the path to the remote file/directory
0715: *
0716: * @throws IOException if an IO error occurs or the file does not exist
0717: *
0718: * @since 0.2.0
0719: */
0720: public void chown(int uid, String path) throws IOException {
0721: String actual = resolveRemotePath(path);
0722: FileAttributes attrs = sftp.getAttributes(actual);
0723: attrs.setUID(new UnsignedInteger32(uid));
0724: sftp.setAttributes(actual, attrs);
0725: }
0726:
0727: /**
0728: * <p>
0729: * Sets the group ID for the file or directory.
0730: * </p>
0731: *
0732: * @param gid the numeric group id for the new group
0733: * @param path the path to the remote file/directory
0734: *
0735: * @throws IOException if an IO error occurs or the file does not exist
0736: *
0737: * @since 0.2.0
0738: */
0739: public void chgrp(int gid, String path) throws IOException {
0740: String actual = resolveRemotePath(path);
0741: FileAttributes attrs = sftp.getAttributes(actual);
0742: attrs.setGID(new UnsignedInteger32(gid));
0743: sftp.setAttributes(actual, attrs);
0744: }
0745:
0746: /**
0747: * <p>
0748: * Changes the access permissions or modes of the specified file or
0749: * directory.
0750: * </p>
0751: *
0752: * <p>
0753: * Modes determine who can read, change or execute a file.
0754: * </p>
0755: * <blockquote><pre>Absolute modes are octal numbers specifying the complete list of
0756: * attributes for the files; you specify attributes by OR'ing together
0757: * these bits.
0758: *
0759: * 0400 Individual read
0760: * 0200 Individual write
0761: * 0100 Individual execute (or list directory)
0762: * 0040 Group read
0763: * 0020 Group write
0764: * 0010 Group execute
0765: * 0004 Other read
0766: * 0002 Other write
0767: * 0001 Other execute </pre></blockquote>
0768: *
0769: * @param permissions the absolute mode of the file/directory
0770: * @param path the path to the file/directory on the remote server
0771: *
0772: * @throws IOException if an IO error occurs or the file if not found
0773: *
0774: * @since 0.2.0
0775: */
0776: public void chmod(int permissions, String path) throws IOException {
0777: String actual = resolveRemotePath(path);
0778: sftp.changePermissions(actual, permissions);
0779: }
0780:
0781: public void umask(String umask) throws IOException {
0782: try {
0783: this .umask = Integer.parseInt(umask, 8);
0784: } catch (NumberFormatException ex) {
0785: throw new IOException(
0786: "umask must be 4 digit octal number e.g. 0022");
0787: }
0788: }
0789:
0790: /**
0791: * <p>
0792: * Rename a file on the remote computer.
0793: * </p>
0794: *
0795: * @param oldpath the old path
0796: * @param newpath the new path
0797: *
0798: * @throws IOException if an IO error occurs
0799: *
0800: * @since 0.2.0
0801: */
0802: public void rename(String oldpath, String newpath)
0803: throws IOException {
0804: String from = resolveRemotePath(oldpath);
0805: String to = resolveRemotePath(newpath);
0806: sftp.renameFile(from, to);
0807: }
0808:
0809: /**
0810: * <p>
0811: * Remove a file or directory from the remote computer.
0812: * </p>
0813: *
0814: * @param path the path of the remote file/directory
0815: *
0816: * @throws IOException if an IO error occurs
0817: *
0818: * @since 0.2.0
0819: */
0820: public void rm(String path) throws IOException {
0821: String actual = resolveRemotePath(path);
0822: FileAttributes attrs = sftp.getAttributes(actual);
0823:
0824: if (attrs.isDirectory()) {
0825: sftp.removeDirectory(actual);
0826: } else {
0827: sftp.removeFile(actual);
0828: }
0829: }
0830:
0831: /**
0832: *
0833: *
0834: * @param path
0835: * @param force
0836: * @param recurse
0837: *
0838: * @throws IOException
0839: */
0840: public void rm(String path, boolean force, boolean recurse)
0841: throws IOException {
0842: String actual = resolveRemotePath(path);
0843: FileAttributes attrs = sftp.getAttributes(actual);
0844: SftpFile file;
0845:
0846: if (attrs.isDirectory()) {
0847: List list = ls(path);
0848:
0849: if (!force && (list.size() > 0)) {
0850: throw new IOException(
0851: "You cannot delete non-empty directory, use force=true to overide");
0852: } else {
0853: for (Iterator it = list.iterator(); it.hasNext();) {
0854: file = (SftpFile) it.next();
0855:
0856: if (file.isDirectory()
0857: && !file.getFilename().equals(".")
0858: && !file.getFilename().equals("..")) {
0859: if (recurse) {
0860: rm(file.getAbsolutePath(), force, recurse);
0861: } else {
0862: throw new IOException(
0863: "Directory has contents, cannot delete without recurse=true");
0864: }
0865: } else if (file.isFile()) {
0866: sftp.removeFile(file.getAbsolutePath());
0867: }
0868: }
0869: }
0870:
0871: sftp.removeDirectory(actual);
0872: } else {
0873: sftp.removeFile(actual);
0874: }
0875: }
0876:
0877: /**
0878: * <p>
0879: * Create a symbolic link on the remote computer.
0880: * </p>
0881: *
0882: * @param path the path to the existing file
0883: * @param link the new link
0884: *
0885: * @throws IOException if an IO error occurs or the operation is not
0886: * supported on the remote platform
0887: *
0888: * @since 0.2.0
0889: */
0890: public void symlink(String path, String link) throws IOException {
0891: String actualPath = resolveRemotePath(path);
0892: String actualLink = resolveRemotePath(link);
0893: sftp.createSymbolicLink(actualPath, actualLink);
0894: }
0895:
0896: /**
0897: * <p>
0898: * Returns the attributes of the file from the remote computer.
0899: * </p>
0900: *
0901: * @param path the path of the file on the remote computer
0902: *
0903: * @return the attributes
0904: *
0905: * @throws IOException if an IO error occurs or the file does not exist
0906: *
0907: * @see com.sshtools.j2ssh.sftp.FileAttributes
0908: * @since 0.2.0
0909: */
0910: public FileAttributes stat(String path) throws IOException {
0911: String actual = resolveRemotePath(path);
0912:
0913: return sftp.getAttributes(actual);
0914: }
0915:
0916: /**
0917: *
0918: *
0919: * @param path
0920: *
0921: * @return
0922: *
0923: * @throws IOException
0924: */
0925: public String getAbsolutePath(String path) throws IOException {
0926: String actual = resolveRemotePath(path);
0927:
0928: return sftp.getAbsolutePath(path);
0929: }
0930:
0931: /**
0932: * <p>
0933: * Close the SFTP client.
0934: * </p>
0935: *
0936: * @throws IOException
0937: *
0938: * @since 0.2.0
0939: */
0940: public void quit() throws IOException {
0941: sftp.close();
0942: }
0943:
0944: /**
0945: *
0946: *
0947: * @param localdir
0948: * @param remotedir
0949: * @param recurse
0950: * @param sync
0951: * @param commit
0952: * @param progress
0953: *
0954: * @return
0955: *
0956: * @throws IOException
0957: */
0958: public DirectoryOperation copyLocalDirectory(String localdir,
0959: String remotedir, boolean recurse, boolean sync,
0960: boolean commit, FileTransferProgress progress)
0961: throws IOException {
0962: DirectoryOperation op = new DirectoryOperation();
0963:
0964: // Record the previous
0965: String pwd = pwd();
0966: String lpwd = lpwd();
0967: File local = resolveLocalPath(localdir);
0968: remotedir = resolveRemotePath(remotedir);
0969: remotedir += (remotedir.endsWith("/") ? "" : "/");
0970: remotedir += local.getName();
0971: remotedir += (remotedir.endsWith("/") ? "" : "/");
0972:
0973: // Setup the remote directory if were committing
0974: if (commit) {
0975: try {
0976: FileAttributes attrs = stat(remotedir);
0977: } catch (IOException ex) {
0978: mkdir(remotedir);
0979: }
0980: }
0981:
0982: // List the local files and verify against the remote server
0983: File[] ls = local.listFiles();
0984:
0985: if (ls != null) {
0986: for (int i = 0; i < ls.length; i++) {
0987: if (ls[i].isDirectory() && !ls[i].getName().equals(".")
0988: && !ls[i].getName().equals("..")) {
0989: if (recurse) {
0990: File f = new File(local, ls[i].getName());
0991: op.addDirectoryOperation(copyLocalDirectory(f
0992: .getAbsolutePath(), remotedir, recurse,
0993: sync, commit, progress), f);
0994: }
0995: } else if (ls[i].isFile()) {
0996: try {
0997: FileAttributes attrs = stat(remotedir
0998: + ls[i].getName());
0999:
1000: if ((ls[i].length() == attrs.getSize()
1001: .longValue())
1002: && ((ls[i].lastModified() / 1000) == attrs
1003: .getModifiedTime().longValue())) {
1004: op.addUnchangedFile(ls[i]);
1005: } else {
1006: op.addUpdatedFile(ls[i]);
1007: }
1008: } catch (IOException ex1) {
1009: op.addNewFile(ls[i]);
1010: }
1011:
1012: if (commit) {
1013: put(ls[i].getAbsolutePath(), remotedir
1014: + ls[i].getName(), progress);
1015:
1016: FileAttributes attrs = stat(remotedir
1017: + ls[i].getName());
1018: attrs.setTimes(new UnsignedInteger32(ls[i]
1019: .lastModified() / 1000),
1020: new UnsignedInteger32(ls[i]
1021: .lastModified() / 1000));
1022: sftp.setAttributes(remotedir + ls[i].getName(),
1023: attrs);
1024: }
1025: }
1026: }
1027: }
1028:
1029: if (sync) {
1030: // List the contents of the new local directory and remove any
1031: // files/directories that were not updated
1032: try {
1033: List files = ls(remotedir);
1034: SftpFile file;
1035: File f;
1036:
1037: for (Iterator it = files.iterator(); it.hasNext();) {
1038: file = (SftpFile) it.next();
1039:
1040: // Create a local file object to test for its existence
1041: f = new File(local, file.getFilename());
1042:
1043: if (!op.containsFile(file)
1044: && !file.getFilename().equals(".")
1045: && !file.getFilename().equals("..")) {
1046: op.addDeletedFile(file);
1047:
1048: if (commit) {
1049: if (file.isDirectory()) {
1050: // Recurse through the directory, deleting stuff
1051: recurseMarkForDeletion(file, op);
1052:
1053: if (commit) {
1054: rm(file.getAbsolutePath(), true,
1055: true);
1056: }
1057: } else if (file.isFile()) {
1058: rm(file.getAbsolutePath());
1059: }
1060: }
1061: }
1062: }
1063: } catch (IOException ex2) {
1064: // Ignorew since if it does not exist we cant delete it
1065: }
1066: }
1067:
1068: // Return the operation details
1069: return op;
1070: }
1071:
1072: /**
1073: *
1074: *
1075: * @param eventListener
1076: */
1077: public void addEventListener(ChannelEventListener eventListener) {
1078: sftp.addEventListener(eventListener);
1079: }
1080:
1081: private void recurseMarkForDeletion(SftpFile file,
1082: DirectoryOperation op) throws IOException {
1083: List list = ls(file.getAbsolutePath());
1084: op.addDeletedFile(file);
1085:
1086: for (Iterator it = list.iterator(); it.hasNext();) {
1087: file = (SftpFile) it.next();
1088:
1089: if (file.isDirectory() && !file.getFilename().equals(".")
1090: && !file.getFilename().equals("..")) {
1091: recurseMarkForDeletion(file, op);
1092: } else if (file.isFile()) {
1093: op.addDeletedFile(file);
1094: }
1095: }
1096: }
1097:
1098: private void recurseMarkForDeletion(File file, DirectoryOperation op)
1099: throws IOException {
1100: File[] list = file.listFiles();
1101: op.addDeletedFile(file);
1102:
1103: if (list != null) {
1104: for (int i = 0; i < list.length; i++) {
1105: file = list[i];
1106:
1107: if (file.isDirectory() && !file.getName().equals(".")
1108: && !file.getName().equals("..")) {
1109: recurseMarkForDeletion(file, op);
1110: } else if (file.isFile()) {
1111: op.addDeletedFile(file);
1112: }
1113: }
1114: }
1115: }
1116:
1117: /**
1118: *
1119: *
1120: * @param remotedir
1121: * @param localdir
1122: * @param recurse
1123: * @param sync
1124: * @param commit
1125: * @param progress
1126: *
1127: * @return
1128: *
1129: * @throws IOException
1130: */
1131: public DirectoryOperation copyRemoteDirectory(String remotedir,
1132: String localdir, boolean recurse, boolean sync,
1133: boolean commit, FileTransferProgress progress)
1134: throws IOException {
1135: // Create an operation object to hold the information
1136: DirectoryOperation op = new DirectoryOperation();
1137:
1138: // Record the previous working directoies
1139: String pwd = pwd();
1140: String lpwd = lpwd();
1141: cd(remotedir);
1142:
1143: // Setup the local cwd
1144: String base = remotedir;
1145: int idx = base.lastIndexOf('/');
1146:
1147: if (idx != -1) {
1148: base = base.substring(idx + 1);
1149: }
1150:
1151: File local = new File(localdir, base);
1152:
1153: // File local = new File(localdir, remotedir);
1154: if (!local.isAbsolute()) {
1155: local = new File(lpwd(), localdir);
1156: }
1157:
1158: if (!local.exists() && commit) {
1159: local.mkdir();
1160: }
1161:
1162: List files = ls();
1163: SftpFile file;
1164: File f;
1165:
1166: for (Iterator it = files.iterator(); it.hasNext();) {
1167: file = (SftpFile) it.next();
1168:
1169: if (file.isDirectory() && !file.getFilename().equals(".")
1170: && !file.getFilename().equals("..")) {
1171: if (recurse) {
1172: f = new File(local, file.getFilename());
1173: op.addDirectoryOperation(copyRemoteDirectory(file
1174: .getFilename(), local.getAbsolutePath(),
1175: recurse, sync, commit, progress), f);
1176: }
1177: } else if (file.isFile()) {
1178: f = new File(local, file.getFilename());
1179:
1180: if (f.exists()
1181: && (f.length() == file.getAttributes()
1182: .getSize().longValue())
1183: && ((f.lastModified() / 1000) == file
1184: .getAttributes().getModifiedTime()
1185: .longValue())) {
1186: if (commit) {
1187: op.addUnchangedFile(f);
1188: } else {
1189: op.addUnchangedFile(file);
1190: }
1191:
1192: continue;
1193: }
1194:
1195: if (f.exists()) {
1196: if (commit) {
1197: op.addUpdatedFile(f);
1198: } else {
1199: op.addUpdatedFile(file);
1200: }
1201: } else {
1202: if (commit) {
1203: op.addNewFile(f);
1204: } else {
1205: op.addNewFile(file);
1206: }
1207: }
1208:
1209: if (commit) {
1210: FileAttributes attrs = get(file.getFilename(), f
1211: .getAbsolutePath(), progress);
1212: f.setLastModified(attrs.getModifiedTime()
1213: .longValue() * 1000);
1214: }
1215: }
1216: }
1217:
1218: if (sync) {
1219: // List the contents of the new local directory and remove any
1220: // files/directories that were not updated
1221: File[] contents = local.listFiles();
1222:
1223: if (contents != null) {
1224: for (int i = 0; i < contents.length; i++) {
1225: if (!op.containsFile(contents[i])) {
1226: op.addDeletedFile(contents[i]);
1227:
1228: if (contents[i].isDirectory()
1229: && !contents[i].getName().equals(".")
1230: && !contents[i].getName().equals("..")) {
1231: recurseMarkForDeletion(contents[i], op);
1232:
1233: if (commit) {
1234: IOUtil
1235: .recurseDeleteDirectory(contents[i]);
1236: }
1237: } else if (commit) {
1238: contents[i].delete();
1239: }
1240: }
1241: }
1242: }
1243: }
1244:
1245: cd(pwd);
1246:
1247: return op;
1248: }
1249: }
|