/* blizzy's Backup - Easy to use personal file backup application Copyright (C) 2011-2012 Maik Schreiber This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package de.blizzy.backup.vfs.sftp; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.attribute.FileTime; import java.util.HashSet; import java.util.List; import java.util.Set; import net.schmizz.sshj.sftp.FileAttributes; import net.schmizz.sshj.sftp.FileMode; import net.schmizz.sshj.sftp.RemoteResourceInfo; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import de.blizzy.backup.vfs.ActionRunner; import de.blizzy.backup.vfs.IAction; import de.blizzy.backup.vfs.IFile; import de.blizzy.backup.vfs.IFileSystemEntry; import de.blizzy.backup.vfs.IFolder; import de.blizzy.backup.vfs.IOutputStreamProvider; class SftpFileOrFolder implements IFile, IFolder { private static final int BUFFER_SIZE = 128 * 1024; private String file; private SftpLocation location; private FileAttributes fileAttributes; SftpFileOrFolder(String file, SftpLocation location) { this.file = file; this.location = location; } @Override public String getName() { return StringUtils.substringAfterLast(file, "/"); //$NON-NLS-1$ } @Override public String getAbsolutePath() { return "sftp://" + location.getHost() + file; //$NON-NLS-1$ } @Override public IFolder getParentFolder() { if (!file.equals("/")) { //$NON-NLS-1$ String parentFolder = StringUtils.substringBeforeLast(file, "/"); //$NON-NLS-1$ if (StringUtils.isBlank(parentFolder)) { parentFolder = "/"; //$NON-NLS-1$ } return new SftpFileOrFolder(parentFolder, location); } return null; } @Override public boolean isHidden() { return getName().startsWith("."); //$NON-NLS-1$ } @Override public FileTime getCreationTime() { return null; } @Override public FileTime getLastModificationTime() throws IOException { return FileTime.fromMillis(getFileAttributes().getMtime() * 1000L); } private FileAttributes getFileAttributes() throws IOException { if (fileAttributes == null) { IAction<FileAttributes> action = new IAction<FileAttributes>() { @Override public FileAttributes run() throws IOException { return location.getSftpClient().lstat(file); } @Override public boolean canRetry(IOException e) { return location.canRetryAction(e); } }; fileAttributes = new ActionRunner<>( action, SftpLocation.MAX_TRIES, location).run(); } return fileAttributes; } @Override public boolean isFolder() throws IOException { return getFileAttributes().getType() == FileMode.Type.DIRECTORY; } @Override public Set<IFileSystemEntry> list() throws IOException { IAction<List<RemoteResourceInfo>> action = new IAction<List<RemoteResourceInfo>>() { @Override public List<RemoteResourceInfo> run() throws IOException { return location.getSftpClient().ls(file); } @Override public boolean canRetry(IOException e) { return location.canRetryAction(e); } }; List<RemoteResourceInfo> files = new ActionRunner<>( action, SftpLocation.MAX_TRIES, location).run(); Set<IFileSystemEntry> result = new HashSet<>(); for (RemoteResourceInfo file : files) { result.add(new SftpFileOrFolder(file.getPath(), location)); } return result; } @Override public void copy(final IOutputStreamProvider outputStreamProvider) throws IOException { IAction<Void> action = new IAction<Void>() { @Override public Void run() throws IOException { InputStream in = null; OutputStream out = null; try { in = new BufferedInputStream(location.getSftpClient().open(file).getInputStream(), BUFFER_SIZE); out = outputStreamProvider.getOutputStream(); IOUtils.copy(in, out); } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(out); } return null; } @Override public boolean canRetry(IOException e) { return location.canRetryAction(e); } }; new ActionRunner<>(action, SftpLocation.MAX_TRIES, location).run(); } @Override public long getLength() throws IOException { return getFileAttributes().getSize(); } @Override public int hashCode() { return file.hashCode(); } @Override public boolean equals(Object o) { if (o == this) { return true; } else if ((o != null) && o.getClass().equals(getClass())) { SftpFileOrFolder other = (SftpFileOrFolder) o; return other.file.equals(file); } return false; } }