1e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair/*
2e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * Copyright 2007 the original author or authors.
3e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair *
4e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * Licensed under the Apache License, Version 2.0 (the "License");
5e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * you may not use this file except in compliance with the License.
6e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * You may obtain a copy of the License at
7e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair *
8e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair *      http://www.apache.org/licenses/LICENSE-2.0
9e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair *
10e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * Unless required by applicable law or agreed to in writing, software
11e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * distributed under the License is distributed on an "AS IS" BASIS,
12e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * See the License for the specific language governing permissions and
14e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * limitations under the License.
15e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair */
16e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairpackage org.mockftpserver.stub.command;
17e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
18e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport java.net.InetAddress;
19e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport java.net.UnknownHostException;
20e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport java.util.Arrays;
21e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
22e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport org.apache.log4j.Logger;
23e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport org.mockftpserver.core.command.Command;
24e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport org.mockftpserver.core.command.CommandHandler;
25e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport org.mockftpserver.core.command.InvocationRecord;
26e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport org.mockftpserver.core.command.ReplyCodes;
27e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport org.mockftpserver.core.session.Session;
28e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport org.mockftpserver.core.util.Assert;
29e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairimport org.mockftpserver.core.util.AssertFailedException;
30e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
31e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair/**
32e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * CommandHandler for the PORT command. Send back a reply code of 200.
33e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * <p>
34e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * Each invocation record stored by this CommandHandler includes the following data element key/values:
35e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * <ul>
36e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair *    <li>{@link #HOST_KEY} ("host") - the client data host (InetAddress) submitted on the invocation (from parameters 1-4)
37e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair *    <li>{@link #PORT_KEY} ("port") - the port number (Integer) submitted on the invocation (from parameter 5-6)
38e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * </ul>
39e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair *
40e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * @version $Revision$ - $Date$
41e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair *
42e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair * @author Chris Mair
43e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair */
44e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismairpublic final class PortCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
45e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
46e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    public static final String HOST_KEY = "host";
47e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    public static final String PORT_KEY = "port";
48e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    private static final Logger LOG = Logger.getLogger(PortCommandHandler.class);
49e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
50e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    /**
51e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * Constructor. Initialize the replyCode.
52e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     */
53e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    public PortCommandHandler() {
54e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        setReplyCode(ReplyCodes.PORT_OK);
55e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    }
56e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
57e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    /**
58e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @throws UnknownHostException
59e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @throws UnknownHostException
60e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
61e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     */
62e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws UnknownHostException {
63e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        InetAddress host = parseHost(command.getParameters());
64e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        int port = parsePortNumber(command.getParameters());
65e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        LOG.debug("host=" + host + " port=" + port);
66e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        session.setClientDataHost(host);
67e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        session.setClientDataPort(port);
68e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        invocationRecord.set(HOST_KEY, host);
69e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        invocationRecord.set(PORT_KEY, new Integer(port));
70e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        sendReply(session);
71e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    }
72e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
73e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    /**
74e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * Parse a 32-bit IP address from the String[] of FTP command parameters.
75e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *
76e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @param parameters - the String[] of command parameters. It is the concatenation
77e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      of a 32-bit internet host address and a 16-bit TCP port address. This address
78e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      information is broken into 8-bit fields and the value of each field is encoded
79e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      as a separate parameter whose value is a decimal number (in character string
80e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      representation).  Thus, the six parameters for the port command would be:
81e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *              h1,h2,h3,h4,p1,p2
82e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      where h1 is the high order 8 bits of the internet host address, and p1 is the
83e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      high order 8 bits of the port number.
84e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *
85e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @return the InetAddres representing the host parsed from the parameters
86e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *
87e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @throws AssertFailedException - if parameters is null or contains an insufficient number of elements
88e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @throws NumberFormatException - if one of the parameters does not contain a parsable integer
89e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @throws UnknownHostException
90e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     */
91e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    static InetAddress parseHost(String[] parameters) throws UnknownHostException {
92e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        verifySufficientParameters(parameters);
93e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
94e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        byte host1 = Byte.parseByte(parameters[0]);
95e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        byte host2 = Byte.parseByte(parameters[1]);
96e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        byte host3 = Byte.parseByte(parameters[2]);
97e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        byte host4 = Byte.parseByte(parameters[3]);
98e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
99e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        byte[] address = { host1, host2, host3, host4 };
100e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        InetAddress inetAddress = InetAddress.getByAddress(address);
101e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
102e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        return inetAddress;
103e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    }
104e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
105e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    /**
106e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * Parse a 16-bit port number from the String[] of FTP command parameters.
107e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *
108e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @param parameters - the String[] of command parameters. It is the concatenation
109e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      of a 32-bit internet host address and a 16-bit TCP port address. This address
110e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      information is broken into 8-bit fields and the value of each field is encoded
111e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      as a separate parameter whose value is a decimal number (in character string
112e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      representation).  Thus, the six parameters for the port command would be:
113e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *              h1,h2,h3,h4,p1,p2
114e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      where h1 is the high order 8 bits of the internet host address, and p1 is the
115e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *      high order 8 bits of the port number.
116e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *
117e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @return the port number parsed from the parameters
118e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     *
119e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @throws AssertFailedException - if parameters is null or contains an insufficient number of elements
120e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @throws NumberFormatException - if one of the parameters does not contain a parsable integer
121e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     */
122e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    static int parsePortNumber(String[] parameters) {
123e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        verifySufficientParameters(parameters);
124e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
125e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        int port1 = Integer.parseInt(parameters[4]);
126e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        int port2 = Integer.parseInt(parameters[5]);
127e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        int port = (port1 << 8) + port2;
128e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
129e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        return port;
130e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    }
131e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
132e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    /**
133e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * Verify that the parameters is not null and contains the required number of elements
134e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @param parameters - the String[] of command parameters
135e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     * @throws AssertFailedException - if parameters is null or contains an insufficient number of elements
136e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair     */
137e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    private static void verifySufficientParameters(String[] parameters) {
138e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        Assert.notNull(parameters, "parameters");
139e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair        Assert.isTrue(parameters.length >= 6, "The PORT command must contain least be 6 parameters: " + Arrays.asList(parameters));
140e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair    }
141e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair
142e47352fb2508e2b25f003b8df12fa79c3215b4b1chrismair}
143