http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java deleted file mode 100644 index 5cfbf01..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java +++ /dev/null @@ -1,1069 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.sshd.server.subsystem.sftp; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.UnknownServiceException; -import java.nio.file.AccessDeniedException; -import java.nio.file.FileSystem; -import java.nio.file.FileSystemLoopException; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.NoSuchFileException; -import java.nio.file.NotDirectoryException; -import java.nio.file.Path; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; -import java.util.TreeMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.sshd.common.Factory; -import org.apache.sshd.common.FactoryManager; -import org.apache.sshd.common.digest.BuiltinDigests; -import org.apache.sshd.common.digest.DigestFactory; -import org.apache.sshd.common.file.FileSystemAware; -import org.apache.sshd.common.random.Random; -import org.apache.sshd.common.subsystem.sftp.SftpConstants; -import org.apache.sshd.common.subsystem.sftp.SftpHelper; -import org.apache.sshd.common.subsystem.sftp.extensions.openssh.FsyncExtensionParser; -import org.apache.sshd.common.subsystem.sftp.extensions.openssh.HardLinkExtensionParser; -import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.ValidateUtils; -import org.apache.sshd.common.util.buffer.Buffer; -import org.apache.sshd.common.util.buffer.BufferUtils; -import org.apache.sshd.common.util.buffer.ByteArrayBuffer; -import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; -import org.apache.sshd.common.util.threads.ThreadUtils; -import org.apache.sshd.server.Command; -import org.apache.sshd.server.Environment; -import org.apache.sshd.server.ExitCallback; -import org.apache.sshd.server.SessionAware; -import org.apache.sshd.server.session.ServerSession; - -/** - * SFTP subsystem - * - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public class SftpSubsystem - extends AbstractSftpSubsystemHelper - implements Command, Runnable, SessionAware, FileSystemAware, ExecutorServiceCarrier { - - /** - * Properties key for the maximum of available open handles per session. - */ - public static final String MAX_OPEN_HANDLES_PER_SESSION = "max-open-handles-per-session"; - public static final int DEFAULT_MAX_OPEN_HANDLES = Integer.MAX_VALUE; - - /** - * Size in bytes of the opaque handle value - * - * @see #DEFAULT_FILE_HANDLE_SIZE - */ - public static final String FILE_HANDLE_SIZE = "sftp-handle-size"; - public static final int MIN_FILE_HANDLE_SIZE = 4; // ~uint32 - public static final int DEFAULT_FILE_HANDLE_SIZE = 16; - public static final int MAX_FILE_HANDLE_SIZE = 64; // ~sha512 - - /** - * Max. rounds to attempt to create a unique file handle - if all handles - * already in use after these many rounds, then an exception is thrown - * - * @see #generateFileHandle(Path) - * @see #DEFAULT_FILE_HANDLE_ROUNDS - */ - public static final String MAX_FILE_HANDLE_RAND_ROUNDS = "sftp-handle-rand-max-rounds"; - public static final int MIN_FILE_HANDLE_ROUNDS = 1; - public static final int DEFAULT_FILE_HANDLE_ROUNDS = MIN_FILE_HANDLE_SIZE; - public static final int MAX_FILE_HANDLE_ROUNDS = MAX_FILE_HANDLE_SIZE; - - /** - * Maximum amount of data allocated for listing the contents of a directory - * in any single invocation of {@link #doReadDir(Buffer, int)} - * - * @see #DEFAULT_MAX_READDIR_DATA_SIZE - */ - public static final String MAX_READDIR_DATA_SIZE_PROP = "sftp-max-readdir-data-size"; - public static final int DEFAULT_MAX_READDIR_DATA_SIZE = 16 * 1024; - - protected ExitCallback callback; - protected InputStream in; - protected OutputStream out; - protected OutputStream err; - protected Environment env; - protected Random randomizer; - protected int fileHandleSize = DEFAULT_FILE_HANDLE_SIZE; - protected int maxFileHandleRounds = DEFAULT_FILE_HANDLE_ROUNDS; - protected Future<?> pendingFuture; - protected byte[] workBuf = new byte[Math.max(DEFAULT_FILE_HANDLE_SIZE, Integer.BYTES)]; - protected FileSystem fileSystem = FileSystems.getDefault(); - protected Path defaultDir = fileSystem.getPath(System.getProperty("user.dir")); - protected long requestsCount; - protected int version; - protected final Map<String, byte[]> extensions = new TreeMap<>(Comparator.naturalOrder()); - protected final Map<String, Handle> handles = new HashMap<>(); - - private ServerSession serverSession; - private final AtomicBoolean closed = new AtomicBoolean(false); - private ExecutorService executorService; - private boolean shutdownOnExit; - - /** - * @param executorService The {@link ExecutorService} to be used by - * the {@link SftpSubsystem} command when starting execution. If - * {@code null} then a single-threaded ad-hoc service is used. - * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()} - * will be called when subsystem terminates - unless it is the ad-hoc - * service, which will be shutdown regardless - * @param policy The {@link UnsupportedAttributePolicy} to use if failed to access - * some local file attributes - * @param accessor The {@link SftpFileSystemAccessor} to use for opening files and directories - * @param errorStatusDataHandler The (never {@code null}) {@link SftpErrorStatusDataHandler} to - * use when generating failed commands error messages - * @see ThreadUtils#newSingleThreadExecutor(String) - */ - public SftpSubsystem(ExecutorService executorService, boolean shutdownOnExit, UnsupportedAttributePolicy policy, - SftpFileSystemAccessor accessor, SftpErrorStatusDataHandler errorStatusDataHandler) { - super(policy, accessor, errorStatusDataHandler); - - if (executorService == null) { - this.executorService = ThreadUtils.newSingleThreadExecutor(getClass().getSimpleName()); - this.shutdownOnExit = true; // we always close the ad-hoc executor service - } else { - this.executorService = executorService; - this.shutdownOnExit = shutdownOnExit; - } - } - - @Override - public int getVersion() { - return version; - } - - @Override - public Path getDefaultDirectory() { - return defaultDir; - } - - @Override - public ExecutorService getExecutorService() { - return executorService; - } - - @Override - public boolean isShutdownOnExit() { - return shutdownOnExit; - } - - @Override - public void setSession(ServerSession session) { - this.serverSession = Objects.requireNonNull(session, "No session"); - - FactoryManager manager = session.getFactoryManager(); - Factory<? extends Random> factory = manager.getRandomFactory(); - this.randomizer = factory.create(); - - this.fileHandleSize = session.getIntProperty(FILE_HANDLE_SIZE, DEFAULT_FILE_HANDLE_SIZE); - ValidateUtils.checkTrue(this.fileHandleSize >= MIN_FILE_HANDLE_SIZE, "File handle size too small: %d", this.fileHandleSize); - ValidateUtils.checkTrue(this.fileHandleSize <= MAX_FILE_HANDLE_SIZE, "File handle size too big: %d", this.fileHandleSize); - - this.maxFileHandleRounds = session.getIntProperty(MAX_FILE_HANDLE_RAND_ROUNDS, DEFAULT_FILE_HANDLE_ROUNDS); - ValidateUtils.checkTrue(this.maxFileHandleRounds >= MIN_FILE_HANDLE_ROUNDS, "File handle rounds too small: %d", this.maxFileHandleRounds); - ValidateUtils.checkTrue(this.maxFileHandleRounds <= MAX_FILE_HANDLE_ROUNDS, "File handle rounds too big: %d", this.maxFileHandleRounds); - - if (workBuf.length < this.fileHandleSize) { - workBuf = new byte[this.fileHandleSize]; - } - } - - @Override - public ServerSession getServerSession() { - return serverSession; - } - - @Override - public void setFileSystem(FileSystem fileSystem) { - if (fileSystem != this.fileSystem) { - this.fileSystem = fileSystem; - - Iterable<Path> roots = Objects.requireNonNull(fileSystem.getRootDirectories(), "No root directories"); - Iterator<Path> available = Objects.requireNonNull(roots.iterator(), "No roots iterator"); - ValidateUtils.checkTrue(available.hasNext(), "No available root"); - this.defaultDir = available.next(); - } - } - - @Override - public void setExitCallback(ExitCallback callback) { - this.callback = callback; - } - - @Override - public void setInputStream(InputStream in) { - this.in = in; - } - - @Override - public void setOutputStream(OutputStream out) { - this.out = out; - } - - @Override - public void setErrorStream(OutputStream err) { - this.err = err; - } - - @Override - public void start(Environment env) throws IOException { - this.env = env; - try { - ExecutorService executor = getExecutorService(); - pendingFuture = executor.submit(this); - } catch (RuntimeException e) { // e.g., RejectedExecutionException - log.error("Failed (" + e.getClass().getSimpleName() + ") to start command: " + e.toString(), e); - throw new IOException(e); - } - } - - @Override - public void run() { - try { - for (long count = 1L;; count++) { - int length = BufferUtils.readInt(in, workBuf, 0, workBuf.length); - ValidateUtils.checkTrue(length >= (Integer.BYTES + 1 /* command */), "Bad length to read: %d", length); - - Buffer buffer = new ByteArrayBuffer(length + Integer.BYTES + Long.SIZE /* a bit extra */, false); - buffer.putInt(length); - for (int remainLen = length; remainLen > 0;) { - int l = in.read(buffer.array(), buffer.wpos(), remainLen); - if (l < 0) { - throw new IllegalArgumentException("Premature EOF at buffer #" + count + " while read length=" + length + " and remain=" + remainLen); - } - buffer.wpos(buffer.wpos() + l); - remainLen -= l; - } - - process(buffer); - } - } catch (Throwable t) { - if ((!closed.get()) && (!(t instanceof EOFException))) { // Ignore - log.error("run({}) {} caught in SFTP subsystem: {}", - getServerSession(), t.getClass().getSimpleName(), t.getMessage()); - if (log.isDebugEnabled()) { - log.debug("run(" + getServerSession() + ") caught exception details", t); - } - } - } finally { - boolean debugEnabled = log.isDebugEnabled(); - handles.forEach((id, handle) -> { - try { - handle.close(); - if (debugEnabled) { - log.debug("run({}) closed pending handle {} [{}]", getServerSession(), id, handle); - } - } catch (IOException ioe) { - log.error("run({}) failed ({}) to close handle={}[{}]: {}", - getServerSession(), ioe.getClass().getSimpleName(), id, handle, ioe.getMessage()); - } - }); - - callback.onExit(0); - } - } - - @Override - protected void process(Buffer buffer) throws IOException { - int length = buffer.getInt(); - int type = buffer.getUByte(); - int id = buffer.getInt(); - if (log.isDebugEnabled()) { - log.debug("process({})[length={}, type={}, id={}] processing", - getServerSession(), length, SftpConstants.getCommandMessageName(type), id); - } - - switch (type) { - case SftpConstants.SSH_FXP_INIT: - doInit(buffer, id); - break; - case SftpConstants.SSH_FXP_OPEN: - doOpen(buffer, id); - break; - case SftpConstants.SSH_FXP_CLOSE: - doClose(buffer, id); - break; - case SftpConstants.SSH_FXP_READ: - doRead(buffer, id); - break; - case SftpConstants.SSH_FXP_WRITE: - doWrite(buffer, id); - break; - case SftpConstants.SSH_FXP_LSTAT: - doLStat(buffer, id); - break; - case SftpConstants.SSH_FXP_FSTAT: - doFStat(buffer, id); - break; - case SftpConstants.SSH_FXP_SETSTAT: - doSetStat(buffer, id); - break; - case SftpConstants.SSH_FXP_FSETSTAT: - doFSetStat(buffer, id); - break; - case SftpConstants.SSH_FXP_OPENDIR: - doOpenDir(buffer, id); - break; - case SftpConstants.SSH_FXP_READDIR: - doReadDir(buffer, id); - break; - case SftpConstants.SSH_FXP_REMOVE: - doRemove(buffer, id); - break; - case SftpConstants.SSH_FXP_MKDIR: - doMakeDirectory(buffer, id); - break; - case SftpConstants.SSH_FXP_RMDIR: - doRemoveDirectory(buffer, id); - break; - case SftpConstants.SSH_FXP_REALPATH: - doRealPath(buffer, id); - break; - case SftpConstants.SSH_FXP_STAT: - doStat(buffer, id); - break; - case SftpConstants.SSH_FXP_RENAME: - doRename(buffer, id); - break; - case SftpConstants.SSH_FXP_READLINK: - doReadLink(buffer, id); - break; - case SftpConstants.SSH_FXP_SYMLINK: - doSymLink(buffer, id); - break; - case SftpConstants.SSH_FXP_LINK: - doLink(buffer, id); - break; - case SftpConstants.SSH_FXP_BLOCK: - doBlock(buffer, id); - break; - case SftpConstants.SSH_FXP_UNBLOCK: - doUnblock(buffer, id); - break; - case SftpConstants.SSH_FXP_EXTENDED: - doExtended(buffer, id); - break; - default: - { - String name = SftpConstants.getCommandMessageName(type); - log.warn("process({})[length={}, type={}, id={}] unknown command", - getServerSession(), length, name, id); - sendStatus(BufferUtils.clear(buffer), id, SftpConstants.SSH_FX_OP_UNSUPPORTED, "Command " + name + " is unsupported or not implemented"); - } - } - - if (type != SftpConstants.SSH_FXP_INIT) { - requestsCount++; - } - } - - @Override - protected void executeExtendedCommand(Buffer buffer, int id, String extension) throws IOException { - switch (extension) { - case SftpConstants.EXT_TEXT_SEEK: - doTextSeek(buffer, id); - break; - case SftpConstants.EXT_VERSION_SELECT: - doVersionSelect(buffer, id); - break; - case SftpConstants.EXT_COPY_FILE: - doCopyFile(buffer, id); - break; - case SftpConstants.EXT_COPY_DATA: - doCopyData(buffer, id); - break; - case SftpConstants.EXT_MD5_HASH: - case SftpConstants.EXT_MD5_HASH_HANDLE: - doMD5Hash(buffer, id, extension); - break; - case SftpConstants.EXT_CHECK_FILE_HANDLE: - case SftpConstants.EXT_CHECK_FILE_NAME: - doCheckFileHash(buffer, id, extension); - break; - case FsyncExtensionParser.NAME: - doOpenSSHFsync(buffer, id); - break; - case SftpConstants.EXT_SPACE_AVAILABLE: - doSpaceAvailable(buffer, id); - break; - case HardLinkExtensionParser.NAME: - doOpenSSHHardLink(buffer, id); - break; - default: - if (log.isDebugEnabled()) { - log.debug("executeExtendedCommand({}) received unsupported SSH_FXP_EXTENDED({})", getServerSession(), extension); - } - sendStatus(BufferUtils.clear(buffer), id, SftpConstants.SSH_FX_OP_UNSUPPORTED, "Command SSH_FXP_EXTENDED(" + extension + ") is unsupported or not implemented"); - break; - } - } - - @Override - protected void createLink(int id, String existingPath, String linkPath, boolean symLink) throws IOException { - Path link = resolveFile(linkPath); - Path existing = fileSystem.getPath(existingPath); - if (log.isDebugEnabled()) { - log.debug("createLink({})[id={}], existing={}[{}], link={}[{}], symlink={})", - getServerSession(), id, linkPath, link, existingPath, existing, symLink); - } - - SftpEventListener listener = getSftpEventListenerProxy(); - ServerSession session = getServerSession(); - listener.linking(session, link, existing, symLink); - try { - if (symLink) { - Files.createSymbolicLink(link, existing); - } else { - Files.createLink(link, existing); - } - } catch (IOException | RuntimeException e) { - listener.linked(session, link, existing, symLink, e); - throw e; - } - listener.linked(session, link, existing, symLink, null); - } - - @Override - protected void doTextSeek(int id, String handle, long line) throws IOException { - Handle h = handles.get(handle); - if (log.isDebugEnabled()) { - log.debug("doTextSeek({})[id={}] SSH_FXP_EXTENDED(text-seek) (handle={}[{}], line={})", - getServerSession(), id, handle, h, line); - } - - FileHandle fileHandle = validateHandle(handle, h, FileHandle.class); - throw new UnknownServiceException("doTextSeek(" + fileHandle + ")"); - } - - @Override - protected void doOpenSSHFsync(int id, String handle) throws IOException { - Handle h = handles.get(handle); - if (log.isDebugEnabled()) { - log.debug("doOpenSSHFsync({})[id={}] {}[{}]", getServerSession(), id, handle, h); - } - - FileHandle fileHandle = validateHandle(handle, h, FileHandle.class); - SftpFileSystemAccessor accessor = getFileSystemAccessor(); - ServerSession session = getServerSession(); - accessor.syncFileData(session, this, fileHandle.getFile(), fileHandle.getFileHandle(), fileHandle.getFileChannel()); - } - - @Override - protected void doCheckFileHash( - int id, String targetType, String target, Collection<String> algos, - long startOffset, long length, int blockSize, Buffer buffer) - throws Exception { - Path path; - if (SftpConstants.EXT_CHECK_FILE_HANDLE.equalsIgnoreCase(targetType)) { - Handle h = handles.get(target); - FileHandle fileHandle = validateHandle(target, h, FileHandle.class); - path = fileHandle.getFile(); - - /* - * To quote http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt section 9.1.2: - * - * If ACE4_READ_DATA was not included when the file was opened, - * the server MUST return STATUS_PERMISSION_DENIED. - */ - int access = fileHandle.getAccessMask(); - if ((access & SftpConstants.ACE4_READ_DATA) == 0) { - throw new AccessDeniedException(path.toString(), path.toString(), "File not opened for read"); - } - } else { - path = resolveFile(target); - - /* - * To quote http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt section 9.1.2: - * - * If 'check-file-name' refers to a SSH_FILEXFER_TYPE_SYMLINK, the - * target should be opened. - */ - for (int index = 0; Files.isSymbolicLink(path) && (index < Byte.MAX_VALUE /* TODO make this configurable */); index++) { - path = Files.readSymbolicLink(path); - } - - if (Files.isSymbolicLink(path)) { - throw new FileSystemLoopException(target); - } - - if (Files.isDirectory(path, IoUtils.getLinkOptions(false))) { - throw new NotDirectoryException(path.toString()); - } - } - - ValidateUtils.checkNotNullAndNotEmpty(algos, "No hash algorithms specified"); - - DigestFactory factory = null; - for (String a : algos) { - factory = BuiltinDigests.fromFactoryName(a); - if ((factory != null) && factory.isSupported()) { - break; - } - } - ValidateUtils.checkNotNull(factory, "No matching digest factory found for %s", algos); - - doCheckFileHash(id, path, factory, startOffset, length, blockSize, buffer); - } - - @Override - protected byte[] doMD5Hash( - int id, String targetType, String target, long startOffset, long length, byte[] quickCheckHash) - throws Exception { - if (log.isDebugEnabled()) { - log.debug("doMD5Hash({})({})[{}] offset={}, length={}, quick-hash={}", - getServerSession(), targetType, target, startOffset, length, - BufferUtils.toHex(':', quickCheckHash)); - } - - Path path; - if (SftpConstants.EXT_MD5_HASH_HANDLE.equalsIgnoreCase(targetType)) { - Handle h = handles.get(target); - FileHandle fileHandle = validateHandle(target, h, FileHandle.class); - path = fileHandle.getFile(); - - /* - * To quote http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt section 9.1.1: - * - * The handle MUST be a file handle, and ACE4_READ_DATA MUST - * have been included in the desired-access when the file - * was opened - */ - int access = fileHandle.getAccessMask(); - if ((access & SftpConstants.ACE4_READ_DATA) == 0) { - throw new AccessDeniedException(path.toString(), path.toString(), "File not opened for read"); - } - } else { - path = resolveFile(target); - if (Files.isDirectory(path, IoUtils.getLinkOptions(true))) { - throw new NotDirectoryException(path.toString()); - } - } - - /* - * To quote http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt section 9.1.1: - * - * If both start-offset and length are zero, the entire file should be included - */ - long effectiveLength = length; - long totalSize = Files.size(path); - if ((startOffset == 0L) && (length == 0L)) { - effectiveLength = totalSize; - } else { - long maxRead = startOffset + effectiveLength; - if (maxRead > totalSize) { - effectiveLength = totalSize - startOffset; - } - } - - return doMD5Hash(id, path, startOffset, effectiveLength, quickCheckHash); - } - - protected void doVersionSelect(Buffer buffer, int id) throws IOException { - String proposed = buffer.getString(); - ServerSession session = getServerSession(); - /* - * The 'version-select' MUST be the first request from the client to the - * server; if it is not, the server MUST fail the request and close the - * channel. - */ - if (requestsCount > 0L) { - sendStatus(BufferUtils.clear(buffer), id, - SftpConstants.SSH_FX_FAILURE, - "Version selection not the 1st request for proposal = " + proposed); - session.close(true); - return; - } - - Boolean result = validateProposedVersion(buffer, id, proposed); - /* - * "MUST then close the channel without processing any further requests" - */ - if (result == null) { // response sent internally - session.close(true); - return; - } - if (result) { - version = Integer.parseInt(proposed); - sendStatus(BufferUtils.clear(buffer), id, SftpConstants.SSH_FX_OK, ""); - } else { - sendStatus(BufferUtils.clear(buffer), id, SftpConstants.SSH_FX_FAILURE, "Unsupported version " + proposed); - session.close(true); - } - } - - @Override - protected void doBlock(int id, String handle, long offset, long length, int mask) throws IOException { - Handle p = handles.get(handle); - if (log.isDebugEnabled()) { - log.debug("doBlock({})[id={}] SSH_FXP_BLOCK (handle={}[{}], offset={}, length={}, mask=0x{})", - getServerSession(), id, handle, p, offset, length, Integer.toHexString(mask)); - } - - FileHandle fileHandle = validateHandle(handle, p, FileHandle.class); - SftpEventListener listener = getSftpEventListenerProxy(); - ServerSession session = getServerSession(); - listener.blocking(session, handle, fileHandle, offset, length, mask); - try { - fileHandle.lock(offset, length, mask); - } catch (IOException | RuntimeException e) { - listener.blocked(session, handle, fileHandle, offset, length, mask, e); - throw e; - } - listener.blocked(session, handle, fileHandle, offset, length, mask, null); - } - - @Override - protected void doUnblock(int id, String handle, long offset, long length) throws IOException { - Handle p = handles.get(handle); - if (log.isDebugEnabled()) { - log.debug("doUnblock({})[id={}] SSH_FXP_UNBLOCK (handle={}[{}], offset={}, length={})", - getServerSession(), id, handle, p, offset, length); - } - - FileHandle fileHandle = validateHandle(handle, p, FileHandle.class); - SftpEventListener listener = getSftpEventListenerProxy(); - ServerSession session = getServerSession(); - listener.unblocking(session, handle, fileHandle, offset, length); - try { - fileHandle.unlock(offset, length); - } catch (IOException | RuntimeException e) { - listener.unblocked(session, handle, fileHandle, offset, length, e); - throw e; - } - listener.unblocked(session, handle, fileHandle, offset, length, null); - } - - @Override - @SuppressWarnings("resource") - protected void doCopyData(int id, String readHandle, long readOffset, long readLength, String writeHandle, long writeOffset) throws IOException { - boolean inPlaceCopy = readHandle.equals(writeHandle); - Handle rh = handles.get(readHandle); - Handle wh = inPlaceCopy ? rh : handles.get(writeHandle); - if (log.isDebugEnabled()) { - log.debug("doCopyData({})[id={}] SSH_FXP_EXTENDED[{}] read={}[{}], read-offset={}, read-length={}, write={}[{}], write-offset={})", - getServerSession(), id, SftpConstants.EXT_COPY_DATA, - readHandle, rh, readOffset, readLength, - writeHandle, wh, writeOffset); - } - - FileHandle srcHandle = validateHandle(readHandle, rh, FileHandle.class); - Path srcPath = srcHandle.getFile(); - int srcAccess = srcHandle.getAccessMask(); - if ((srcAccess & SftpConstants.ACE4_READ_DATA) != SftpConstants.ACE4_READ_DATA) { - throw new AccessDeniedException(srcPath.toString(), srcPath.toString(), "Source file not opened for read"); - } - - ValidateUtils.checkTrue(readLength >= 0L, "Invalid read length: %d", readLength); - ValidateUtils.checkTrue(readOffset >= 0L, "Invalid read offset: %d", readOffset); - - long totalSize = Files.size(srcHandle.getFile()); - long effectiveLength = readLength; - if (effectiveLength == 0L) { - effectiveLength = totalSize - readOffset; - } else { - long maxRead = readOffset + effectiveLength; - if (maxRead > totalSize) { - effectiveLength = totalSize - readOffset; - } - } - ValidateUtils.checkTrue(effectiveLength > 0L, "Non-positive effective copy data length: %d", effectiveLength); - - FileHandle dstHandle = inPlaceCopy ? srcHandle : validateHandle(writeHandle, wh, FileHandle.class); - int dstAccess = dstHandle.getAccessMask(); - if ((dstAccess & SftpConstants.ACE4_WRITE_DATA) != SftpConstants.ACE4_WRITE_DATA) { - throw new AccessDeniedException(srcHandle.toString(), srcHandle.toString(), "Source handle not opened for write"); - } - - ValidateUtils.checkTrue(writeOffset >= 0L, "Invalid write offset: %d", writeOffset); - // check if overlapping ranges as per the draft - if (inPlaceCopy) { - long maxRead = readOffset + effectiveLength; - if (maxRead > totalSize) { - maxRead = totalSize; - } - - long maxWrite = writeOffset + effectiveLength; - if (maxWrite > readOffset) { - throw new IllegalArgumentException("Write range end [" + writeOffset + "-" + maxWrite + "]" - + " overlaps with read range [" + readOffset + "-" + maxRead + "]"); - } else if (maxRead > writeOffset) { - throw new IllegalArgumentException("Read range end [" + readOffset + "-" + maxRead + "]" - + " overlaps with write range [" + writeOffset + "-" + maxWrite + "]"); - } - } - - byte[] copyBuf = new byte[Math.min(IoUtils.DEFAULT_COPY_SIZE, (int) effectiveLength)]; - while (effectiveLength > 0L) { - int remainLength = Math.min(copyBuf.length, (int) effectiveLength); - int readLen = srcHandle.read(copyBuf, 0, remainLength, readOffset); - if (readLen < 0) { - throw new EOFException("Premature EOF while still remaining " + effectiveLength + " bytes"); - } - dstHandle.write(copyBuf, 0, readLen, writeOffset); - - effectiveLength -= readLen; - readOffset += readLen; - writeOffset += readLen; - } - } - - @Override - protected void doReadDir(Buffer buffer, int id) throws IOException { - String handle = buffer.getString(); - Handle h = handles.get(handle); - boolean debugEnabled = log.isDebugEnabled(); - if (debugEnabled) { - log.debug("doReadDir({})[id={}] SSH_FXP_READDIR (handle={}[{}])", - getServerSession(), id, handle, h); - } - - Buffer reply = null; - try { - DirectoryHandle dh = validateHandle(handle, h, DirectoryHandle.class); - if (dh.isDone()) { - sendStatus(BufferUtils.clear(buffer), id, SftpConstants.SSH_FX_EOF, "Directory reading is done"); - return; - } - - Path file = dh.getFile(); - LinkOption[] options = - getPathResolutionLinkOption(SftpConstants.SSH_FXP_READDIR, "", file); - Boolean status = IoUtils.checkFileExists(file, options); - if (status == null) { - throw new AccessDeniedException(file.toString(), file.toString(), "Cannot determine existence of read-dir"); - } - - if (!status) { - throw new NoSuchFileException(file.toString(), file.toString(), "Non-existent directory"); - } else if (!Files.isDirectory(file, options)) { - throw new NotDirectoryException(file.toString()); - } else if (!Files.isReadable(file)) { - throw new AccessDeniedException(file.toString(), file.toString(), "Not readable"); - } - - if (dh.isSendDot() || dh.isSendDotDot() || dh.hasNext()) { - // There is at least one file in the directory or we need to send the "..". - // Send only a few files at a time to not create packets of a too - // large size or have a timeout to occur. - - reply = BufferUtils.clear(buffer); - reply.putByte((byte) SftpConstants.SSH_FXP_NAME); - reply.putInt(id); - - int lenPos = reply.wpos(); - reply.putInt(0); - - ServerSession session = getServerSession(); - int maxDataSize = session.getIntProperty(MAX_READDIR_DATA_SIZE_PROP, DEFAULT_MAX_READDIR_DATA_SIZE); - int count = doReadDir(id, handle, dh, reply, maxDataSize, IoUtils.getLinkOptions(false)); - BufferUtils.updateLengthPlaceholder(reply, lenPos, count); - if ((!dh.isSendDot()) && (!dh.isSendDotDot()) && (!dh.hasNext())) { - dh.markDone(); - } - - Boolean indicator = - SftpHelper.indicateEndOfNamesList(reply, getVersion(), session, dh.isDone()); - if (debugEnabled) { - log.debug("doReadDir({})({})[{}] - seding {} entries - eol={}", session, handle, h, count, indicator); - } - } else { - // empty directory - dh.markDone(); - sendStatus(BufferUtils.clear(buffer), id, SftpConstants.SSH_FX_EOF, "Empty directory"); - return; - } - - Objects.requireNonNull(reply, "No reply buffer created"); - } catch (IOException | RuntimeException e) { - sendStatus(BufferUtils.clear(buffer), id, e, SftpConstants.SSH_FXP_READDIR, handle); - return; - } - - send(reply); - } - - @Override - protected String doOpenDir(int id, String path, Path p, LinkOption... options) throws IOException { - Boolean status = IoUtils.checkFileExists(p, options); - if (status == null) { - throw new AccessDeniedException(p.toString(), p.toString(), "Cannot determine open-dir existence"); - } - - if (!status) { - throw new NoSuchFileException(path, path, "Referenced target directory N/A"); - } else if (!Files.isDirectory(p, options)) { - throw new NotDirectoryException(path); - } else if (!Files.isReadable(p)) { - throw new AccessDeniedException(p.toString(), p.toString(), "Not readable"); - } else { - String handle = generateFileHandle(p); - DirectoryHandle dirHandle = new DirectoryHandle(this, p, handle); - handles.put(handle, dirHandle); - return handle; - } - } - - @Override - protected void doFSetStat(int id, String handle, Map<String, ?> attrs) throws IOException { - Handle h = handles.get(handle); - if (log.isDebugEnabled()) { - log.debug("doFsetStat({})[id={}] SSH_FXP_FSETSTAT (handle={}[{}], attrs={})", - getServerSession(), id, handle, h, attrs); - } - - doSetAttributes(validateHandle(handle, h, Handle.class).getFile(), attrs); - } - - @Override - protected Map<String, Object> doFStat(int id, String handle, int flags) throws IOException { - Handle h = handles.get(handle); - if (log.isDebugEnabled()) { - log.debug("doFStat({})[id={}] SSH_FXP_FSTAT (handle={}[{}], flags=0x{})", - getServerSession(), id, handle, h, Integer.toHexString(flags)); - } - - Handle fileHandle = validateHandle(handle, h, Handle.class); - return resolveFileAttributes(fileHandle.getFile(), flags, IoUtils.getLinkOptions(true)); - } - - @Override - protected void doWrite(int id, String handle, long offset, int length, byte[] data, int doff, int remaining) throws IOException { - Handle h = handles.get(handle); - if (log.isTraceEnabled()) { - log.trace("doWrite({})[id={}] SSH_FXP_WRITE (handle={}[{}], offset={}, data=byte[{}])", - getServerSession(), id, handle, h, offset, length); - } - - FileHandle fh = validateHandle(handle, h, FileHandle.class); - if (length < 0) { - throw new IllegalStateException("Bad length (" + length + ") for writing to " + fh); - } - - if (remaining < length) { - throw new IllegalStateException("Not enough buffer data for writing to " + fh + ": required=" + length + ", available=" + remaining); - } - - SftpEventListener listener = getSftpEventListenerProxy(); - listener.writing(getServerSession(), handle, fh, offset, data, doff, length); - try { - if (fh.isOpenAppend()) { - fh.append(data, doff, length); - } else { - fh.write(data, doff, length, offset); - } - } catch (IOException | RuntimeException e) { - listener.written(getServerSession(), handle, fh, offset, data, doff, length, e); - throw e; - } - listener.written(getServerSession(), handle, fh, offset, data, doff, length, null); - } - - @Override - protected int doRead(int id, String handle, long offset, int length, byte[] data, int doff) throws IOException { - Handle h = handles.get(handle); - if (log.isTraceEnabled()) { - log.trace("doRead({})[id={}] SSH_FXP_READ (handle={}[{}], offset={}, length={})", - getServerSession(), id, handle, h, offset, length); - } - - ValidateUtils.checkTrue(length > 0L, "Invalid read length: %d", length); - FileHandle fh = validateHandle(handle, h, FileHandle.class); - SftpEventListener listener = getSftpEventListenerProxy(); - ServerSession serverSession = getServerSession(); - int readLen; - listener.reading(serverSession, handle, fh, offset, data, doff, length); - try { - readLen = fh.read(data, doff, length, offset); - } catch (IOException | RuntimeException e) { - listener.read(serverSession, handle, fh, offset, data, doff, length, -1, e); - throw e; - } - listener.read(serverSession, handle, fh, offset, data, doff, length, readLen, null); - return readLen; - } - - @Override - protected void doClose(int id, String handle) throws IOException { - Handle h = handles.remove(handle); - if (log.isDebugEnabled()) { - log.debug("doClose({})[id={}] SSH_FXP_CLOSE (handle={}[{}])", - getServerSession(), id, handle, h); - } - validateHandle(handle, h, Handle.class).close(); - - SftpEventListener listener = getSftpEventListenerProxy(); - listener.close(getServerSession(), handle, h); - } - - @Override - protected String doOpen(int id, String path, int pflags, int access, Map<String, Object> attrs) throws IOException { - if (log.isDebugEnabled()) { - log.debug("doOpen({})[id={}] SSH_FXP_OPEN (path={}, access=0x{}, pflags=0x{}, attrs={})", - getServerSession(), id, path, Integer.toHexString(access), Integer.toHexString(pflags), attrs); - } - int curHandleCount = handles.size(); - int maxHandleCount = getServerSession().getIntProperty(MAX_OPEN_HANDLES_PER_SESSION, DEFAULT_MAX_OPEN_HANDLES); - if (curHandleCount > maxHandleCount) { - throw new IllegalStateException("Too many open handles: current=" + curHandleCount + ", max.=" + maxHandleCount); - } - - Path file = resolveFile(path); - String handle = generateFileHandle(file); - FileHandle fileHandle = new FileHandle(this, file, handle, pflags, access, attrs); - handles.put(handle, fileHandle); - return handle; - } - - // we stringify our handles and treat them as such on decoding as well as it is easier to use as a map key - protected String generateFileHandle(Path file) { - // use several rounds in case the file handle size is relatively small so we might get conflicts - for (int index = 0; index < maxFileHandleRounds; index++) { - randomizer.fill(workBuf, 0, fileHandleSize); - String handle = BufferUtils.toHex(workBuf, 0, fileHandleSize, BufferUtils.EMPTY_HEX_SEPARATOR); - if (handles.containsKey(handle)) { - if (log.isTraceEnabled()) { - log.trace("generateFileHandle({})[{}] handle={} in use at round {}", - getServerSession(), file, handle, index); - } - continue; - } - - if (log.isTraceEnabled()) { - log.trace("generateFileHandle({})[{}] {}", getServerSession(), file, handle); - } - return handle; - } - - throw new IllegalStateException("Failed to generate a unique file handle for " + file); - } - - protected void doInit(Buffer buffer, int id) throws IOException { - if (log.isDebugEnabled()) { - log.debug("doInit({})[id={}] SSH_FXP_INIT (version={})", getServerSession(), id, id); - } - - String all = checkVersionCompatibility(buffer, id, id, SftpConstants.SSH_FX_OP_UNSUPPORTED); - if (GenericUtils.isEmpty(all)) { // i.e. validation failed - return; - } - - version = id; - while (buffer.available() > 0) { - String name = buffer.getString(); - byte[] data = buffer.getBytes(); - extensions.put(name, data); - } - - buffer.clear(); - - buffer.putByte((byte) SftpConstants.SSH_FXP_VERSION); - buffer.putInt(version); - appendExtensions(buffer, all); - - SftpEventListener listener = getSftpEventListenerProxy(); - listener.initialized(getServerSession(), version); - - send(buffer); - } - - @Override - protected void send(Buffer buffer) throws IOException { - int len = buffer.available(); - BufferUtils.writeInt(out, len, workBuf, 0, workBuf.length); - out.write(buffer.array(), buffer.rpos(), len); - out.flush(); - } - - @Override - public void destroy() { - if (closed.getAndSet(true)) { - return; // ignore if already closed - } - - ServerSession session = getServerSession(); - boolean debugEnabled = log.isDebugEnabled(); - if (debugEnabled) { - log.debug("destroy({}) - mark as closed", session); - } - - try { - SftpEventListener listener = getSftpEventListenerProxy(); - listener.destroying(session); - } catch (Exception e) { - log.warn("destroy({}) Failed ({}) to announce destruction event: {}", - session, e.getClass().getSimpleName(), e.getMessage()); - if (debugEnabled) { - log.debug("destroy(" + session + ") destruction announcement failure details", e); - } - } - - // if thread has not completed, cancel it - if ((pendingFuture != null) && (!pendingFuture.isDone())) { - boolean result = pendingFuture.cancel(true); - // TODO consider waiting some reasonable (?) amount of time for cancellation - if (debugEnabled) { - log.debug("destroy(" + session + ") - cancel pending future=" + result); - } - } - - pendingFuture = null; - - ExecutorService executors = getExecutorService(); - if ((executors != null) && (!executors.isShutdown()) && isShutdownOnExit()) { - Collection<Runnable> runners = executors.shutdownNow(); - if (debugEnabled) { - log.debug("destroy(" + session + ") - shutdown executor service - runners count=" + runners.size()); - } - } - this.executorService = null; - - try { - fileSystem.close(); - } catch (UnsupportedOperationException e) { - if (debugEnabled) { - log.debug("destroy(" + session + ") closing the file system is not supported"); - } - } catch (IOException e) { - if (debugEnabled) { - log.debug("destroy(" + session + ")" - + " failed (" + e.getClass().getSimpleName() + ")" - + " to close file system: " + e.getMessage(), e); - } - } - } -}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemEnvironment.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemEnvironment.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemEnvironment.java deleted file mode 100644 index 493a450..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemEnvironment.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sshd.server.subsystem.sftp; - -import java.nio.file.Path; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.apache.sshd.common.subsystem.sftp.SftpConstants; -import org.apache.sshd.server.session.ServerSessionHolder; - -/** - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public interface SftpSubsystemEnvironment extends ServerSessionHolder { - /** - * Force the use of a given sftp version - */ - String SFTP_VERSION = "sftp-version"; - - int LOWER_SFTP_IMPL = SftpConstants.SFTP_V3; // Working implementation from v3 - - int HIGHER_SFTP_IMPL = SftpConstants.SFTP_V6; // .. up to and including - - String ALL_SFTP_IMPL = IntStream.rangeClosed(LOWER_SFTP_IMPL, HIGHER_SFTP_IMPL) - .mapToObj(Integer::toString) - .collect(Collectors.joining(",")); - - /** - * @return The negotiated version - */ - int getVersion(); - - /** - * @return The {@link SftpFileSystemAccessor} used to access effective - * server-side paths - */ - SftpFileSystemAccessor getFileSystemAccessor(); - - /** - * @return The selected behavior in case some unsupported attributes are requested - */ - UnsupportedAttributePolicy getUnsupportedAttributePolicy(); - - /** - * @return The default root directory used to resolve relative paths - * - a.k.a. the {@code chroot} location - */ - Path getDefaultDirectory(); -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactory.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactory.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactory.java deleted file mode 100644 index 4e4aa77..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactory.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sshd.server.subsystem.sftp; - -import java.util.Objects; -import java.util.concurrent.ExecutorService; - -import org.apache.sshd.common.subsystem.sftp.SftpConstants; -import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.ObjectBuilder; -import org.apache.sshd.common.util.threads.ExecutorServiceConfigurer; -import org.apache.sshd.server.Command; -import org.apache.sshd.server.subsystem.SubsystemFactory; - -/** - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public class SftpSubsystemFactory - extends AbstractSftpEventListenerManager - implements SubsystemFactory, ExecutorServiceConfigurer, SftpEventListenerManager, SftpFileSystemAccessorManager { - public static final String NAME = SftpConstants.SFTP_SUBSYSTEM_NAME; - public static final UnsupportedAttributePolicy DEFAULT_POLICY = UnsupportedAttributePolicy.Warn; - - public static class Builder extends AbstractSftpEventListenerManager implements ObjectBuilder<SftpSubsystemFactory> { - private ExecutorService executors; - private boolean shutdownExecutor; - private UnsupportedAttributePolicy policy = DEFAULT_POLICY; - private SftpFileSystemAccessor fileSystemAccessor = SftpFileSystemAccessor.DEFAULT; - private SftpErrorStatusDataHandler errorStatusDataHandler = SftpErrorStatusDataHandler.DEFAULT; - - public Builder() { - super(); - } - - public Builder withExecutorService(ExecutorService service) { - executors = service; - return this; - } - - public Builder withShutdownOnExit(boolean shutdown) { - shutdownExecutor = shutdown; - return this; - } - - public Builder withUnsupportedAttributePolicy(UnsupportedAttributePolicy p) { - policy = Objects.requireNonNull(p, "No policy"); - return this; - } - - public Builder withFileSystemAccessor(SftpFileSystemAccessor accessor) { - fileSystemAccessor = Objects.requireNonNull(accessor, "No accessor"); - return this; - } - - public Builder withSftpErrorStatusDataHandler(SftpErrorStatusDataHandler handler) { - errorStatusDataHandler = Objects.requireNonNull(handler, "No error status handler"); - return this; - } - - @Override - public SftpSubsystemFactory build() { - SftpSubsystemFactory factory = new SftpSubsystemFactory(); - factory.setExecutorService(executors); - factory.setShutdownOnExit(shutdownExecutor); - factory.setUnsupportedAttributePolicy(policy); - factory.setFileSystemAccessor(fileSystemAccessor); - factory.setErrorStatusDataHandler(errorStatusDataHandler); - GenericUtils.forEach(getRegisteredListeners(), factory::addSftpEventListener); - return factory; - } - } - - private ExecutorService executors; - private boolean shutdownExecutor; - private UnsupportedAttributePolicy policy = DEFAULT_POLICY; - private SftpFileSystemAccessor fileSystemAccessor = SftpFileSystemAccessor.DEFAULT; - private SftpErrorStatusDataHandler errorStatusDataHandler = SftpErrorStatusDataHandler.DEFAULT; - - public SftpSubsystemFactory() { - super(); - } - - @Override - public String getName() { - return NAME; - } - - @Override - public ExecutorService getExecutorService() { - return executors; - } - - /** - * @param service The {@link ExecutorService} to be used by the {@link SftpSubsystem} - * command when starting execution. If {@code null} then a single-threaded ad-hoc service is used. - */ - @Override - public void setExecutorService(ExecutorService service) { - executors = service; - } - - @Override - public boolean isShutdownOnExit() { - return shutdownExecutor; - } - - /** - * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()} - * will be called when subsystem terminates - unless it is the ad-hoc service, which - * will be shutdown regardless - */ - @Override - public void setShutdownOnExit(boolean shutdownOnExit) { - shutdownExecutor = shutdownOnExit; - } - - public UnsupportedAttributePolicy getUnsupportedAttributePolicy() { - return policy; - } - - /** - * @param p The {@link UnsupportedAttributePolicy} to use if failed to access - * some local file attributes - never {@code null} - */ - public void setUnsupportedAttributePolicy(UnsupportedAttributePolicy p) { - policy = Objects.requireNonNull(p, "No policy"); - } - - @Override - public SftpFileSystemAccessor getFileSystemAccessor() { - return fileSystemAccessor; - } - - @Override - public void setFileSystemAccessor(SftpFileSystemAccessor accessor) { - fileSystemAccessor = Objects.requireNonNull(accessor, "No accessor"); - } - - public SftpErrorStatusDataHandler getErrorStatusDataHandler() { - return errorStatusDataHandler; - } - - public void setErrorStatusDataHandler(SftpErrorStatusDataHandler handler) { - errorStatusDataHandler = Objects.requireNonNull(handler, "No error status data handler provided"); - } - - @Override - public Command create() { - SftpSubsystem subsystem = - new SftpSubsystem(getExecutorService(), isShutdownOnExit(), - getUnsupportedAttributePolicy(), getFileSystemAccessor(), - getErrorStatusDataHandler()); - GenericUtils.forEach(getRegisteredListeners(), subsystem::addSftpEventListener); - return subsystem; - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/UnixDateFormat.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/UnixDateFormat.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/UnixDateFormat.java deleted file mode 100644 index 3ce474a..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/UnixDateFormat.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.sshd.server.subsystem.sftp; - -import java.nio.file.attribute.FileTime; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.GregorianCalendar; -import java.util.List; - -/** - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public final class UnixDateFormat { - - /** - * A {@link List} of <U>short</U> months names where Jan=0, Feb=1, etc. - */ - public static final List<String> MONTHS = - Collections.unmodifiableList(Arrays.asList( - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - )); - - /** - * Six months duration in msec. - */ - public static final long SIX_MONTHS = 183L * 24L * 60L * 60L * 1000L; - - private UnixDateFormat() { - throw new UnsupportedOperationException("No instance allowed"); - } - - /** - * Get unix style date string. - * - * @param time The {@link FileTime} to format - ignored if {@code null} - * @return The formatted date string - * @see #getUnixDate(long) - */ - public static String getUnixDate(FileTime time) { - return getUnixDate((time != null) ? time.toMillis() : -1L); - } - - public static String getUnixDate(long millis) { - if (millis < 0L) { - return "------------"; - } - - StringBuilder sb = new StringBuilder(16); - Calendar cal = new GregorianCalendar(); - cal.setTimeInMillis(millis); - - // month - sb.append(MONTHS.get(cal.get(Calendar.MONTH))); - sb.append(' '); - - // day - int day = cal.get(Calendar.DATE); - if (day < 10) { - sb.append(' '); - } - sb.append(day); - sb.append(' '); - - long nowTime = System.currentTimeMillis(); - if (Math.abs(nowTime - millis) > SIX_MONTHS) { - - // year - int year = cal.get(Calendar.YEAR); - sb.append(' '); - sb.append(year); - } else { - // hour - int hh = cal.get(Calendar.HOUR_OF_DAY); - if (hh < 10) { - sb.append('0'); - } - sb.append(hh); - sb.append(':'); - - // minute - int mm = cal.get(Calendar.MINUTE); - if (mm < 10) { - sb.append('0'); - } - sb.append(mm); - } - - return sb.toString(); - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/UnsupportedAttributePolicy.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/UnsupportedAttributePolicy.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/UnsupportedAttributePolicy.java deleted file mode 100644 index ca763e3..0000000 --- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/UnsupportedAttributePolicy.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sshd.server.subsystem.sftp; - -import java.util.Collections; -import java.util.EnumSet; -import java.util.Set; - -/** - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public enum UnsupportedAttributePolicy { - Ignore, - Warn, - ThrowException; - - public static final Set<UnsupportedAttributePolicy> VALUES = - Collections.unmodifiableSet(EnumSet.allOf(UnsupportedAttributePolicy.class)); -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java index 06c9c75..473b6d5 100644 --- a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java @@ -19,6 +19,7 @@ package org.apache.sshd; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; @@ -27,6 +28,7 @@ import java.lang.reflect.Proxy; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.concurrent.Semaphore; @@ -51,10 +53,13 @@ import org.apache.sshd.common.kex.BuiltinDHFactories; import org.apache.sshd.common.kex.KeyExchange; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.session.SessionListener; -import org.apache.sshd.common.subsystem.sftp.SftpConstants; import org.apache.sshd.common.util.io.NullOutputStream; import org.apache.sshd.common.util.security.SecurityUtils; +import org.apache.sshd.server.Command; +import org.apache.sshd.server.Environment; +import org.apache.sshd.server.ExitCallback; import org.apache.sshd.server.SshServer; +import org.apache.sshd.server.subsystem.SubsystemFactory; import org.apache.sshd.util.test.BaseTestSupport; import org.apache.sshd.util.test.JSchLogger; import org.apache.sshd.util.test.OutputCountTrackingOutputStream; @@ -96,6 +101,7 @@ public class KeyReExchangeTest extends BaseTestSupport { protected void setUp(long bytesLimit, long timeLimit, long packetsLimit) throws Exception { sshd = setupTestServer(); + sshd.setSubsystemFactories(Collections.singletonList(new TestSubsystemFactory())); if (bytesLimit > 0L) { PropertyResolverUtils.updateProperty(sshd, FactoryManager.REKEY_BYTES_LIMIT, bytesLimit); } @@ -126,7 +132,7 @@ public class KeyReExchangeTest extends BaseTestSupport { outputDebugMessage("Request switch to none cipher for %s", session); KeyExchangeFuture switchFuture = session.switchToNoneCipher(); switchFuture.verify(5L, TimeUnit.SECONDS); - try (ClientChannel channel = session.createSubsystemChannel(SftpConstants.SFTP_SUBSYSTEM_NAME)) { + try (ClientChannel channel = session.createSubsystemChannel(TestSubsystemFactory.NAME)) { channel.open().verify(5L, TimeUnit.SECONDS); } } finally { @@ -657,4 +663,47 @@ public class KeyReExchangeTest extends BaseTestSupport { } } } + + private static class TestSubsystemFactory implements SubsystemFactory { + + public static final String NAME = "test-subsystem"; + + @Override + public Command create() { + return new Command() { + private ExitCallback callback; + + @Override + public void setInputStream(InputStream in) { + } + + @Override + public void setOutputStream(OutputStream out) { + } + + @Override + public void setErrorStream(OutputStream err) { + } + + @Override + public void setExitCallback(ExitCallback callback) { + this.callback = callback; + } + + @Override + public void start(Environment env) throws IOException { + } + + @Override + public void destroy() throws Exception { + callback.onExit(0); + } + }; + } + + @Override + public String getName() { + return NAME; + } + } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java index e67df5f..3d03e8e 100644 --- a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java @@ -65,7 +65,6 @@ import org.apache.sshd.client.future.AuthFuture; import org.apache.sshd.client.future.OpenFuture; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.client.subsystem.SubsystemClient; -import org.apache.sshd.client.subsystem.sftp.SftpClient; import org.apache.sshd.common.Factory; import org.apache.sshd.common.FactoryManager; import org.apache.sshd.common.NamedResource; @@ -77,7 +76,6 @@ import org.apache.sshd.common.SshException; import org.apache.sshd.common.channel.Channel; import org.apache.sshd.common.channel.ChannelListener; import org.apache.sshd.common.channel.ChannelListenerManager; -import org.apache.sshd.common.channel.TestChannelListener; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.future.CloseFuture; import org.apache.sshd.common.future.SshFutureListener; @@ -91,7 +89,6 @@ import org.apache.sshd.common.session.ConnectionService; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.session.SessionListener; import org.apache.sshd.common.session.helpers.AbstractSession; -import org.apache.sshd.common.subsystem.sftp.SftpConstants; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.buffer.Buffer; @@ -111,12 +108,12 @@ import org.apache.sshd.server.session.ServerConnectionServiceFactory; import org.apache.sshd.server.session.ServerSession; import org.apache.sshd.server.session.ServerUserAuthService; import org.apache.sshd.server.session.ServerUserAuthServiceFactory; -import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; import org.apache.sshd.util.test.AsyncEchoShellFactory; import org.apache.sshd.util.test.BaseTestSupport; import org.apache.sshd.util.test.EchoShell; import org.apache.sshd.util.test.EchoShellFactory; import org.apache.sshd.util.test.TeeOutputStream; +import org.apache.sshd.util.test.TestChannelListener; import org.junit.After; import org.junit.Before; import org.junit.FixMethodOrder; @@ -453,7 +450,6 @@ public class ClientTest extends BaseTestSupport { assertSame("Mismatched closed channel instances", channel, channelHolder.getAndSet(null)); } }); - sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory())); client.start(); @@ -472,13 +468,6 @@ public class ClientTest extends BaseTestSupport { throw new RuntimeException(e); } }); - testClientListener(channelHolder, SftpClient.class, () -> { - try { - return session.createSftpClient(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); } finally { client.stop(); } @@ -1369,7 +1358,6 @@ public class ClientTest extends BaseTestSupport { try (ClientSession session = createTestClientSession()) { // required since we do not use an SFTP subsystem PropertyResolverUtils.updateProperty(session, ChannelSubsystem.REQUEST_SUBSYSTEM_REPLY, false); - channels.add(session.createChannel(Channel.CHANNEL_SUBSYSTEM, SftpConstants.SFTP_SUBSYSTEM_NAME)); channels.add(session.createChannel(Channel.CHANNEL_EXEC, getCurrentTestName())); channels.add(session.createChannel(Channel.CHANNEL_SHELL, getClass().getSimpleName())); @@ -1392,42 +1380,6 @@ public class ClientTest extends BaseTestSupport { assertNull("Session closure not signalled", clientSessionHolder.get()); } - /** - * Makes sure that the {@link ChannelListener}s added to the client, session - * and channel are <U>cumulative</U> - i.e., all of them invoked - * @throws Exception If failed - */ - @Test - public void testChannelListenersPropagation() throws Exception { - Map<String, TestChannelListener> clientListeners = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - addChannelListener(clientListeners, client, new TestChannelListener(client.getClass().getSimpleName())); - - // required since we do not use an SFTP subsystem - PropertyResolverUtils.updateProperty(client, ChannelSubsystem.REQUEST_SUBSYSTEM_REPLY, false); - client.start(); - try (ClientSession session = createTestClientSession()) { - addChannelListener(clientListeners, session, new TestChannelListener(session.getClass().getSimpleName())); - assertListenerSizes("ClientSessionOpen", clientListeners, 0, 0); - - try (ClientChannel channel = session.createSubsystemChannel(SftpConstants.SFTP_SUBSYSTEM_NAME)) { - channel.open().verify(5L, TimeUnit.SECONDS); - - TestChannelListener channelListener = new TestChannelListener(channel.getClass().getSimpleName()); - // need to emulate them since we are adding the listener AFTER the channel is open - channelListener.channelInitialized(channel); - channelListener.channelOpenSuccess(channel); - channel.addChannelListener(channelListener); - assertListenerSizes("ClientChannelOpen", clientListeners, 1, 1); - } - - assertListenerSizes("ClientChannelClose", clientListeners, 0, 1); - } finally { - client.stop(); - } - - assertListenerSizes("ClientStop", clientListeners, 0, 1); - } - @Test public void testConnectUsingIPv6Address() throws IOException { client.start(); http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSftpClientTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSftpClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSftpClientTest.java deleted file mode 100644 index 6e037b0..0000000 --- a/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSftpClientTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sshd.client.simple; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.EnumSet; - -import org.apache.sshd.client.subsystem.sftp.SftpClient; -import org.apache.sshd.common.file.FileSystemFactory; -import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory; -import org.apache.sshd.common.session.Session; -import org.apache.sshd.common.subsystem.sftp.SftpConstants; -import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.server.scp.ScpCommandFactory; -import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; -import org.apache.sshd.util.test.Utils; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runners.MethodSorters; - -/** - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class SimpleSftpClientTest extends BaseSimpleClientTestSupport { - private final Path targetPath; - private final Path parentPath; - private final FileSystemFactory fileSystemFactory; - - public SimpleSftpClientTest() throws Exception { - targetPath = detectTargetFolder(); - parentPath = targetPath.getParent(); - fileSystemFactory = new VirtualFileSystemFactory(parentPath); - } - - @Override - public void setUp() throws Exception { - super.setUp(); - sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory())); - sshd.setCommandFactory(new ScpCommandFactory()); - sshd.setFileSystemFactory(fileSystemFactory); - client.start(); - } - - @Test - public void testSessionClosedWhenClientClosed() throws Exception { - try (SftpClient sftp = login()) { - assertTrue("SFTP not open", sftp.isOpen()); - - Session session = sftp.getClientSession(); - assertTrue("Session not open", session.isOpen()); - - sftp.close(); - assertFalse("Session not closed", session.isOpen()); - assertFalse("SFTP not closed", sftp.isOpen()); - } - } - - @Test - public void testSftpProxyCalls() throws Exception { - Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName()); - Utils.deleteRecursive(lclSftp); - Path clientFolder = assertHierarchyTargetFolderExists(lclSftp).resolve("client"); - Path clientFile = clientFolder.resolve("file.txt"); - String remoteFileDir = Utils.resolveRelativeRemotePath(parentPath, clientFolder); - String clientFileName = clientFile.getFileName().toString(); - String remoteFilePath = remoteFileDir + "/" + clientFileName; - - try (SftpClient sftp = login()) { - sftp.mkdir(remoteFileDir); - - byte[] written = (getClass().getSimpleName() + "#" + getCurrentTestName() + IoUtils.EOL).getBytes(StandardCharsets.UTF_8); - try (SftpClient.CloseableHandle h = sftp.open(remoteFilePath, EnumSet.of(SftpClient.OpenMode.Write, SftpClient.OpenMode.Create))) { - sftp.write(h, 0L, written); - - SftpClient.Attributes attrs = sftp.stat(h); - assertNotNull("No handle attributes", attrs); - assertEquals("Mismatched remote file size", written.length, attrs.getSize()); - } - - assertTrue("Remote file not created: " + clientFile, Files.exists(clientFile, IoUtils.EMPTY_LINK_OPTIONS)); - byte[] local = Files.readAllBytes(clientFile); - assertArrayEquals("Mismatched remote written data", written, local); - - try (SftpClient.CloseableHandle h = sftp.openDir(remoteFileDir)) { - boolean matchFound = false; - for (SftpClient.DirEntry entry : sftp.listDir(h)) { - String name = entry.getFilename(); - if (clientFileName.equals(name)) { - matchFound = true; - break; - } - } - - assertTrue("No directory entry found for " + clientFileName, matchFound); - } - - sftp.remove(remoteFilePath); - assertFalse("Remote file not removed: " + clientFile, Files.exists(clientFile, IoUtils.EMPTY_LINK_OPTIONS)); - } - } - - private SftpClient login() throws IOException { - return simple.sftpLogin(TEST_LOCALHOST, port, getCurrentTestName(), getCurrentTestName()); - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java deleted file mode 100644 index 57c5998..0000000 --- a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sshd.client.subsystem.sftp; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Collections; - -import org.apache.sshd.client.SshClient; -import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtension; -import org.apache.sshd.common.file.FileSystemFactory; -import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory; -import org.apache.sshd.server.SshServer; -import org.apache.sshd.server.scp.ScpCommandFactory; -import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; -import org.apache.sshd.util.test.BaseTestSupport; -import org.apache.sshd.util.test.JSchLogger; -import org.apache.sshd.util.test.Utils; -import org.junit.AfterClass; -import org.junit.BeforeClass; - -/** - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public abstract class AbstractSftpClientTestSupport extends BaseTestSupport { - protected static SshServer sshd; - protected static int port; - protected static SshClient client; - - protected final FileSystemFactory fileSystemFactory; - - protected AbstractSftpClientTestSupport() throws IOException { - Path targetPath = detectTargetFolder(); - Path parentPath = targetPath.getParent(); - fileSystemFactory = new VirtualFileSystemFactory(parentPath); - } - - @BeforeClass - public static void setupClientAndServer() throws Exception { - JSchLogger.init(); - sshd = Utils.setupTestServer(AbstractSftpClientTestSupport.class); - sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory())); - sshd.setCommandFactory(new ScpCommandFactory()); - sshd.start(); - port = sshd.getPort(); - - client = Utils.setupTestClient(AbstractSftpClientTestSupport.class); - client.start(); - } - - @AfterClass - public static void tearDownClientAndServer() throws Exception { - if (sshd != null) { - try { - sshd.stop(true); - } finally { - sshd = null; - } - } - - if (client != null) { - try { - client.stop(); - } finally { - client = null; - } - } - } - - protected void setupServer() throws Exception { - sshd.setFileSystemFactory(fileSystemFactory); - } - - protected static <E extends SftpClientExtension> E assertExtensionCreated(SftpClient sftp, Class<E> type) { - E instance = sftp.getExtension(type); - assertNotNull("Extension not created: " + type.getSimpleName(), instance); - assertTrue("Extension not supported: " + instance.getName(), instance.isSupported()); - return instance; - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java deleted file mode 100644 index e5265d5..0000000 --- a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sshd.client.subsystem.sftp; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle; -import org.apache.sshd.client.subsystem.sftp.SftpClient.Handle; -import org.apache.sshd.client.subsystem.sftp.impl.DefaultCloseableHandle; -import org.apache.sshd.util.test.BaseTestSupport; -import org.apache.sshd.util.test.NoIoTestCase; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runners.MethodSorters; -import org.mockito.ArgumentMatchers; -import org.mockito.Mockito; - -/** - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Category({ NoIoTestCase.class }) -public class DefaultCloseableHandleTest extends BaseTestSupport { - public DefaultCloseableHandleTest() { - super(); - } - - @Test - public void testChannelBehavior() throws IOException { - final byte[] id = getCurrentTestName().getBytes(StandardCharsets.UTF_8); - SftpClient client = Mockito.mock(SftpClient.class); - Mockito.doAnswer(invocation -> { - Object[] args = invocation.getArguments(); - Handle handle = (Handle) args[0]; - assertArrayEquals("Mismatched closing handle", id, handle.getIdentifier()); - return null; - }).when(client).close(ArgumentMatchers.any(Handle.class)); - - CloseableHandle handle = new DefaultCloseableHandle(client, getCurrentTestName(), id); - try { - assertTrue("Handle not initially open", handle.isOpen()); - } finally { - handle.close(); - } - assertFalse("Handle not marked as closed", handle.isOpen()); - // make sure close was called - Mockito.verify(client).close(handle); - } - - @Test - public void testCloseIdempotent() throws IOException { - SftpClient client = Mockito.mock(SftpClient.class); - final AtomicBoolean closeCalled = new AtomicBoolean(false); - Mockito.doAnswer(invocation -> { - Object[] args = invocation.getArguments(); - assertFalse("Close already called on handle=" + args[0], closeCalled.getAndSet(true)); - return null; - }).when(client).close(ArgumentMatchers.any(Handle.class)); - - CloseableHandle handle = new DefaultCloseableHandle(client, getCurrentTestName(), getCurrentTestName().getBytes(StandardCharsets.UTF_8)); - for (int index = 0; index < Byte.SIZE; index++) { - handle.close(); - } - - assertTrue("Close method not called", closeCalled.get()); - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpCommandMain.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpCommandMain.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpCommandMain.java deleted file mode 100644 index 4b34f92..0000000 --- a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpCommandMain.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sshd.client.subsystem.sftp; - -/** - * Just a test class used to invoke {@link SftpCommand#main(String[])} in - * order to have logging - which is in {@code test} scope - * - * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> - */ -public final class SftpCommandMain { - private SftpCommandMain() { - throw new UnsupportedOperationException("No instance"); - } - - public static void main(String[] args) throws Exception { - SftpCommand.main(args); - } -}