177391c2a01ce1fed085906743cc240a4d58edd92chrismair/*
277391c2a01ce1fed085906743cc240a4d58edd92chrismair * Copyright 2007 the original author or authors.
377391c2a01ce1fed085906743cc240a4d58edd92chrismair *
477391c2a01ce1fed085906743cc240a4d58edd92chrismair * Licensed under the Apache License, Version 2.0 (the "License");
577391c2a01ce1fed085906743cc240a4d58edd92chrismair * you may not use this file except in compliance with the License.
677391c2a01ce1fed085906743cc240a4d58edd92chrismair * You may obtain a copy of the License at
777391c2a01ce1fed085906743cc240a4d58edd92chrismair *
877391c2a01ce1fed085906743cc240a4d58edd92chrismair *      http://www.apache.org/licenses/LICENSE-2.0
977391c2a01ce1fed085906743cc240a4d58edd92chrismair *
1077391c2a01ce1fed085906743cc240a4d58edd92chrismair * Unless required by applicable law or agreed to in writing, software
1177391c2a01ce1fed085906743cc240a4d58edd92chrismair * distributed under the License is distributed on an "AS IS" BASIS,
1277391c2a01ce1fed085906743cc240a4d58edd92chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1377391c2a01ce1fed085906743cc240a4d58edd92chrismair * See the License for the specific language governing permissions and
1477391c2a01ce1fed085906743cc240a4d58edd92chrismair * limitations under the License.
1577391c2a01ce1fed085906743cc240a4d58edd92chrismair */
1677391c2a01ce1fed085906743cc240a4d58edd92chrismairpackage org.mockftpserver.stub.command;
1777391c2a01ce1fed085906743cc240a4d58edd92chrismair
1877391c2a01ce1fed085906743cc240a4d58edd92chrismairimport java.net.InetAddress;
1977391c2a01ce1fed085906743cc240a4d58edd92chrismairimport java.net.UnknownHostException;
2077391c2a01ce1fed085906743cc240a4d58edd92chrismairimport java.util.Arrays;
2177391c2a01ce1fed085906743cc240a4d58edd92chrismair
2277391c2a01ce1fed085906743cc240a4d58edd92chrismairimport org.apache.log4j.Logger;
2377391c2a01ce1fed085906743cc240a4d58edd92chrismairimport org.mockftpserver.core.command.Command;
2477391c2a01ce1fed085906743cc240a4d58edd92chrismairimport org.mockftpserver.core.command.CommandHandler;
2577391c2a01ce1fed085906743cc240a4d58edd92chrismairimport org.mockftpserver.core.command.InvocationRecord;
2677391c2a01ce1fed085906743cc240a4d58edd92chrismairimport org.mockftpserver.core.command.ReplyCodes;
2777391c2a01ce1fed085906743cc240a4d58edd92chrismairimport org.mockftpserver.core.session.Session;
2877391c2a01ce1fed085906743cc240a4d58edd92chrismairimport org.mockftpserver.core.util.Assert;
2977391c2a01ce1fed085906743cc240a4d58edd92chrismairimport org.mockftpserver.core.util.AssertFailedException;
3077391c2a01ce1fed085906743cc240a4d58edd92chrismair
3177391c2a01ce1fed085906743cc240a4d58edd92chrismair/**
3277391c2a01ce1fed085906743cc240a4d58edd92chrismair * CommandHandler for the PORT command. Send back a reply code of 200.
3377391c2a01ce1fed085906743cc240a4d58edd92chrismair * <p>
3477391c2a01ce1fed085906743cc240a4d58edd92chrismair * Each invocation record stored by this CommandHandler includes the following data element key/values:
3577391c2a01ce1fed085906743cc240a4d58edd92chrismair * <ul>
3677391c2a01ce1fed085906743cc240a4d58edd92chrismair *    <li>{@link #HOST_KEY} ("host") - the client data host (InetAddress) submitted on the invocation (from parameters 1-4)
3777391c2a01ce1fed085906743cc240a4d58edd92chrismair *    <li>{@link #PORT_KEY} ("port") - the port number (Integer) submitted on the invocation (from parameter 5-6)
3877391c2a01ce1fed085906743cc240a4d58edd92chrismair * </ul>
3977391c2a01ce1fed085906743cc240a4d58edd92chrismair *
4077391c2a01ce1fed085906743cc240a4d58edd92chrismair * @version $Revision$ - $Date$
4177391c2a01ce1fed085906743cc240a4d58edd92chrismair *
4277391c2a01ce1fed085906743cc240a4d58edd92chrismair * @author Chris Mair
4377391c2a01ce1fed085906743cc240a4d58edd92chrismair */
4477391c2a01ce1fed085906743cc240a4d58edd92chrismairpublic final class PortCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
4577391c2a01ce1fed085906743cc240a4d58edd92chrismair
4677391c2a01ce1fed085906743cc240a4d58edd92chrismair    public static final String HOST_KEY = "host";
4777391c2a01ce1fed085906743cc240a4d58edd92chrismair    public static final String PORT_KEY = "port";
4877391c2a01ce1fed085906743cc240a4d58edd92chrismair    private static final Logger LOG = Logger.getLogger(PortCommandHandler.class);
4977391c2a01ce1fed085906743cc240a4d58edd92chrismair
5077391c2a01ce1fed085906743cc240a4d58edd92chrismair    /**
5177391c2a01ce1fed085906743cc240a4d58edd92chrismair     * Constructor. Initialize the replyCode.
5277391c2a01ce1fed085906743cc240a4d58edd92chrismair     */
5377391c2a01ce1fed085906743cc240a4d58edd92chrismair    public PortCommandHandler() {
5477391c2a01ce1fed085906743cc240a4d58edd92chrismair        setReplyCode(ReplyCodes.PORT_OK);
5577391c2a01ce1fed085906743cc240a4d58edd92chrismair    }
5677391c2a01ce1fed085906743cc240a4d58edd92chrismair
5777391c2a01ce1fed085906743cc240a4d58edd92chrismair    /**
5877391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @throws UnknownHostException
5977391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @throws UnknownHostException
6077391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
6177391c2a01ce1fed085906743cc240a4d58edd92chrismair     */
6277391c2a01ce1fed085906743cc240a4d58edd92chrismair    public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws UnknownHostException {
6377391c2a01ce1fed085906743cc240a4d58edd92chrismair        InetAddress host = parseHost(command.getParameters());
6477391c2a01ce1fed085906743cc240a4d58edd92chrismair        int port = parsePortNumber(command.getParameters());
6577391c2a01ce1fed085906743cc240a4d58edd92chrismair        LOG.debug("host=" + host + " port=" + port);
6677391c2a01ce1fed085906743cc240a4d58edd92chrismair        session.setClientDataHost(host);
6777391c2a01ce1fed085906743cc240a4d58edd92chrismair        session.setClientDataPort(port);
6877391c2a01ce1fed085906743cc240a4d58edd92chrismair        invocationRecord.set(HOST_KEY, host);
6977391c2a01ce1fed085906743cc240a4d58edd92chrismair        invocationRecord.set(PORT_KEY, new Integer(port));
7077391c2a01ce1fed085906743cc240a4d58edd92chrismair        sendReply(session);
7177391c2a01ce1fed085906743cc240a4d58edd92chrismair    }
7277391c2a01ce1fed085906743cc240a4d58edd92chrismair
7377391c2a01ce1fed085906743cc240a4d58edd92chrismair    /**
7477391c2a01ce1fed085906743cc240a4d58edd92chrismair     * Parse a 32-bit IP address from the String[] of FTP command parameters.
7577391c2a01ce1fed085906743cc240a4d58edd92chrismair     *
7677391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @param parameters - the String[] of command parameters. It is the concatenation
7777391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      of a 32-bit internet host address and a 16-bit TCP port address. This address
7877391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      information is broken into 8-bit fields and the value of each field is encoded
7977391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      as a separate parameter whose value is a decimal number (in character string
8077391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      representation).  Thus, the six parameters for the port command would be:
8177391c2a01ce1fed085906743cc240a4d58edd92chrismair     *              h1,h2,h3,h4,p1,p2
8277391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      where h1 is the high order 8 bits of the internet host address, and p1 is the
8377391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      high order 8 bits of the port number.
8477391c2a01ce1fed085906743cc240a4d58edd92chrismair     *
8577391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @return the InetAddres representing the host parsed from the parameters
8677391c2a01ce1fed085906743cc240a4d58edd92chrismair     *
8777391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @throws AssertFailedException - if parameters is null or contains an insufficient number of elements
8877391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @throws NumberFormatException - if one of the parameters does not contain a parsable integer
8977391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @throws UnknownHostException
9077391c2a01ce1fed085906743cc240a4d58edd92chrismair     */
9177391c2a01ce1fed085906743cc240a4d58edd92chrismair    static InetAddress parseHost(String[] parameters) throws UnknownHostException {
9277391c2a01ce1fed085906743cc240a4d58edd92chrismair        verifySufficientParameters(parameters);
9377391c2a01ce1fed085906743cc240a4d58edd92chrismair
9477391c2a01ce1fed085906743cc240a4d58edd92chrismair        byte host1 = Byte.parseByte(parameters[0]);
9577391c2a01ce1fed085906743cc240a4d58edd92chrismair        byte host2 = Byte.parseByte(parameters[1]);
9677391c2a01ce1fed085906743cc240a4d58edd92chrismair        byte host3 = Byte.parseByte(parameters[2]);
9777391c2a01ce1fed085906743cc240a4d58edd92chrismair        byte host4 = Byte.parseByte(parameters[3]);
9877391c2a01ce1fed085906743cc240a4d58edd92chrismair
9977391c2a01ce1fed085906743cc240a4d58edd92chrismair        byte[] address = { host1, host2, host3, host4 };
10077391c2a01ce1fed085906743cc240a4d58edd92chrismair        InetAddress inetAddress = InetAddress.getByAddress(address);
10177391c2a01ce1fed085906743cc240a4d58edd92chrismair
10277391c2a01ce1fed085906743cc240a4d58edd92chrismair        return inetAddress;
10377391c2a01ce1fed085906743cc240a4d58edd92chrismair    }
10477391c2a01ce1fed085906743cc240a4d58edd92chrismair
10577391c2a01ce1fed085906743cc240a4d58edd92chrismair    /**
10677391c2a01ce1fed085906743cc240a4d58edd92chrismair     * Parse a 16-bit port number from the String[] of FTP command parameters.
10777391c2a01ce1fed085906743cc240a4d58edd92chrismair     *
10877391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @param parameters - the String[] of command parameters. It is the concatenation
10977391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      of a 32-bit internet host address and a 16-bit TCP port address. This address
11077391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      information is broken into 8-bit fields and the value of each field is encoded
11177391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      as a separate parameter whose value is a decimal number (in character string
11277391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      representation).  Thus, the six parameters for the port command would be:
11377391c2a01ce1fed085906743cc240a4d58edd92chrismair     *              h1,h2,h3,h4,p1,p2
11477391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      where h1 is the high order 8 bits of the internet host address, and p1 is the
11577391c2a01ce1fed085906743cc240a4d58edd92chrismair     *      high order 8 bits of the port number.
11677391c2a01ce1fed085906743cc240a4d58edd92chrismair     *
11777391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @return the port number parsed from the parameters
11877391c2a01ce1fed085906743cc240a4d58edd92chrismair     *
11977391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @throws AssertFailedException - if parameters is null or contains an insufficient number of elements
12077391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @throws NumberFormatException - if one of the parameters does not contain a parsable integer
12177391c2a01ce1fed085906743cc240a4d58edd92chrismair     */
12277391c2a01ce1fed085906743cc240a4d58edd92chrismair    static int parsePortNumber(String[] parameters) {
12377391c2a01ce1fed085906743cc240a4d58edd92chrismair        verifySufficientParameters(parameters);
12477391c2a01ce1fed085906743cc240a4d58edd92chrismair
12577391c2a01ce1fed085906743cc240a4d58edd92chrismair        int port1 = Integer.parseInt(parameters[4]);
12677391c2a01ce1fed085906743cc240a4d58edd92chrismair        int port2 = Integer.parseInt(parameters[5]);
12777391c2a01ce1fed085906743cc240a4d58edd92chrismair        int port = (port1 << 8) + port2;
12877391c2a01ce1fed085906743cc240a4d58edd92chrismair
12977391c2a01ce1fed085906743cc240a4d58edd92chrismair        return port;
13077391c2a01ce1fed085906743cc240a4d58edd92chrismair    }
13177391c2a01ce1fed085906743cc240a4d58edd92chrismair
13277391c2a01ce1fed085906743cc240a4d58edd92chrismair    /**
13377391c2a01ce1fed085906743cc240a4d58edd92chrismair     * Verify that the parameters is not null and contains the required number of elements
13477391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @param parameters - the String[] of command parameters
13577391c2a01ce1fed085906743cc240a4d58edd92chrismair     * @throws AssertFailedException - if parameters is null or contains an insufficient number of elements
13677391c2a01ce1fed085906743cc240a4d58edd92chrismair     */
13777391c2a01ce1fed085906743cc240a4d58edd92chrismair    private static void verifySufficientParameters(String[] parameters) {
13877391c2a01ce1fed085906743cc240a4d58edd92chrismair        Assert.notNull(parameters, "parameters");
13977391c2a01ce1fed085906743cc240a4d58edd92chrismair        Assert.isTrue(parameters.length >= 6, "The PORT command must contain least be 6 parameters: " + Arrays.asList(parameters));
14077391c2a01ce1fed085906743cc240a4d58edd92chrismair    }
14177391c2a01ce1fed085906743cc240a4d58edd92chrismair
14277391c2a01ce1fed085906743cc240a4d58edd92chrismair}
143