1a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair/* 2a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Copyright 2008 the original author or authors. 3a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 4a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Licensed under the Apache License, Version 2.0 (the "License"); 5a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * you may not use this file except in compliance with the License. 6a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * You may obtain a copy of the License at 7a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 8a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * http://www.apache.org/licenses/LICENSE-2.0 9a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 10a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Unless required by applicable law or agreed to in writing, software 11a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * distributed under the License is distributed on an "AS IS" BASIS, 12a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * See the License for the specific language governing permissions and 14a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * limitations under the License. 15a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 16a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairpackage org.mockftpserver.fake.command; 17a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 18a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.CommandSyntaxException; 19a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.IllegalStateException; 20a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.NotLoggedInException; 21a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.command.AbstractCommandHandler; 22a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.command.Command; 23a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.command.ReplyCodes; 24a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.session.Session; 25a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.session.SessionKeys; 26a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.util.Assert; 27a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.fake.ServerConfiguration; 28a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.fake.ServerConfigurationAware; 29a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.fake.UserAccount; 30a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.fake.filesystem.FileSystem; 31a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.fake.filesystem.FileSystemEntry; 32a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.fake.filesystem.FileSystemException; 33a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.fake.filesystem.InvalidFilenameException; 34a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 35a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.text.MessageFormat; 36a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.ArrayList; 37a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.Collections; 38a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.List; 39a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.MissingResourceException; 40a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 41a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair/** 42a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Abstract superclass for CommandHandler classes for the "Fake" server. 43a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 44a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @author Chris Mair 45a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @version $Revision$ - $Date$ 46a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 47a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairpublic abstract class AbstractFakeCommandHandler extends AbstractCommandHandler implements ServerConfigurationAware { 48a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 49a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected static final String INTERNAL_ERROR_KEY = "internalError"; 50a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 51a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private ServerConfiguration serverConfiguration; 52a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 53a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 54a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Reply code sent back when a FileSystemException is caught by the {@link #handleCommand(Command, Session)} 55a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * This defaults to ReplyCodes.EXISTING_FILE_ERROR (550). 56a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 57a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected int replyCodeForFileSystemException = ReplyCodes.READ_FILE_ERROR; 58a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 59a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public ServerConfiguration getServerConfiguration() { 60a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return serverConfiguration; 61a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 62a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 63a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public void setServerConfiguration(ServerConfiguration serverConfiguration) { 64a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair this.serverConfiguration = serverConfiguration; 65a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 66a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 67a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 68a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Use template method to centralize and ensure common validation 69a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 70a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public void handleCommand(Command command, Session session) { 71a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(serverConfiguration, "serverConfiguration"); 72a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(command, "command"); 73a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(session, "session"); 74a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 75a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair try { 76a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair handle(command, session); 77a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 78a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair catch (CommandSyntaxException e) { 79a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair handleException(command, session, e, ReplyCodes.COMMAND_SYNTAX_ERROR); 80a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 81a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair catch (IllegalStateException e) { 82a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair handleException(command, session, e, ReplyCodes.ILLEGAL_STATE); 83a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 84a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair catch (NotLoggedInException e) { 85a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair handleException(command, session, e, ReplyCodes.NOT_LOGGED_IN); 86a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 87a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair catch (InvalidFilenameException e) { 88a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair handleFileSystemException(command, session, e, ReplyCodes.FILENAME_NOT_VALID, list(e.getPath())); 89a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 90a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair catch (FileSystemException e) { 91a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair handleFileSystemException(command, session, e, replyCodeForFileSystemException, list(e.getPath())); 92a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 93a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 94a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 95a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 96a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Convenience method to return the FileSystem stored in the ServerConfiguration 97a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 98a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the FileSystem 99a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 100a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected FileSystem getFileSystem() { 101a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return serverConfiguration.getFileSystem(); 102a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 103a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 104a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 105a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Handle the specified command for the session. All checked exceptions are expected to be wrapped or handled 106a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * by the caller. 107a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 108a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param command - the Command to be handled 109a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the session on which the Command was submitted 110a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 111a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected abstract void handle(Command command, Session session); 112a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 113a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // ------------------------------------------------------------------------- 114a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // Utility methods for subclasses 115a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // ------------------------------------------------------------------------- 116a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 117a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 118a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Send a reply for this command on the control connection. 119a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * <p/> 120a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * The reply code is designated by the <code>replyCode</code> property, and the reply text 121a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * is retrieved from the <code>replyText</code> ResourceBundle, using the specified messageKey. 122a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 123a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 124a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param replyCode - the reply code 125a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param messageKey - the resource bundle key for the reply text 126a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if session is null 127a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see MessageFormat 128a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 129a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void sendReply(Session session, int replyCode, String messageKey) { 130a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair sendReply(session, replyCode, messageKey, Collections.EMPTY_LIST); 131a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 132a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 133a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 134a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Send a reply for this command on the control connection. 135a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * <p/> 136a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * The reply code is designated by the <code>replyCode</code> property, and the reply text 137a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * is retrieved from the <code>replyText</code> ResourceBundle, using the specified messageKey. 138a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 139a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 140a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param replyCode - the reply code 141a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param messageKey - the resource bundle key for the reply text 142a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param args - the optional message arguments; defaults to [] 143a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if session is null 144a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see MessageFormat 145a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 146a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void sendReply(Session session, int replyCode, String messageKey, List args) { 147a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(session, "session"); 148a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair assertValidReplyCode(replyCode); 149a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 150a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String text = getTextForKey(messageKey); 151a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String replyText = (args != null && !args.isEmpty()) ? MessageFormat.format(text, args.toArray()) : text; 152a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 153a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String replyTextToLog = (replyText == null) ? "" : " " + replyText; 154a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String argsToLog = (args != null && !args.isEmpty()) ? (" args=" + args) : ""; 155a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair LOG.info("Sending reply [" + replyCode + replyTextToLog + "]" + argsToLog); 156a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair session.sendReply(replyCode, replyText); 157a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 158a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 159a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 160a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Send a reply for this command on the control connection. 161a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * <p/> 162a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * The reply code is designated by the <code>replyCode</code> property, and the reply text 163a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * is retrieved from the <code>replyText</code> ResourceBundle, using the reply code as the key. 164a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 165a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 166a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param replyCode - the reply code 167a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if session is null 168a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see MessageFormat 169a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 170a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void sendReply(Session session, int replyCode) { 171a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair sendReply(session, replyCode, Collections.EMPTY_LIST); 172a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 173a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 174a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 175a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Send a reply for this command on the control connection. 176a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * <p/> 177a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * The reply code is designated by the <code>replyCode</code> property, and the reply text 178a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * is retrieved from the <code>replyText</code> ResourceBundle, using the reply code as the key. 179a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 180a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 181a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param replyCode - the reply code 182a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param args - the optional message arguments; defaults to [] 183a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if session is null 184a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see MessageFormat 185a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 186a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void sendReply(Session session, int replyCode, List args) { 187a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair sendReply(session, replyCode, Integer.toString(replyCode), args); 188a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 189a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 190a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 191a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Handle the exception caught during handleCommand() 192a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 193a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param command - the Command 194a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 195a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param exception - the caught exception 196a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param replyCode - the reply code that should be sent back 197a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 198a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private void handleException(Command command, Session session, Throwable exception, int replyCode) { 199a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair LOG.warn("Error handling command: " + command + "; " + exception, exception); 200a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair sendReply(session, replyCode); 201a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 202a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 203a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 204a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Handle the exception caught during handleCommand() 205a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 206a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param command - the Command 207a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 208a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param exception - the caught exception 209a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param replyCode - the reply code that should be sent back 210a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param arg - the arg for the reply (message) 211a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 212a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private void handleFileSystemException(Command command, Session session, FileSystemException exception, int replyCode, Object arg) { 213a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair LOG.warn("Error handling command: " + command + "; " + exception, exception); 214a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair sendReply(session, replyCode, exception.getMessageKey(), Collections.singletonList(arg)); 215a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 216a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 217a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 218a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the value of the named attribute within the session. 219a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 220a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 221a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param name - the name of the session attribute to retrieve 222a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the value of the named session attribute 223a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws IllegalStateException - if the Session does not contain the named attribute 224a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 225a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected Object getRequiredSessionAttribute(Session session, String name) { 226a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Object value = session.getAttribute(name); 227a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (value == null) { 228a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair throw new IllegalStateException("Session missing required attribute [" + name + "]"); 229a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 230a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return value; 231a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 232a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 233a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 234a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Verify that the current user (if any) has already logged in successfully. 235a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 236a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 237a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 238a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void verifyLoggedIn(Session session) { 239a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (getUserAccount(session) == null) { 240a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair throw new NotLoggedInException("User has not logged in"); 241a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 242a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 243a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 244a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 245a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 246a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the UserAccount stored in the specified session; may be null 247a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 248a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected UserAccount getUserAccount(Session session) { 249a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return (UserAccount) session.getAttribute(SessionKeys.USER_ACCOUNT); 250a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 251a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 252a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 253a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Verify that the specified condition related to the file system is true, 254a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * otherwise throw a FileSystemException. 255a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 256a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param condition - the condition that must be true 257a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path involved in the operation; this will be included in the 258a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * error message if the condition is not true. 259a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param messageKey - the message key for the exception message 260a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws FileSystemException - if the condition is not true 261a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 262a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void verifyFileSystemCondition(boolean condition, String path, String messageKey) { 263a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (!condition) { 264a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair throw new FileSystemException(path, messageKey); 265a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 266a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 267a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 268a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 269a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Verify that the current user has execute permission to the specified path 270a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 271a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 272a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the file system path 273a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws FileSystemException - if the condition is not true 274a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 275a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void verifyExecutePermission(Session session, String path) { 276a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair UserAccount userAccount = getUserAccount(session); 277a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry entry = getFileSystem().getEntry(path); 278a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair verifyFileSystemCondition(userAccount.canExecute(entry), path, "filesystem.cannotExecute"); 279a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 280a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 281a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 282a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Verify that the current user has write permission to the specified path 283a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 284a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 285a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the file system path 286a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws FileSystemException - if the condition is not true 287a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 288a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void verifyWritePermission(Session session, String path) { 289a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair UserAccount userAccount = getUserAccount(session); 290a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry entry = getFileSystem().getEntry(path); 291a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair verifyFileSystemCondition(userAccount.canWrite(entry), path, "filesystem.cannotWrite"); 292a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 293a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 294a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 295a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Verify that the current user has read permission to the specified path 296a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 297a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 298a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the file system path 299a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws FileSystemException - if the condition is not true 300a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 301a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void verifyReadPermission(Session session, String path) { 302a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair UserAccount userAccount = getUserAccount(session); 303a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry entry = getFileSystem().getEntry(path); 304a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair verifyFileSystemCondition(userAccount.canRead(entry), path, "filesystem.cannotRead"); 305a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 306a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 307a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 308a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the full, absolute path for the specified abstract pathname. 309a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * If path is null, return the current directory (stored in the session). If 310a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * path represents an absolute path, then return path as is. Otherwise, path 311a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * is relative, so assemble the full path from the current directory 312a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * and the specified relative path. 313a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 314a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the Session 315a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the abstract pathname; may be null 316a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the resulting full, absolute path 317a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 318a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected String getRealPath(Session session, String path) { 319a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String currentDirectory = (String) session.getAttribute(SessionKeys.CURRENT_DIRECTORY); 320a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (path == null) { 321a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return currentDirectory; 322a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 323a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (getFileSystem().isAbsolute(path)) { 324a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return path; 325a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 326a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return getFileSystem().path(currentDirectory, path); 327a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 328a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 329a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 330a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the end-of-line character(s) used when building multi-line responses 331a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 332a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return "\r\n" 333a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 334a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected String endOfLine() { 335a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return "\r\n"; 336a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 337a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 338a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private String getTextForKey(String key) { 339a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String msgKey = (key != null) ? key : INTERNAL_ERROR_KEY; 340a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair try { 341a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return getReplyTextBundle().getString(msgKey); 342a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 343a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair catch (MissingResourceException e) { 344a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // No reply text is mapped for the specified key 345a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair LOG.warn("No reply text defined for key [" + msgKey + "]"); 346a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return null; 347a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 348a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 349a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 350a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // ------------------------------------------------------------------------- 351a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // Login Support (used by USER and PASS commands) 352a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // ------------------------------------------------------------------------- 353a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 354a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 355a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Validate the UserAccount for the specified username. If valid, return true. If the UserAccount does 356a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * not exist or is invalid, log an error message, send back a reply code of 530 with an appropriate 357a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * error message, and return false. A UserAccount is considered invalid if the homeDirectory property 358a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * is not set or is set to a non-existent directory. 359a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 360a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param username - the username 361a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the session; used to send back an error reply if necessary 362a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true only if the UserAccount for the named user is valid 363a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 364a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected boolean validateUserAccount(String username, Session session) { 365a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair UserAccount userAccount = serverConfiguration.getUserAccount(username); 366a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (userAccount == null || !userAccount.isValid()) { 367a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair LOG.error("UserAccount missing or not valid for username [" + username + "]: " + userAccount); 368a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair sendReply(session, ReplyCodes.USER_ACCOUNT_NOT_VALID, "login.userAccountNotValid", list(username)); 369a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return false; 370a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 371a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 372a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String home = userAccount.getHomeDirectory(); 373a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (!getFileSystem().isDirectory(home)) { 374a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair LOG.error("Home directory configured for username [" + username + "] is not valid: " + home); 375a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair sendReply(session, ReplyCodes.USER_ACCOUNT_NOT_VALID, "login.homeDirectoryNotValid", list(username, home)); 376a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return false; 377a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 378a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 379a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return true; 380a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 381a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 382a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 383a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Log in the specified user for the current session. Send back a reply of 230 with a message indicated 384a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * by the replyMessageKey and set the UserAccount and current directory (homeDirectory) in the session. 385a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 386a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param userAccount - the userAccount for the user to be logged in 387a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param session - the session 388a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param replyCode - the reply code to send 389a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param replyMessageKey - the message key for the reply text 390a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 391a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void login(UserAccount userAccount, Session session, int replyCode, String replyMessageKey) { 392a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair sendReply(session, replyCode, replyMessageKey); 393a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair session.setAttribute(SessionKeys.USER_ACCOUNT, userAccount); 394a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair session.setAttribute(SessionKeys.CURRENT_DIRECTORY, userAccount.getHomeDirectory()); 395a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 396a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 397a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 398a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Convenience method to return a List with the specified single item 399a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 400a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param item - the single item in the returned List 401a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return a new List with that single item 402a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 403a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected List list(Object item) { 404a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return Collections.singletonList(item); 405a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 406a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 407a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 408a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Convenience method to return a List with the specified two items 409a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 410a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param item1 - the first item in the returned List 411a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param item2 - the second item in the returned List 412a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return a new List with the specified items 413a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 414a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected List list(Object item1, Object item2) { 415a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List list = new ArrayList(2); 416a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair list.add(item1); 417a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair list.add(item2); 418a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return list; 419a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 420a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 421a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 422a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return true if the specified string is null or empty 423a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 424a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param string - the String to check; may be null 425a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true only if the specified String is null or empyt 426a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 427a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected boolean notNullOrEmpty(String string) { 428a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return string != null && string.length() > 0; 429a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 430a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 431a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 432a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the string unless it is null or empty, in which case return the defaultString. 433a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 434a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param string - the String to check; may be null 435a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param defaultString - the value to return if string is null or empty 436a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return string if not null and not empty; otherwise return defaultString 437a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 438a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected String defaultIfNullOrEmpty(String string, String defaultString) { 439a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return (notNullOrEmpty(string) ? string : defaultString); 440a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 441a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 442a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair}