100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair/*
200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * Copyright 2008 the original author or authors.
300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * Licensed under the Apache License, Version 2.0 (the "License");
500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * you may not use this file except in compliance with the License.
600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * You may obtain a copy of the License at
700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *      http://www.apache.org/licenses/LICENSE-2.0
900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
1000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * Unless required by applicable law or agreed to in writing, software
1100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * distributed under the License is distributed on an "AS IS" BASIS,
1200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * See the License for the specific language governing permissions and
1400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * limitations under the License.
1500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair */
1600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairpackage org.mockftpserver.fake;
1700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
1800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport org.mockftpserver.core.command.CommandHandler;
1900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport org.mockftpserver.core.command.CommandNames;
2000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport org.mockftpserver.core.command.ConnectCommandHandler;
2100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport org.mockftpserver.core.command.ReplyTextBundleUtil;
2200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport org.mockftpserver.core.command.UnsupportedCommandHandler;
2300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport org.mockftpserver.core.server.AbstractFtpServer;
2400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport org.mockftpserver.fake.command.*;
2500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport org.mockftpserver.fake.filesystem.FileSystem;
2600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
2700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport java.util.HashMap;
2800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport java.util.List;
2900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairimport java.util.Map;
3000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
3100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair/**
3200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <b>FakeFtpServer</b> is the top-level class for a "fake" implementation of an FTP Server,
3300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * suitable for testing FTP client code or standing in for a live FTP server.
3400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <p/>
3500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <b>FakeFtpServer</b> provides a high-level abstraction for an FTP Server and is suitable
3600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * for most testing and simulation scenarios. You define a filesystem (internal, in-memory) containing
3700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * an arbitrary set of files and directories. These files and directories can (optionally) have
3800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * associated access permissions. You also configure a set of one or more user accounts that
3900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * control which users can login to the FTP server, and their home (default) directories. The
4000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * user account is also used when assigning file and directory ownership for new files.
4100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <p> <b>FakeFtpServer</b> processes FTP client requests and responds with reply codes and
4200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * reply messages consistent with its configuration and the contents of its internal filesystem,
4300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * including file and directory permissions, if they have been configured.
4400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <p/>
4500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <b>FakeFtpServer</b> can be fully configured programmatically or within the
4600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <a href="http://www.springframework.org/">Spring Framework</a> or other dependency-injection container.
4700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <p/>
4800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * In general the steps for setting up and starting the <b>FakeFtpServer</b> are:
4900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <ol>
5000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <li>Create a new <b>FakeFtpServer</b> instance, and optionally set the server control port.</li>
5100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <li>Create and configure a <b>FileSystem</b>, and attach to the <b>FakeFtpServer</b> instance.</li>
5200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <li>Create and configure one or more <b>UserAccount</b> objects and attach to the <b>FakeFtpServer</b> instance.</li>
5300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <li>Start the <b>FakeFtpServer</b> instance.</li>
5400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * </ol>
5500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <h4>Example Code</h4>
5600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <pre><code>
5700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * FakeFtpServer fakeFtpServer = new FakeFtpServer();
5800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
5900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * FileSystem fileSystem = new WindowsFakeFileSystem();
6000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileSystem.add(new DirectoryEntry("c:\\"));
6100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileSystem.add(new DirectoryEntry("c:\\data"));
6200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileSystem.add(new FileEntry("c:\\data\\file1.txt", "abcdef 1234567890"));
6300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileSystem.add(new FileEntry("c:\\data\\run.exe"));
6400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fakeFtpServer.setFileSystem(fileSystem);
6500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
6600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * // Create UserAccount with username, password, home-directory
6700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * UserAccount userAccount = new UserAccount("joe", "joe123", "c:\\");
6800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fakeFtpServer.addUserAccounts(userAccount);
6900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
7000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fakeFtpServer.start();
7100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * </code></pre>
7200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
7300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <h4>Example Code with Permissions</h4>
7400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * You can optionally set the permissions and owner/group for each file and directory, as in the following example.
7500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <pre><code>
7600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * FileSystem fileSystem = new UnixFakeFileSystem();
7700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * DirectoryEntry directoryEntry1 = new DirectoryEntry("/");
7800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * directoryEntry1.setPermissions(new Permissions("rwxrwx---"));
7900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * directoryEntry1.setOwner("joe");
8000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * directoryEntry1.setGroup("dev");
8100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
8200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * DirectoryEntry directoryEntry2 = new DirectoryEntry("/data");
8300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * directoryEntry2.setPermissions(Permissions.ALL);
8400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * directoryEntry2.setOwner("joe");
8500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * directoryEntry2.setGroup("dev");
8600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
8700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * FileEntry fileEntry1 = new FileEntry("/data/file1.txt", "abcdef 1234567890");
8800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileEntry1.setPermissionsFromString("rw-rw-rw-");
8900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileEntry1.setOwner("joe");
9000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileEntry1.setGroup("dev");
9100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
9200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * FileEntry fileEntry2 = new FileEntry("/data/run.exe");
9300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileEntry2.setPermissionsFromString("rwxrwx---");
9400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileEntry2.setOwner("mary");
9500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileEntry2.setGroup("dev");
9600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
9700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileSystem.add(directoryEntry1);
9800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileSystem.add(directoryEntry2);
9900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileSystem.add(fileEntry1);
10000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fileSystem.add(fileEntry2);
10100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
10200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * FakeFtpServer fakeFtpServer = new FakeFtpServer();
10300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fakeFtpServer.setFileSystem(fileSystem);
10400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
10500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * // Create UserAccount with username, password, home-directory
10600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * UserAccount userAccount = new UserAccount("joe", "joe123", "/");
10700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fakeFtpServer.addUserAccounts(userAccount);
10800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
10900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * fakeFtpServer.start();
11000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * </code></pre>
11100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
11200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <h4>FTP Server Control Port</h4>
11300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * By default, <b>FakeFtpServer</b> binds to the server control port of 21. You can use a different server control
11400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * port by setting the <code>serverControlPort</code> property. If you specify a value of <code>0</code>,
11500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * then a free port number will be chosen automatically; call <code>getServerControlPort()</code> AFTER
11600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <code>start()</code> has been called to determine the actual port number being used. Using a non-default
11700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * port number is usually necessary when running on Unix or some other system where that port number is
11800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * already in use or cannot be bound from a user process.
11900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
12000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <h4>Other Configuration</h4>
12100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * The <code>systemName</code> property specifies the value returned by the <code>SYST</code>
12200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * command. Note that this is typically used by an FTP client to determine how to parse
12300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * system-dependent reply text, such as directory listings. This value defaults to <code>"WINDOWS"</code>.
12400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <p/>
12500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * The <code>helpText</code> property specifies a <i>Map</i> of help text replies sent by the
12600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <code>HELP</code> command. The keys in that <i>Map</i> correspond to the command names passed as
12700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * parameters to the <code>HELP</code> command. An entry with the key of an empty string ("") indicates the
12800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * text used as the default help text when no command name parameter is specified for the <code>HELP</code> command.
12900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
13000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * <h4>FTP Command Reply Text ResourceBundle</h4>
13100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * The default text asociated with each FTP command reply code is contained within the
13200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * "ReplyText.properties" ResourceBundle file. You can customize these messages by providing a
13300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * locale-specific ResourceBundle file on the CLASSPATH, according to the normal lookup rules of
13400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * the ResourceBundle class (e.g., "ReplyText_de.properties"). Alternatively, you can
13500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * completely replace the ResourceBundle file by calling the calling the
13600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * {@link #setReplyTextBaseName(String)} method.
13700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair *
13800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * @author Chris Mair
13900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair * @version $Revision$ - $Date$
14000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair */
14100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismairpublic class FakeFtpServer extends AbstractFtpServer implements ServerConfiguration {
14200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
14300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    private FileSystem fileSystem;
14400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    private String systemName = "WINDOWS";
14500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    private String systemStatus = "Connected";
14600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    private Map helpText = new HashMap();
14700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    private Map userAccounts = new HashMap();
14800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
14900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public FileSystem getFileSystem() {
15000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        return fileSystem;
15100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
15200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
15300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public void setFileSystem(FileSystem fileSystem) {
15400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        this.fileSystem = fileSystem;
15500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
15600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
15700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public String getSystemName() {
15800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        return systemName;
15900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
16000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
16100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public void setSystemName(String systemName) {
16200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        this.systemName = systemName;
16300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
16400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
16500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public Map getHelpText() {
16600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        return helpText;
16700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
16800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
16900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public void setHelpText(Map helpText) {
17000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        this.helpText = helpText;
17100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
17200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
17300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public FakeFtpServer() {
17400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.ACCT, new AcctCommandHandler());
17500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.ABOR, new AborCommandHandler());
17600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.ALLO, new AlloCommandHandler());
17700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.APPE, new AppeCommandHandler());
17800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.CWD, new CwdCommandHandler());
17900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.CDUP, new CdupCommandHandler());
18000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.DELE, new DeleCommandHandler());
18100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.EPRT, new EprtCommandHandler());
18200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.EPSV, new EpsvCommandHandler());
18300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.HELP, new HelpCommandHandler());
18400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.LIST, new ListCommandHandler());
18500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.MKD, new MkdCommandHandler());
18600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.MODE, new ModeCommandHandler());
18700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.NLST, new NlstCommandHandler());
18800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.NOOP, new NoopCommandHandler());
18900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.PASS, new PassCommandHandler());
19000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.PASV, new PasvCommandHandler());
19100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.PWD, new PwdCommandHandler());
19200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.PORT, new PortCommandHandler());
19300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.QUIT, new QuitCommandHandler());
19400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.REIN, new ReinCommandHandler());
19500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.REST, new RestCommandHandler());
19600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.RETR, new RetrCommandHandler());
19700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.RMD, new RmdCommandHandler());
19800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.RNFR, new RnfrCommandHandler());
19900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.RNTO, new RntoCommandHandler());
20000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.SITE, new SiteCommandHandler());
20100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.SMNT, new SmntCommandHandler());
20200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.STAT, new StatCommandHandler());
20300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.STOR, new StorCommandHandler());
20400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.STOU, new StouCommandHandler());
20500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.STRU, new StruCommandHandler());
20600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.SYST, new SystCommandHandler());
20700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.TYPE, new TypeCommandHandler());
20800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.USER, new UserCommandHandler());
20900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.XPWD, new PwdCommandHandler());
21000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
21100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        // "Special" Command Handlers
21200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.CONNECT, new ConnectCommandHandler());
21300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        setCommandHandler(CommandNames.UNSUPPORTED, new UnsupportedCommandHandler());
21400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
21500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
21600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    /**
21700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * Initialize a CommandHandler that has been registered to this server.
21800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     *
21900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * If the CommandHandler implements the <code>ServerConfigurationAware</code> interface, then set its
22000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * <code>ServerConfiguration</code> property to <code>this</code>.
22100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     *
22200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * If the CommandHandler implements the <code>ReplyTextBundleAware</code> interface, then set its
22300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * <code>replyTextBundle</code> property using the reply text bundle for this server.
22400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     *
22500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * @param commandHandler - the CommandHandler to initialize
22600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     */
22700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    protected void initializeCommandHandler(CommandHandler commandHandler) {
22800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        if (commandHandler instanceof ServerConfigurationAware) {
22900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair            ServerConfigurationAware sca = (ServerConfigurationAware) commandHandler;
23000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair            sca.setServerConfiguration(this);
23100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        }
23200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
23300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        ReplyTextBundleUtil.setReplyTextBundleIfAppropriate(commandHandler, getReplyTextBundle());
23400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
23500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
23600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    /**
23700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * @return the {@link UserAccount}        configured for this server for the specified user name
23800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     */
23900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public UserAccount getUserAccount(String username) {
24000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        return (UserAccount) userAccounts.get(username);
24100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
24200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
24300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    /**
24400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * Return the help text for a command or the default help text if no command name is specified
24500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     *
24600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * @param name - the command name; may be empty or null to indicate  a request for the default help text
24700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * @return the help text for the named command or the default help text if no name is supplied
24800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     */
24900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public String getHelpText(String name) {
25000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        String key = name == null ? "" : name;
25100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        return (String) helpText.get(key);
25200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
25300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
25400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    /**
25500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * Add a single UserAccount. If an account with the same <code>username</code> already exists,
25600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * it will be replaced.
25700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     *
25800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * @param userAccount - the UserAccount to add
25900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     */
26000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public void addUserAccount(UserAccount userAccount) {
26100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        userAccounts.put(userAccount.getUsername(), userAccount);
26200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
26300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
26400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    /**
26500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * Add the UserAccount objects in the <code>userAccountList</code> to the set of UserAccounts.
26600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     *
26700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * @param userAccountList - the List of UserAccount objects to add
26800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     */
26900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public void setUserAccounts(List userAccountList) {
27000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        for (int i = 0; i < userAccountList.size(); i++) {
27100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair            UserAccount userAccount = (UserAccount) userAccountList.get(i);
27200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair            userAccounts.put(userAccount.getUsername(), userAccount);
27300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        }
27400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
27500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
27600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    /**
27700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * Return the system status description
27800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     *
27900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * @return the system status
28000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     */
28100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public String getSystemStatus() {
28200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        return systemStatus;
28300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
28400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
28500dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    /**
28600dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * Set the system status description text, used by the STAT command handler.
28700dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     *
28800dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     * @param systemStatus - the system status description text
28900dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair     */
29000dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    public void setSystemStatus(String systemStatus) {
29100dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair        this.systemStatus = systemStatus;
29200dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair    }
29300dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair
29400dc7bdcf1df9e86789d963984dfc6912a8854c6chrismair}