From 5ab035a590a45b605872d3cfc6c11bc917eb9684 Mon Sep 17 00:00:00 2001 From: emmanuel lecharny Date: Sat, 11 Jan 2025 00:14:18 +0100 Subject: [PATCH] o Minor code refactoring o Fixed javadoc warnings o Renamed some variables for clarity o Slight code improvement o Added a toString method to the Default%essageResource class o Bumped up a couple of dependencies --- .../command/CommandFactoryFactory.java | 4 +- .../apache/ftpserver/command/impl/PASS.java | 89 +++++++------------ .../nativefs/impl/NativeFileSystemView.java | 22 +++-- .../ftpserver/impl/DefaultFtpHandler.java | 88 ++++++++++-------- .../ftpserver/impl/DefaultFtpRequest.java | 23 ++--- .../ftpserver/impl/DefaultFtpStatistics.java | 83 +++++++++-------- .../listener/nio/FtpHandlerAdapter.java | 21 +++++ .../listener/nio/FtpLoggingFilter.java | 20 ++--- .../listener/nio/FtpResponseEncoder.java | 3 +- .../message/impl/DefaultMessageResource.java | 67 ++++++++++++++ .../PropertiesUserManagerFactory.java | 7 +- .../apache/ftpserver/util/EncryptUtils.java | 4 +- pom.xml | 4 +- 13 files changed, 262 insertions(+), 173 deletions(-) diff --git a/core/src/main/java/org/apache/ftpserver/command/CommandFactoryFactory.java b/core/src/main/java/org/apache/ftpserver/command/CommandFactoryFactory.java index 540de8c1..5e7af9d8 100644 --- a/core/src/main/java/org/apache/ftpserver/command/CommandFactoryFactory.java +++ b/core/src/main/java/org/apache/ftpserver/command/CommandFactoryFactory.java @@ -81,7 +81,6 @@ * @author Apache MINA Project */ public class CommandFactoryFactory { - private static final HashMap DEFAULT_COMMAND_MAP = new HashMap<>(); static { @@ -116,6 +115,7 @@ public class CommandFactoryFactory { DEFAULT_COMMAND_MAP.put("MDTM", new MDTM()); // rfc3659, 3 // "MFCT, draft-somers-ftp-mfxx, 4 // "MFF, draft-somers-ftp-mfxx, 5 + // "MIC, rfc2228, 3? DEFAULT_COMMAND_MAP.put("MFMT", new MFMT()); // draft-somers-ftp-mfxx, 3 DEFAULT_COMMAND_MAP.put("MKD", new MKD()); // rfc959, 4.1.3 DEFAULT_COMMAND_MAP.put("MLSD", new MLSD()); // rfc3659, 7 @@ -166,6 +166,7 @@ public class CommandFactoryFactory { /** * Create an {@link CommandFactory} based on the configuration on the factory. + * * @return The {@link CommandFactory} */ public CommandFactory createCommandFactory() { @@ -210,6 +211,7 @@ public Map getCommandMap() { /** * Add or override a command. + * * @param commandName The command name, e.g. STOR * @param command The command */ diff --git a/core/src/main/java/org/apache/ftpserver/command/impl/PASS.java b/core/src/main/java/org/apache/ftpserver/command/impl/PASS.java index a2ca32fd..c8395a8b 100644 --- a/core/src/main/java/org/apache/ftpserver/command/impl/PASS.java +++ b/core/src/main/java/org/apache/ftpserver/command/impl/PASS.java @@ -53,7 +53,6 @@ * @author Apache MINA Project */ public class PASS extends AbstractCommand { - private final Logger LOG = LoggerFactory.getLogger(PASS.class); /** @@ -64,46 +63,41 @@ public class PASS extends AbstractCommand { public void execute(final FtpIoSession session, final FtpServerContext context, final FtpRequest request) throws IOException, FtpException { - boolean success = false; + ServerFtpStatistics stat = (ServerFtpStatistics) context .getFtpStatistics(); - ServerFtpStatistics stat = (ServerFtpStatistics) context - .getFtpStatistics(); try { - // reset state variables session.resetState(); // argument check String password = request.getArgument(); - // check user name String userName = session.getUserArgument(); - if (userName == null && session.getUser() == null) { + if ((userName == null) && (session.getUser() == null)) { session.write(LocalizedFtpReply.translate(session, request, context, - FtpReply.REPLY_503_BAD_SEQUENCE_OF_COMMANDS, "PASS", - null)); + FtpReply.REPLY_503_BAD_SEQUENCE_OF_COMMANDS, "PASS", null)); + return; } // already logged-in if (session.isLoggedIn()) { session.write(LocalizedFtpReply.translate(session, request, context, - FtpReply.REPLY_202_COMMAND_NOT_IMPLEMENTED, "PASS", - null)); + FtpReply.REPLY_202_COMMAND_NOT_IMPLEMENTED, "PASS", null)); + return; } // anonymous login limit check + boolean anonymous = UserManager.ANONYMOUS.equals(userName); - boolean anonymous = userName != null - && userName.equals("anonymous"); if (anonymous) { int currAnonLogin = stat.getCurrentAnonymousLoginNumber(); - int maxAnonLogin = context.getConnectionConfig() - .getMaxAnonymousLogins(); + int maxAnonLogin = context.getConnectionConfig().getMaxAnonymousLogins(); + if (maxAnonLogin == 0) { LOG.debug("Currently {} anonymous users logged in, unlimited allowed", currAnonLogin); } else { @@ -112,14 +106,10 @@ public void execute(final FtpIoSession session, if (currAnonLogin >= maxAnonLogin) { LOG.debug("Too many anonymous users logged in, user will be disconnected"); - session - .write(LocalizedFtpReply - .translate( - session, - request, - context, + session.write(LocalizedFtpReply.translate(session, request, context, FtpReply.REPLY_421_SERVICE_NOT_AVAILABLE_CLOSING_CONTROL_CONNECTION, "PASS.anonymous", null)); + return; } } @@ -127,43 +117,42 @@ public void execute(final FtpIoSession session, // login limit check int currLogin = stat.getCurrentLoginNumber(); int maxLogin = context.getConnectionConfig().getMaxLogins(); + if (maxLogin == 0) { LOG.debug("Currently {} users logged in, unlimited allowed", currLogin); } else { LOG.debug("Currently {} out of {} users logged in", currLogin, maxLogin); } + if (maxLogin != 0 && currLogin >= maxLogin) { LOG.debug("Too many users logged in, user will be disconnected"); - session - .write(LocalizedFtpReply - .translate( - session, - request, - context, + session.write(LocalizedFtpReply.translate( + session, request, context, FtpReply.REPLY_421_SERVICE_NOT_AVAILABLE_CLOSING_CONTROL_CONNECTION, "PASS.login", null)); + return; } // authenticate user UserManager userManager = context.getUserManager(); User authenticatedUser = null; + try { UserMetadata userMetadata = new UserMetadata(); if (session.getRemoteAddress() instanceof InetSocketAddress) { - userMetadata.setInetAddress(((InetSocketAddress) session - .getRemoteAddress()).getAddress()); + userMetadata.setInetAddress(((InetSocketAddress) session.getRemoteAddress()).getAddress()); } - userMetadata.setCertificateChain(session - .getClientCertificates()); + + userMetadata.setCertificateChain(session.getClientCertificates()); Authentication auth; + if (anonymous) { auth = new AnonymousAuthentication(userMetadata); } else { - auth = new UsernamePasswordAuthentication(userName, - password, userMetadata); + auth = new UsernamePasswordAuthentication(userName, password, userMetadata); } authenticatedUser = userManager.authenticate(auth); @@ -182,14 +171,9 @@ public void execute(final FtpIoSession session, if (authenticatedUser != null) { if (!authenticatedUser.getEnabled()) { - session - .write(LocalizedFtpReply - .translate( - session, - request, - context, - FtpReply.REPLY_530_NOT_LOGGED_IN, - "PASS", null)); + session.write(LocalizedFtpReply.translate( + session, request, context, FtpReply.REPLY_530_NOT_LOGGED_IN, "PASS", null)); + return; } @@ -207,8 +191,7 @@ public void execute(final FtpIoSession session, session.setUserArgument(oldUserArgument); session.setMaxIdleTime(oldMaxIdleTime); - delayAfterLoginFailure(context.getConnectionConfig() - .getLoginFailureDelay()); + delayAfterLoginFailure(context.getConnectionConfig().getLoginFailureDelay()); LOG.warn("Login failure - " + userName); session.write(LocalizedFtpReply.translate(session, request, context, @@ -218,10 +201,9 @@ public void execute(final FtpIoSession session, session.increaseFailedLogins(); // kick the user if the max number of failed logins is reached - int maxAllowedLoginFailues = context.getConnectionConfig() - .getMaxLoginFailures(); - if (maxAllowedLoginFailues != 0 - && session.getFailedLogins() >= maxAllowedLoginFailues) { + int maxAllowedLoginFailues = context.getConnectionConfig().getMaxLoginFailures(); + + if ((maxAllowedLoginFailues != 0) && (session.getFailedLogins() >= maxAllowedLoginFailues)) { LOG.warn("User exceeded the number of allowed failed logins, session will be closed"); session.close(false).awaitUninterruptibly(10000); @@ -232,22 +214,20 @@ public void execute(final FtpIoSession session, // update different objects FileSystemFactory fmanager = context.getFileSystemManager(); - FileSystemView fsview = fmanager - .createFileSystemView(authenticatedUser); + FileSystemView fsview = fmanager.createFileSystemView(authenticatedUser); session.setLogin(fsview); stat.setLogin(session); // everything is fine - send login ok message session.write(LocalizedFtpReply.translate(session, request, context, - FtpReply.REPLY_230_USER_LOGGED_IN, "PASS", userName)); + FtpReply.REPLY_230_USER_LOGGED_IN, "PASS", userName)); + if (anonymous) { - LOG.info("Anonymous login success - " + password); + LOG.info("Anonymous login success"); } else { - LOG.info("Login success - " + userName); + LOG.info("Login success - {}", userName); } - } finally { - // if login failed - reset user if (!success) { session.reinitialize(); @@ -256,7 +236,6 @@ public void execute(final FtpIoSession session, } private void delayAfterLoginFailure(final int loginFailureDelay) { - if (loginFailureDelay > 0) { LOG.debug("Waiting for {} milliseconds due to login failure", loginFailureDelay); diff --git a/core/src/main/java/org/apache/ftpserver/filesystem/nativefs/impl/NativeFileSystemView.java b/core/src/main/java/org/apache/ftpserver/filesystem/nativefs/impl/NativeFileSystemView.java index 227046e3..d14b96a6 100644 --- a/core/src/main/java/org/apache/ftpserver/filesystem/nativefs/impl/NativeFileSystemView.java +++ b/core/src/main/java/org/apache/ftpserver/filesystem/nativefs/impl/NativeFileSystemView.java @@ -39,9 +39,7 @@ * @author Apache MINA Project */ public class NativeFileSystemView implements FileSystemView { - - private final Logger LOG = LoggerFactory - .getLogger(NativeFileSystemView.class); + private final Logger LOG = LoggerFactory.getLogger(NativeFileSystemView.class); // the root directory will always end with '/'. @@ -69,15 +67,15 @@ protected NativeFileSystemView(User user) throws FtpException { * * @param user The current user * @param caseInsensitive If the OS FS is case sensitive or not + * @throws FtpException Actually, never thrown... To be removed! */ - public NativeFileSystemView(User user, boolean caseInsensitive) - throws FtpException { + public NativeFileSystemView(User user, boolean caseInsensitive) throws FtpException { if (user == null) { throw new IllegalArgumentException("user can not be null"); } + if (user.getHomeDirectory() == null) { - throw new IllegalArgumentException( - "User home directory can not be null"); + throw new IllegalArgumentException("User home directory can not be null"); } this.caseInsensitive = caseInsensitive; @@ -90,7 +88,6 @@ public NativeFileSystemView(User user, boolean caseInsensitive) LOG.debug("Native filesystem view created for user \"{}\" with root \"{}\"", user.getName(), rootDir); this.rootDir = rootDir; - this.user = user; currDir = "/"; @@ -111,13 +108,14 @@ public FtpFile getHomeDirectory() { */ public FtpFile getWorkingDirectory() { FtpFile fileObj = null; + if (currDir.equals("/")) { fileObj = new NativeFtpFile("/", new File(rootDir), user); } else { File file = new File(rootDir, currDir.substring(1)); fileObj = new NativeFtpFile(currDir, file, user); - } + return fileObj; } @@ -125,14 +123,13 @@ public FtpFile getWorkingDirectory() { * {@inheritDoc} */ public FtpFile getFile(String file) { - // get actual file object - String physicalName = getPhysicalName(rootDir, - currDir, file, caseInsensitive); + String physicalName = getPhysicalName(rootDir, currDir, file, caseInsensitive); File fileObj = new File(physicalName); // strip the root directory and return String userFileName = physicalName.substring(rootDir.length() - 1); + return new NativeFtpFile(userFileName, fileObj, user); } @@ -304,6 +301,7 @@ private String trimTrailingSlash(String path) { private String normalizeSeparateChar(final String pathName) { String normalizedPathName = pathName.replace(File.separatorChar, '/'); normalizedPathName = normalizedPathName.replace('\\', '/'); + return normalizedPathName; } diff --git a/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpHandler.java b/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpHandler.java index 0f25973a..3afe06bd 100644 --- a/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpHandler.java +++ b/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpHandler.java @@ -44,7 +44,6 @@ * */ public class DefaultFtpHandler implements FtpHandler { - private final Logger LOG = LoggerFactory.getLogger(DefaultFtpHandler.class); private static final String[] NON_AUTHENTICATED_COMMANDS = new String[] { @@ -59,27 +58,34 @@ public void init(final FtpServerContext context, final Listener listener) { this.listener = listener; } + /** + * {@inheritDoc} + */ public void sessionCreated(final FtpIoSession session) throws Exception { session.setListener(listener); - ServerFtpStatistics stats = ((ServerFtpStatistics) context - .getFtpStatistics()); + ServerFtpStatistics stats = ((ServerFtpStatistics) context.getFtpStatistics()); if (stats != null) { stats.setOpenConnection(session); } } + /** + * {@inheritDoc} + */ public void sessionOpened(final FtpIoSession session) throws Exception { FtpletContainer ftplets = context.getFtpletContainer(); FtpletResult ftpletRet; + try { ftpletRet = ftplets.onConnect(session.getFtpletSession()); } catch (Exception e) { LOG.debug("Ftplet threw exception", e); ftpletRet = FtpletResult.DISCONNECT; } + if (ftpletRet == FtpletResult.DISCONNECT) { LOG.debug("Ftplet returned DISCONNECT, session will be closed"); session.close(false).awaitUninterruptibly(10000); @@ -91,8 +97,12 @@ public void sessionOpened(final FtpIoSession session) throws Exception { } } + /** + * {@inheritDoc} + */ public void sessionClosed(final FtpIoSession session) throws Exception { LOG.debug("Closing session"); + try { context.getFtpletContainer().onDisconnect( session.getFtpletSession()); @@ -104,6 +114,7 @@ public void sessionClosed(final FtpIoSession session) throws Exception { // make sure we close the data connection if it happens to be open try { ServerDataConnectionFactory dc = session.getDataConnection(); + if (dc != null) { dc.closeDataConnection(); } @@ -113,6 +124,7 @@ public void sessionClosed(final FtpIoSession session) throws Exception { } FileSystemView fs = session.getFileSystemView(); + if (fs != null) { try { fs.dispose(); @@ -131,12 +143,14 @@ public void sessionClosed(final FtpIoSession session) throws Exception { } else { LOG.warn("Statistics not available in session, can not decrease login and connection count"); } + LOG.debug("Session closed"); } - public void exceptionCaught(final FtpIoSession session, - final Throwable cause) throws Exception { - + /** + * {@inheritDoc} + */ + public void exceptionCaught(final FtpIoSession session, final Throwable cause) throws Exception { if (cause instanceof ProtocolDecoderException && cause.getCause() instanceof MalformedInputException) { // client probably sent something which is not UTF-8 and we failed to @@ -147,33 +161,30 @@ public void exceptionCaught(final FtpIoSession session, session.write(new DefaultFtpReply(FtpReply.REPLY_501_SYNTAX_ERROR_IN_PARAMETERS_OR_ARGUMENTS, "Invalid character in command")); } else if (cause instanceof WriteToClosedSessionException) { - WriteToClosedSessionException writeToClosedSessionException = - (WriteToClosedSessionException) cause; - LOG.warn( - "Client closed connection before all replies could be sent, last reply was {}", - writeToClosedSessionException.getRequest()); + WriteToClosedSessionException writeToClosedSessionException = (WriteToClosedSessionException) cause; + LOG.warn("Client closed connection before all replies could be sent, last reply was {}", + writeToClosedSessionException.getRequest()); session.close(false).awaitUninterruptibly(10000); } else { LOG.error("Exception caught, closing session", cause); session.close(false).awaitUninterruptibly(10000); } - - } private boolean isCommandOkWithoutAuthentication(String command) { - boolean okay = false; for (String allowed : NON_AUTHENTICATED_COMMANDS) { if (allowed.equals(command)) { - okay = true; - break; + return true; } } - return okay; + + return false; } - public void messageReceived(final FtpIoSession session, - final FtpRequest request) throws Exception { + /** + * {@inheritDoc} + */ + public void messageReceived(final FtpIoSession session, final FtpRequest request) throws Exception { try { session.updateLastAccessTime(); @@ -182,30 +193,30 @@ public void messageReceived(final FtpIoSession session, Command command = commandFactory.getCommand(commandName); // make sure the user is authenticated before he issues commands - if (!session.isLoggedIn() - && !isCommandOkWithoutAuthentication(commandName)) { + if (!session.isLoggedIn() && !isCommandOkWithoutAuthentication(commandName)) { session.write(LocalizedFtpReply.translate(session, request, - context, FtpReply.REPLY_530_NOT_LOGGED_IN, - "permission", null)); + context, FtpReply.REPLY_530_NOT_LOGGED_IN, "permission", null)); + return; } FtpletContainer ftplets = context.getFtpletContainer(); FtpletResult ftpletRet; + try { - ftpletRet = ftplets.beforeCommand(session.getFtpletSession(), - request); + ftpletRet = ftplets.beforeCommand(session.getFtpletSession(), request); } catch (Exception e) { LOG.debug("Ftplet container threw exception", e); ftpletRet = FtpletResult.DISCONNECT; } + if (ftpletRet == FtpletResult.DISCONNECT) { LOG.debug("Ftplet returned DISCONNECT, session will be closed"); session.close(false).awaitUninterruptibly(10000); + return; } else if (ftpletRet != FtpletResult.SKIP) { - if (command != null) { synchronized (session) { command.execute(session, context, request); @@ -219,28 +230,28 @@ public void messageReceived(final FtpIoSession session, try { ftpletRet = ftplets.afterCommand( - session.getFtpletSession(), request, session - .getLastReply()); + session.getFtpletSession(), request, session.getLastReply()); } catch (Exception e) { LOG.debug("Ftplet container threw exception", e); ftpletRet = FtpletResult.DISCONNECT; } + if (ftpletRet == FtpletResult.DISCONNECT) { LOG.debug("Ftplet returned DISCONNECT, session will be closed"); session.close(false).awaitUninterruptibly(10000); + return; } } - } catch (Exception ex) { // send error reply - try { + //try { session.write(LocalizedFtpReply.translate(session, request, context, FtpReply.REPLY_550_REQUESTED_ACTION_NOT_TAKEN, null, null)); - } catch (Exception ex1) { - } + // } catch (Exception ex1) { + //} if (ex instanceof java.io.IOException) { throw (IOException) ex; @@ -251,15 +262,18 @@ public void messageReceived(final FtpIoSession session, } - public void sessionIdle(final FtpIoSession session, final IdleStatus status) - throws Exception { + /** + * {@inheritDoc} + */ + public void sessionIdle(final FtpIoSession session, final IdleStatus status) throws Exception { LOG.info("Session idle, closing"); session.close(false).awaitUninterruptibly(10000); } - public void messageSent(final FtpIoSession session, final FtpReply reply) - throws Exception { + /** + * {@inheritDoc} + */ + public void messageSent(final FtpIoSession session, final FtpReply reply) throws Exception { // do nothing - } } diff --git a/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpRequest.java b/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpRequest.java index 5c4d2efd..89e1dbc2 100644 --- a/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpRequest.java +++ b/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpRequest.java @@ -54,23 +54,25 @@ public DefaultFtpRequest(final String requestLine) { //going to be accurate and need to look for an alternative solution. receivedTime = System.currentTimeMillis(); line = requestLine.trim(); - int spInd = line.indexOf(' '); - command = parseCmd(line, spInd); - argument = parseArg(line, spInd); + int spaceIndex = line.indexOf(' '); + command = parseCmd(line, spaceIndex); + argument = parseArg(line, spaceIndex); } /** * Parse the ftp command line. */ - private String parseCmd(final String lineToParse, int spInd) { + private String parseCmd(final String lineToParse, int spaceIndex) { String cmd = null; - if (spInd != -1) { - cmd = line.substring(0, spInd).toUpperCase(); + if (spaceIndex != -1) { + cmd = line.substring(0, spaceIndex); } else { - cmd = line.toUpperCase(); + cmd = line; } + cmd = cmd.toUpperCase(); + if ((cmd.length() > 0) && (cmd.charAt(0) == 'X')) { cmd = cmd.substring(1); } @@ -78,10 +80,11 @@ private String parseCmd(final String lineToParse, int spInd) { return cmd; } - private String parseArg(final String lineToParse, int spInd) { + private String parseArg(final String lineToParse, int spaceIndex) { String arg = null; - if (spInd != -1) { - arg = line.substring(spInd + 1); + + if (spaceIndex != -1) { + arg = line.substring(spaceIndex + 1); if (arg.isEmpty()) { arg = null; diff --git a/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpStatistics.java b/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpStatistics.java index 095c0ec5..98a73739 100644 --- a/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpStatistics.java +++ b/core/src/main/java/org/apache/ftpserver/impl/DefaultFtpStatistics.java @@ -29,6 +29,7 @@ import org.apache.ftpserver.ftplet.FtpFile; import org.apache.ftpserver.ftplet.User; +import org.apache.ftpserver.ftplet.UserManager; /** * Internal class, do not use directly. @@ -41,7 +42,6 @@ * @author Apache MINA Project */ public class DefaultFtpStatistics implements ServerFtpStatistics { - private StatisticsObserver observer = null; private FileObserver fileObserver = null; @@ -87,10 +87,12 @@ private static class UserLogins { public AtomicInteger loginsFromInetAddress(InetAddress address) { AtomicInteger logins = perAddress.get(address); + if (logins == null) { logins = new AtomicInteger(0); perAddress.put(address, logins); } + return logins; } @@ -234,6 +236,7 @@ public int getCurrentAnonymousLoginNumber() { */ public synchronized int getCurrentUserLoginNumber(final User user) { UserLogins userLogins = userLoginTable.get(user.getName()); + if (userLogins == null) {// not found the login user's statistics info return 0; } else { @@ -248,9 +251,9 @@ public synchronized int getCurrentUserLoginNumber(final User user) { * @param ipAddress the ip address of the remote user * @return The login number */ - public synchronized int getCurrentUserLoginNumber(final User user, - final InetAddress ipAddress) { + public synchronized int getCurrentUserLoginNumber(final User user, final InetAddress ipAddress) { UserLogins userLogins = userLoginTable.get(user.getName()); + if (userLogins == null) {// not found the login user's statistics info return 0; } else { @@ -263,8 +266,7 @@ public synchronized int getCurrentUserLoginNumber(final User user, /** * {@inheritDoc} */ - public synchronized void setUpload(final FtpIoSession session, - final FtpFile file, final long size) { + public synchronized void setUpload(final FtpIoSession session, final FtpFile file, final long size) { uploadCount.incrementAndGet(); bytesUpload.addAndGet(size); notifyUpload(session, file, size); @@ -273,8 +275,7 @@ public synchronized void setUpload(final FtpIoSession session, /** * {@inheritDoc} */ - public synchronized void setDownload(final FtpIoSession session, - final FtpFile file, final long size) { + public synchronized void setDownload(final FtpIoSession session, final FtpFile file, final long size) { downloadCount.incrementAndGet(); bytesDownload.addAndGet(size); notifyDownload(session, file, size); @@ -283,8 +284,7 @@ public synchronized void setDownload(final FtpIoSession session, /** * {@inheritDoc} */ - public synchronized void setDelete(final FtpIoSession session, - final FtpFile file) { + public synchronized void setDelete(final FtpIoSession session, final FtpFile file) { deleteCount.incrementAndGet(); notifyDelete(session, file); } @@ -292,8 +292,7 @@ public synchronized void setDelete(final FtpIoSession session, /** * {@inheritDoc} */ - public synchronized void setMkdir(final FtpIoSession session, - final FtpFile file) { + public synchronized void setMkdir(final FtpIoSession session, final FtpFile file) { mkdirCount.incrementAndGet(); notifyMkdir(session, file); } @@ -301,8 +300,7 @@ public synchronized void setMkdir(final FtpIoSession session, /** * {@inheritDoc} */ - public synchronized void setRmdir(final FtpIoSession session, - final FtpFile file) { + public synchronized void setRmdir(final FtpIoSession session, final FtpFile file) { rmdirCount.incrementAndGet(); notifyRmdir(session, file); } @@ -323,6 +321,7 @@ public synchronized void setCloseConnection(final FtpIoSession session) { if (currConnections.get() > 0) { currConnections.decrementAndGet(); } + notifyCloseConnection(session); } @@ -333,7 +332,8 @@ public synchronized void setLogin(final FtpIoSession session) { currLogins.incrementAndGet(); totalLogins.incrementAndGet(); User user = session.getUser(); - if ("anonymous".equals(user.getName())) { + + if (UserManager.ANONYMOUS.equals(user.getName())) { currAnonLogins.incrementAndGet(); totalAnonLogins.incrementAndGet(); } @@ -341,25 +341,25 @@ public synchronized void setLogin(final FtpIoSession session) { synchronized (user) {// thread safety is needed. Since the login occurrs // at low frequency, this overhead is endurable UserLogins statisticsTable = userLoginTable.get(user.getName()); + if (statisticsTable == null) { // the hash table that records the login information of the user // and its ip address. InetAddress address = null; + if (session.getRemoteAddress() instanceof InetSocketAddress) { - address = ((InetSocketAddress) session.getRemoteAddress()) - .getAddress(); + address = ((InetSocketAddress) session.getRemoteAddress()).getAddress(); } + statisticsTable = new UserLogins(address); userLoginTable.put(user.getName(), statisticsTable); } else { statisticsTable.totalLogins.incrementAndGet(); if (session.getRemoteAddress() instanceof InetSocketAddress) { - InetAddress address = ((InetSocketAddress) session - .getRemoteAddress()).getAddress(); - statisticsTable.loginsFromInetAddress(address) - .incrementAndGet(); + InetAddress address = ((InetSocketAddress) session.getRemoteAddress()).getAddress(); + statisticsTable.loginsFromInetAddress(address).incrementAndGet(); } } @@ -381,13 +381,14 @@ public synchronized void setLoginFail(final FtpIoSession session) { */ public synchronized void setLogout(final FtpIoSession session) { User user = session.getUser(); + if (user == null) { return; } currLogins.decrementAndGet(); - if ("anonymous".equals(user.getName())) { + if (UserManager.ANONYMOUS.equals(user.getName())) { currAnonLogins.decrementAndGet(); } @@ -396,9 +397,9 @@ public synchronized void setLogout(final FtpIoSession session) { if (userLogins != null) { userLogins.totalLogins.decrementAndGet(); + if (session.getRemoteAddress() instanceof InetSocketAddress) { - InetAddress address = ((InetSocketAddress) session - .getRemoteAddress()).getAddress(); + InetAddress address = ((InetSocketAddress) session.getRemoteAddress()).getAddress(); userLogins.loginsFromInetAddress(address).decrementAndGet(); } } @@ -413,14 +414,15 @@ public synchronized void setLogout(final FtpIoSession session) { /** * {@inheritDoc} */ - private void notifyUpload(final FtpIoSession session, - final FtpFile file, long size) { + private void notifyUpload(final FtpIoSession session, final FtpFile file, long size) { StatisticsObserver observer = this.observer; + if (observer != null) { observer.notifyUpload(); } FileObserver fileObserver = this.fileObserver; + if (fileObserver != null) { fileObserver.notifyUpload(session, file, size); } @@ -429,14 +431,15 @@ private void notifyUpload(final FtpIoSession session, /** * {@inheritDoc} */ - private void notifyDownload(final FtpIoSession session, - final FtpFile file, final long size) { + private void notifyDownload(final FtpIoSession session, final FtpFile file, final long size) { StatisticsObserver observer = this.observer; + if (observer != null) { observer.notifyDownload(); } FileObserver fileObserver = this.fileObserver; + if (fileObserver != null) { fileObserver.notifyDownload(session, file, size); } @@ -447,11 +450,13 @@ private void notifyDownload(final FtpIoSession session, */ private void notifyDelete(final FtpIoSession session, final FtpFile file) { StatisticsObserver observer = this.observer; + if (observer != null) { observer.notifyDelete(); } FileObserver fileObserver = this.fileObserver; + if (fileObserver != null) { fileObserver.notifyDelete(session, file); } @@ -462,11 +467,13 @@ private void notifyDelete(final FtpIoSession session, final FtpFile file) { */ private void notifyMkdir(final FtpIoSession session, final FtpFile file) { StatisticsObserver observer = this.observer; + if (observer != null) { observer.notifyMkdir(); } FileObserver fileObserver = this.fileObserver; + if (fileObserver != null) { fileObserver.notifyMkdir(session, file); } @@ -477,11 +484,13 @@ private void notifyMkdir(final FtpIoSession session, final FtpFile file) { */ private void notifyRmdir(final FtpIoSession session, final FtpFile file) { StatisticsObserver observer = this.observer; + if (observer != null) { observer.notifyRmdir(); } FileObserver fileObserver = this.fileObserver; + if (fileObserver != null) { fileObserver.notifyRmdir(session, file); } @@ -492,6 +501,7 @@ private void notifyRmdir(final FtpIoSession session, final FtpFile file) { */ private void notifyOpenConnection(final FtpIoSession session) { StatisticsObserver observer = this.observer; + if (observer != null) { observer.notifyOpenConnection(); } @@ -502,6 +512,7 @@ private void notifyOpenConnection(final FtpIoSession session) { */ private void notifyCloseConnection(final FtpIoSession session) { StatisticsObserver observer = this.observer; + if (observer != null) { observer.notifyCloseConnection(); } @@ -512,15 +523,17 @@ private void notifyCloseConnection(final FtpIoSession session) { */ private void notifyLogin(final FtpIoSession session) { StatisticsObserver observer = this.observer; - if (observer != null) { + if (observer != null) { // is anonymous login User user = session.getUser(); boolean anonymous = false; + if (user != null) { String login = user.getName(); - anonymous = (login != null) && login.equals("anonymous"); + anonymous = (login != null) && login.equals(UserManager.ANONYMOUS); } + observer.notifyLogin(anonymous); } } @@ -530,12 +543,9 @@ private void notifyLogin(final FtpIoSession session) { */ private void notifyLoginFail(final FtpIoSession session) { StatisticsObserver observer = this.observer; - if (observer != null) { - if (session.getRemoteAddress() instanceof InetSocketAddress) { - observer.notifyLoginFail(((InetSocketAddress) session - .getRemoteAddress()).getAddress()); - } + if ( (observer != null) && (session.getRemoteAddress() instanceof InetSocketAddress)) { + observer.notifyLoginFail(((InetSocketAddress) session.getRemoteAddress()).getAddress()); } } @@ -544,14 +554,17 @@ private void notifyLoginFail(final FtpIoSession session) { */ private void notifyLogout(final FtpIoSession session) { StatisticsObserver observer = this.observer; + if (observer != null) { // is anonymous login User user = session.getUser(); boolean anonymous = false; + if (user != null) { String login = user.getName(); - anonymous = (login != null) && login.equals("anonymous"); + anonymous = (login != null) && login.equals(UserManager.ANONYMOUS); } + observer.notifyLogout(anonymous); } } diff --git a/core/src/main/java/org/apache/ftpserver/listener/nio/FtpHandlerAdapter.java b/core/src/main/java/org/apache/ftpserver/listener/nio/FtpHandlerAdapter.java index f3358ac8..833b041f 100644 --- a/core/src/main/java/org/apache/ftpserver/listener/nio/FtpHandlerAdapter.java +++ b/core/src/main/java/org/apache/ftpserver/listener/nio/FtpHandlerAdapter.java @@ -48,12 +48,18 @@ public FtpHandlerAdapter(FtpServerContext context, FtpHandler ftpHandler) { this.ftpHandler = ftpHandler; } + /** + * {@inheritDoc} + */ public void exceptionCaught(IoSession session, Throwable cause) throws Exception { FtpIoSession ftpSession = new FtpIoSession(session, context); ftpHandler.exceptionCaught(ftpSession, cause); } + /** + * {@inheritDoc} + */ public void messageReceived(IoSession session, Object message) throws Exception { FtpIoSession ftpSession = new FtpIoSession(session, context); @@ -62,16 +68,25 @@ public void messageReceived(IoSession session, Object message) ftpHandler.messageReceived(ftpSession, request); } + /** + * {@inheritDoc} + */ public void messageSent(IoSession session, Object message) throws Exception { FtpIoSession ftpSession = new FtpIoSession(session, context); ftpHandler.messageSent(ftpSession, (FtpReply) message); } + /** + * {@inheritDoc} + */ public void sessionClosed(IoSession session) throws Exception { FtpIoSession ftpSession = new FtpIoSession(session, context); ftpHandler.sessionClosed(ftpSession); } + /** + * {@inheritDoc} + */ public void sessionCreated(IoSession session) throws Exception { FtpIoSession ftpSession = new FtpIoSession(session, context); MdcInjectionFilter.setProperty(session, "session", ftpSession.getSessionId().toString()); @@ -80,12 +95,18 @@ public void sessionCreated(IoSession session) throws Exception { } + /** + * {@inheritDoc} + */ public void sessionIdle(IoSession session, IdleStatus status) throws Exception { FtpIoSession ftpSession = new FtpIoSession(session, context); ftpHandler.sessionIdle(ftpSession, status); } + /** + * {@inheritDoc} + */ public void sessionOpened(IoSession session) throws Exception { FtpIoSession ftpSession = new FtpIoSession(session, context); ftpHandler.sessionOpened(ftpSession); diff --git a/core/src/main/java/org/apache/ftpserver/listener/nio/FtpLoggingFilter.java b/core/src/main/java/org/apache/ftpserver/listener/nio/FtpLoggingFilter.java index 285da8b5..b8e0f262 100644 --- a/core/src/main/java/org/apache/ftpserver/listener/nio/FtpLoggingFilter.java +++ b/core/src/main/java/org/apache/ftpserver/listener/nio/FtpLoggingFilter.java @@ -32,9 +32,10 @@ * @author Apache MINA Project */ public class FtpLoggingFilter extends LoggingFilter { - + /** A flag telling of the password should be masked in the logs. obvioulsy SET TO TRUE! */ private boolean maskPassword = true; + /** The logger to use */ private final Logger logger; /** @@ -71,23 +72,15 @@ public FtpLoggingFilter(String name) { * {@inheritDoc} */ @Override - public void messageReceived(NextFilter nextFilter, IoSession session, - Object message) throws Exception { + public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception { String request = (String) message; - String logMessage; - if (maskPassword) { - - if (request.trim().toUpperCase().startsWith("PASS ")) { - logMessage = "PASS *****"; - } else { - logMessage = request; - } + if (maskPassword && (request.trim().toUpperCase().startsWith("PASS "))) { + logger.info("RECEIVED: PASS *****"); } else { - logMessage = request; + logger.info("RECEIVED: {}", request); } - logger.info("RECEIVED: {}", logMessage); nextFilter.messageReceived(session, message); } @@ -109,5 +102,4 @@ public boolean isMaskPassword() { public void setMaskPassword(boolean maskPassword) { this.maskPassword = maskPassword; } - } diff --git a/core/src/main/java/org/apache/ftpserver/listener/nio/FtpResponseEncoder.java b/core/src/main/java/org/apache/ftpserver/listener/nio/FtpResponseEncoder.java index 1704a839..4276f8f2 100644 --- a/core/src/main/java/org/apache/ftpserver/listener/nio/FtpResponseEncoder.java +++ b/core/src/main/java/org/apache/ftpserver/listener/nio/FtpResponseEncoder.java @@ -44,8 +44,7 @@ protected CharsetEncoder initialValue() { } }; - public void encode(IoSession session, Object message, - ProtocolEncoderOutput out) throws Exception { + public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception { String value = message.toString(); IoBuffer buf = IoBuffer.allocate(value.length()).setAutoExpand(true); diff --git a/core/src/main/java/org/apache/ftpserver/message/impl/DefaultMessageResource.java b/core/src/main/java/org/apache/ftpserver/message/impl/DefaultMessageResource.java index 49411499..e7ffc5cf 100644 --- a/core/src/main/java/org/apache/ftpserver/message/impl/DefaultMessageResource.java +++ b/core/src/main/java/org/apache/ftpserver/message/impl/DefaultMessageResource.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import org.apache.ftpserver.FtpServerConfigurationException; @@ -264,4 +265,70 @@ public void dispose() { messages.clear(); } + + /** + * A string representation if the MessageResource instance + * + * @return The MessageRessource content + */ + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append("MessageResource:\n"); + + if (languages != null) { + sb.append(" Supported languages:\n"); + + for (String language:languages) { + sb.append(" ").append( language ).append( '\n'); + + PropertiesPair properties = messages.get(language); + + sb.append(" Default properties:\n" ); + + for (Entry entry : properties.defaultProperties.entrySet()) { + sb.append(" <"); + sb.append(entry.getKey()); + sb.append(','); + sb.append(entry.getValue()); + sb.append(">\n"); + } + + sb.append(" Custom properties:\n" ); + + for (Entry entry : properties.customProperties.entrySet()) { + sb.append(" <"); + sb.append(entry.getKey()); + sb.append(','); + sb.append(entry.getValue()); + sb.append(">\n"); + } + } + } else { + sb.append(" No supported language, using the default file\n"); + PropertiesPair properties = messages.get(null); + + sb.append(" Default properties:\n"); + + for (Entry entry : properties.defaultProperties.entrySet()) { + sb.append(" <"); + sb.append(entry.getKey()); + sb.append(','); + sb.append(entry.getValue()); + sb.append(">\n"); + } + + sb.append(" Custom properties:\n"); + + for (Entry entry : properties.customProperties.entrySet()) { + sb.append(" <"); + sb.append(entry.getKey()); + sb.append(','); + sb.append(entry.getValue()); + sb.append(">\n"); + } + } + + return sb.toString(); + } } diff --git a/core/src/main/java/org/apache/ftpserver/usermanager/PropertiesUserManagerFactory.java b/core/src/main/java/org/apache/ftpserver/usermanager/PropertiesUserManagerFactory.java index 918e8917..fb4777f8 100644 --- a/core/src/main/java/org/apache/ftpserver/usermanager/PropertiesUserManagerFactory.java +++ b/core/src/main/java/org/apache/ftpserver/usermanager/PropertiesUserManagerFactory.java @@ -38,8 +38,11 @@ public class PropertiesUserManagerFactory implements UserManagerFactory { private URL userDataURL; - /** The default password encryption method*/ - private PasswordEncryptor passwordEncryptor = new Md5PasswordEncryptor(); + /** + * The default password encryption method. It's a one way mechanism, ie + * a password is *never* unencrypted. + **/ + private PasswordEncryptor passwordEncryptor = new Sha512PasswordEncryptor(); /** * Creates a {@link PropertiesUserManager} instance based on the provided configuration diff --git a/core/src/main/java/org/apache/ftpserver/util/EncryptUtils.java b/core/src/main/java/org/apache/ftpserver/util/EncryptUtils.java index 23390244..d62edee3 100644 --- a/core/src/main/java/org/apache/ftpserver/util/EncryptUtils.java +++ b/core/src/main/java/org/apache/ftpserver/util/EncryptUtils.java @@ -39,7 +39,7 @@ public class EncryptUtils { * * @param source The data to encrypt * @param algorithm The algorithm to use - * @throws NoSuchAlgorithmException If th algorithm does not exist + * @throws NoSuchAlgorithmException If the algorithm does not exist * @return The encrypted data */ public static final byte[] encrypt(byte[] source, String algorithm) throws NoSuchAlgorithmException { @@ -98,7 +98,6 @@ public static final String encryptSHA(String source) { } } - /** * Encrypt string using SHA-256 algorithm * @@ -114,7 +113,6 @@ public static final String encryptSHA256(String source) { } } - /** * Encrypt string using SHA-512 algorithm * diff --git a/pom.xml b/pom.xml index fcb7143f..89a53112 100644 --- a/pom.xml +++ b/pom.xml @@ -151,7 +151,7 @@ - 1.17.1 + 1.17.2 3.11.1 ${project.version} 1.8.0.10 @@ -353,7 +353,7 @@ com.github.siom79.japicmp japicmp-maven-plugin - 0.23.0 + 0.23.1