1bda3441225e0607b5ced8b538123fd7c7a417910chrismair/* 2bda3441225e0607b5ced8b538123fd7c7a417910chrismair * Copyright 2008 the original author or authors. 3bda3441225e0607b5ced8b538123fd7c7a417910chrismair * 4bda3441225e0607b5ced8b538123fd7c7a417910chrismair * Licensed under the Apache License, Version 2.0 (the "License"); 5bda3441225e0607b5ced8b538123fd7c7a417910chrismair * you may not use this file except in compliance with the License. 6bda3441225e0607b5ced8b538123fd7c7a417910chrismair * You may obtain a copy of the License at 7bda3441225e0607b5ced8b538123fd7c7a417910chrismair * 8bda3441225e0607b5ced8b538123fd7c7a417910chrismair * http://www.apache.org/licenses/LICENSE-2.0 9bda3441225e0607b5ced8b538123fd7c7a417910chrismair * 10bda3441225e0607b5ced8b538123fd7c7a417910chrismair * Unless required by applicable law or agreed to in writing, software 11bda3441225e0607b5ced8b538123fd7c7a417910chrismair * distributed under the License is distributed on an "AS IS" BASIS, 12bda3441225e0607b5ced8b538123fd7c7a417910chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bda3441225e0607b5ced8b538123fd7c7a417910chrismair * See the License for the specific language governing permissions and 14bda3441225e0607b5ced8b538123fd7c7a417910chrismair * limitations under the License. 15bda3441225e0607b5ced8b538123fd7c7a417910chrismair */ 16bda3441225e0607b5ced8b538123fd7c7a417910chrismairpackage org.mockftpserver.fake.command; 17bda3441225e0607b5ced8b538123fd7c7a417910chrismair 18bda3441225e0607b5ced8b538123fd7c7a417910chrismairimport org.mockftpserver.core.command.Command; 19bda3441225e0607b5ced8b538123fd7c7a417910chrismairimport org.mockftpserver.core.command.ReplyCodes; 20bda3441225e0607b5ced8b538123fd7c7a417910chrismairimport org.mockftpserver.core.session.Session; 21bda3441225e0607b5ced8b538123fd7c7a417910chrismairimport org.mockftpserver.fake.filesystem.FileEntry; 22bda3441225e0607b5ced8b538123fd7c7a417910chrismairimport org.mockftpserver.fake.filesystem.FileSystemException; 23bda3441225e0607b5ced8b538123fd7c7a417910chrismair 24bda3441225e0607b5ced8b538123fd7c7a417910chrismairimport java.io.IOException; 25bda3441225e0607b5ced8b538123fd7c7a417910chrismairimport java.io.OutputStream; 26bda3441225e0607b5ced8b538123fd7c7a417910chrismair 27bda3441225e0607b5ced8b538123fd7c7a417910chrismair/** 28bda3441225e0607b5ced8b538123fd7c7a417910chrismair * Abstract superclass for CommandHandlers that that store a file (STOR, STOU, APPE). Handler logic: 29bda3441225e0607b5ced8b538123fd7c7a417910chrismair * <ol> 30bda3441225e0607b5ced8b538123fd7c7a417910chrismair * <li>If the user has not logged in, then reply with 530 and terminate</li> 31bda3441225e0607b5ced8b538123fd7c7a417910chrismair * <li>If the pathname parameter is required but missing, then reply with 501 and terminate</li> 32bda3441225e0607b5ced8b538123fd7c7a417910chrismair * <li>If the required pathname parameter does not specify a valid filename, then reply with 553 and terminate</li> 33bda3441225e0607b5ced8b538123fd7c7a417910chrismair * <li>If the current user does not have write access to the named file, if it already exists, or else to its 34bda3441225e0607b5ced8b538123fd7c7a417910chrismair * parent directory, then reply with 553 and terminate</li> 35bda3441225e0607b5ced8b538123fd7c7a417910chrismair * <li>If the current user does not have execute access to the parent directory, then reply with 553 and terminate</li> 36bda3441225e0607b5ced8b538123fd7c7a417910chrismair * <li>Send an initial reply of 150</li> 37bda3441225e0607b5ced8b538123fd7c7a417910chrismair * <li>Read all available bytes from the data connection and store/append to the named file in the server file system</li> 38bda3441225e0607b5ced8b538123fd7c7a417910chrismair * <li>If file write/store fails, then reply with 553 and terminate</li> 39bda3441225e0607b5ced8b538123fd7c7a417910chrismair * <li>Send a final reply with 226</li> 40bda3441225e0607b5ced8b538123fd7c7a417910chrismair * </ol> 41bda3441225e0607b5ced8b538123fd7c7a417910chrismair * 42bda3441225e0607b5ced8b538123fd7c7a417910chrismair * @author Chris Mair 43bda3441225e0607b5ced8b538123fd7c7a417910chrismair * @version $Revision$ - $Date$ 44bda3441225e0607b5ced8b538123fd7c7a417910chrismair */ 45bda3441225e0607b5ced8b538123fd7c7a417910chrismairpublic abstract class AbstractStoreFileCommandHandler extends AbstractFakeCommandHandler { 46bda3441225e0607b5ced8b538123fd7c7a417910chrismair 47bda3441225e0607b5ced8b538123fd7c7a417910chrismair protected void handle(Command command, Session session) { 48bda3441225e0607b5ced8b538123fd7c7a417910chrismair verifyLoggedIn(session); 49bda3441225e0607b5ced8b538123fd7c7a417910chrismair this.replyCodeForFileSystemException = ReplyCodes.WRITE_FILE_ERROR; 50bda3441225e0607b5ced8b538123fd7c7a417910chrismair 51bda3441225e0607b5ced8b538123fd7c7a417910chrismair String filename = getOutputFile(command); 52bda3441225e0607b5ced8b538123fd7c7a417910chrismair String path = getRealPath(session, filename); 53bda3441225e0607b5ced8b538123fd7c7a417910chrismair verifyFileSystemCondition(!getFileSystem().isDirectory(path), path, "filesystem.isDirectory"); 54bda3441225e0607b5ced8b538123fd7c7a417910chrismair String parentPath = getFileSystem().getParent(path); 55bda3441225e0607b5ced8b538123fd7c7a417910chrismair verifyFileSystemCondition(getFileSystem().isDirectory(parentPath), parentPath, "filesystem.isNotADirectory"); 56bda3441225e0607b5ced8b538123fd7c7a417910chrismair 57bda3441225e0607b5ced8b538123fd7c7a417910chrismair // User must have write permission to the file, if an existing file, or else to the directory if a new file 58bda3441225e0607b5ced8b538123fd7c7a417910chrismair String pathMustBeWritable = getFileSystem().exists(path) ? path : parentPath; 59bda3441225e0607b5ced8b538123fd7c7a417910chrismair verifyWritePermission(session, pathMustBeWritable); 60bda3441225e0607b5ced8b538123fd7c7a417910chrismair 61bda3441225e0607b5ced8b538123fd7c7a417910chrismair // User must have execute permission to the parent directory 62bda3441225e0607b5ced8b538123fd7c7a417910chrismair verifyExecutePermission(session, parentPath); 63bda3441225e0607b5ced8b538123fd7c7a417910chrismair 64bda3441225e0607b5ced8b538123fd7c7a417910chrismair sendReply(session, ReplyCodes.TRANSFER_DATA_INITIAL_OK); 65bda3441225e0607b5ced8b538123fd7c7a417910chrismair 66bda3441225e0607b5ced8b538123fd7c7a417910chrismair session.openDataConnection(); 67bda3441225e0607b5ced8b538123fd7c7a417910chrismair byte[] contents = session.readData(); 68bda3441225e0607b5ced8b538123fd7c7a417910chrismair session.closeDataConnection(); 69bda3441225e0607b5ced8b538123fd7c7a417910chrismair 70bda3441225e0607b5ced8b538123fd7c7a417910chrismair FileEntry file = (FileEntry) getFileSystem().getEntry(path); 71bda3441225e0607b5ced8b538123fd7c7a417910chrismair if (file == null) { 72bda3441225e0607b5ced8b538123fd7c7a417910chrismair file = new FileEntry(path); 73bda3441225e0607b5ced8b538123fd7c7a417910chrismair getFileSystem().add(file); 74bda3441225e0607b5ced8b538123fd7c7a417910chrismair } 75bda3441225e0607b5ced8b538123fd7c7a417910chrismair file.setPermissions(getUserAccount(session).getDefaultPermissionsForNewFile()); 76bda3441225e0607b5ced8b538123fd7c7a417910chrismair 77bda3441225e0607b5ced8b538123fd7c7a417910chrismair if (contents != null && contents.length > 0) { 78bda3441225e0607b5ced8b538123fd7c7a417910chrismair OutputStream out = file.createOutputStream(appendToOutputFile()); 79bda3441225e0607b5ced8b538123fd7c7a417910chrismair try { 80bda3441225e0607b5ced8b538123fd7c7a417910chrismair out.write(contents); 81bda3441225e0607b5ced8b538123fd7c7a417910chrismair } 82bda3441225e0607b5ced8b538123fd7c7a417910chrismair catch (IOException e) { 83bda3441225e0607b5ced8b538123fd7c7a417910chrismair LOG.error("Error writing to file [" + file.getPath() + "]", e); 84bda3441225e0607b5ced8b538123fd7c7a417910chrismair throw new FileSystemException(file.getPath(), null, e); 85bda3441225e0607b5ced8b538123fd7c7a417910chrismair } 86bda3441225e0607b5ced8b538123fd7c7a417910chrismair finally { 87bda3441225e0607b5ced8b538123fd7c7a417910chrismair try { 88bda3441225e0607b5ced8b538123fd7c7a417910chrismair out.close(); 89bda3441225e0607b5ced8b538123fd7c7a417910chrismair } catch (IOException e) { 90bda3441225e0607b5ced8b538123fd7c7a417910chrismair LOG.error("Error closing OutputStream for file [" + file.getPath() + "]", e); 91bda3441225e0607b5ced8b538123fd7c7a417910chrismair } 92bda3441225e0607b5ced8b538123fd7c7a417910chrismair } 93bda3441225e0607b5ced8b538123fd7c7a417910chrismair } 94bda3441225e0607b5ced8b538123fd7c7a417910chrismair sendReply(session, ReplyCodes.TRANSFER_DATA_FINAL_OK, getMessageKey(), list(filename)); 95bda3441225e0607b5ced8b538123fd7c7a417910chrismair } 96bda3441225e0607b5ced8b538123fd7c7a417910chrismair 97bda3441225e0607b5ced8b538123fd7c7a417910chrismair /** 98bda3441225e0607b5ced8b538123fd7c7a417910chrismair * Return the path (absolute or relative) for the output file. The default behavior is to return 99bda3441225e0607b5ced8b538123fd7c7a417910chrismair * the required first parameter for the specified Command. Subclasses may override the default behavior. 100bda3441225e0607b5ced8b538123fd7c7a417910chrismair * 101bda3441225e0607b5ced8b538123fd7c7a417910chrismair * @param command - the Command 102bda3441225e0607b5ced8b538123fd7c7a417910chrismair * @return the output file name 103bda3441225e0607b5ced8b538123fd7c7a417910chrismair */ 104bda3441225e0607b5ced8b538123fd7c7a417910chrismair protected String getOutputFile(Command command) { 105bda3441225e0607b5ced8b538123fd7c7a417910chrismair return command.getRequiredParameter(0); 106bda3441225e0607b5ced8b538123fd7c7a417910chrismair } 107bda3441225e0607b5ced8b538123fd7c7a417910chrismair 108bda3441225e0607b5ced8b538123fd7c7a417910chrismair /** 109bda3441225e0607b5ced8b538123fd7c7a417910chrismair * @return true if this command should append the transferred contents to the output file; false means 110bda3441225e0607b5ced8b538123fd7c7a417910chrismair * overwrite an existing file. This default implentation returns false. 111bda3441225e0607b5ced8b538123fd7c7a417910chrismair */ 112bda3441225e0607b5ced8b538123fd7c7a417910chrismair protected boolean appendToOutputFile() { 113bda3441225e0607b5ced8b538123fd7c7a417910chrismair return false; 114bda3441225e0607b5ced8b538123fd7c7a417910chrismair } 115bda3441225e0607b5ced8b538123fd7c7a417910chrismair 116bda3441225e0607b5ced8b538123fd7c7a417910chrismair /** 117bda3441225e0607b5ced8b538123fd7c7a417910chrismair * @return the message key for the reply message sent with the final (226) reply 118bda3441225e0607b5ced8b538123fd7c7a417910chrismair */ 119bda3441225e0607b5ced8b538123fd7c7a417910chrismair protected abstract String getMessageKey(); 120bda3441225e0607b5ced8b538123fd7c7a417910chrismair 121bda3441225e0607b5ced8b538123fd7c7a417910chrismair}