001    /*
002     * Copyright 2001-2005 The Apache Software Foundation
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.apache.commons.net.tftp;
017    
018    import java.net.DatagramPacket;
019    import java.net.InetAddress;
020    
021    /***
022     * An abstract class derived from TFTPPacket definiing a TFTP Request
023     * packet type.  It is subclassed by the
024     * {@link org.apache.commons.net.tftp.TFTPReadRequestPacket}
025     *   and
026     * {@link org.apache.commons.net.tftp.TFTPWriteRequestPacket}
027     *  classes.
028     * <p>
029     * Details regarding the TFTP protocol and the format of TFTP packets can
030     * be found in RFC 783.  But the point of these classes is to keep you
031     * from having to worry about the internals.  Additionally, only very
032     * few people should have to care about any of the TFTPPacket classes
033     * or derived classes.  Almost all users should only be concerned with the
034     * {@link org.apache.commons.net.tftp.TFTPClient} class
035     * {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()}
036     * and
037     * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()}
038     * methods.
039     * <p>
040     * <p>
041     * @author Daniel F. Savarese
042     * @see TFTPPacket
043     * @see TFTPReadRequestPacket
044     * @see TFTPWriteRequestPacket
045     * @see TFTPPacketException
046     * @see TFTP
047     ***/
048    
049    public abstract class TFTPRequestPacket extends TFTPPacket
050    {
051        /***
052         * An array containing the string names of the transfer modes and indexed
053         * by the transfer mode constants.
054         ***/
055        static final String[] _modeStrings = { "netascii", "octet" };
056    
057        /***
058         * A null terminated byte array representation of the ascii names of the
059         * transfer mode constants.  This is convenient for creating the TFTP
060         * request packets.
061         ***/
062        static final byte[] _modeBytes[] = {
063                                               { (byte)'n', (byte)'e', (byte)'t', (byte)'a', (byte)'s', (byte)'c',
064                                                 (byte)'i', (byte)'i', 0 },
065                                               { (byte)'o', (byte)'c', (byte)'t', (byte)'e', (byte)'t', 0 }
066                                           };
067    
068        /*** The transfer mode of the request. ***/
069        int _mode;
070    
071        /*** The filename of the request. ***/
072        String _filename;
073    
074        /***
075         * Creates a request packet of a given type to be sent to a host at a
076         * given port with a filename and transfer mode request.
077         * <p>
078         * @param destination  The host to which the packet is going to be sent.
079         * @param port  The port to which the packet is going to be sent.
080         * @param type The type of the request (either TFTPPacket.READ_REQUEST or
081         *             TFTPPacket.WRITE_REQUEST).
082         * @param filename The requested filename.
083         * @param mode The requested transfer mode.  This should be on of the TFTP
084         *        class MODE constants (e.g., TFTP.NETASCII_MODE).
085         ***/
086        TFTPRequestPacket(InetAddress destination, int port,
087                          int type, String filename, int mode)
088        {
089            super(type, destination, port);
090    
091            _filename = filename;
092            _mode = mode;
093        }
094    
095        /***
096         * Creates a request packet of a given type based on a received
097         * datagram.  Assumes the datagram is at least length 4, else an
098         * ArrayIndexOutOfBoundsException may be thrown.
099         * <p>
100         * @param type The type of the request (either TFTPPacket.READ_REQUEST or
101         *             TFTPPacket.WRITE_REQUEST).
102         * @param datagram  The datagram containing the received request.
103         * @throws TFTPPacketException  If the datagram isn't a valid TFTP
104         *         request packet of the appropriate type.
105         ***/
106        TFTPRequestPacket(int type, DatagramPacket datagram)
107        throws TFTPPacketException
108        {
109            super(type, datagram.getAddress(), datagram.getPort());
110    
111            byte[] data;
112            int index, length;
113            String mode;
114            StringBuffer buffer;
115    
116            data = datagram.getData();
117    
118            if (getType() != data[1])
119                throw new TFTPPacketException("TFTP operator code does not match type.");
120    
121            buffer = new StringBuffer();
122    
123            index = 2;
124            length = datagram.getLength();
125    
126            while (index < length && data[index] != 0)
127            {
128                buffer.append((char)data[index]);
129                ++index;
130            }
131    
132            _filename = buffer.toString();
133    
134            if (index >= length)
135                throw new TFTPPacketException("Bad filename and mode format.");
136    
137            buffer.setLength(0);
138            ++index; // need to advance beyond the end of string marker
139            while (index < length && data[index] != 0)
140            {
141                buffer.append((char)data[index]);
142                ++index;
143            }
144    
145            mode = buffer.toString().toLowerCase();
146            length = _modeStrings.length;
147    
148            for (index = 0; index < length; index++)
149            {
150                if (mode.equals(_modeStrings[index]))
151                {
152                    _mode = index;
153                    break;
154                }
155            }
156    
157            if (index >= length)
158            {
159                throw new TFTPPacketException("Unrecognized TFTP transfer mode: " + mode);
160                // May just want to default to binary mode instead of throwing
161                // exception.
162                //_mode = TFTP.OCTET_MODE;
163            }
164        }
165    
166    
167        /***
168         * This is a method only available within the package for
169         * implementing efficient datagram transport by elminating buffering.
170         * It takes a datagram as an argument, and a byte buffer in which
171         * to store the raw datagram data.  Inside the method, the data
172         * is set as the datagram's data and the datagram returned.
173         * <p>
174         * @param datagram  The datagram to create.
175         * @param data The buffer to store the packet and to use in the datagram.
176         * @return The datagram argument.
177         ***/
178        final DatagramPacket _newDatagram(DatagramPacket datagram, byte[] data)
179        {
180            int fileLength, modeLength;
181    
182            fileLength = _filename.length();
183            modeLength = _modeBytes[_mode].length;
184    
185            data[0] = 0;
186            data[1] = (byte)_type;
187            System.arraycopy(_filename.getBytes(), 0, data, 2, fileLength);
188            data[fileLength + 2] = 0;
189            System.arraycopy(_modeBytes[_mode], 0, data, fileLength + 3,
190                             modeLength);
191    
192            datagram.setAddress(_address);
193            datagram.setPort(_port);
194            datagram.setData(data);
195            datagram.setLength(fileLength + modeLength + 3);
196            
197            return datagram;
198        }
199    
200        /***
201         * Creates a UDP datagram containing all the TFTP
202         * request packet data in the proper format.
203         * This is a method exposed to the programmer in case he
204         * wants to implement his own TFTP client instead of using
205         * the {@link org.apache.commons.net.tftp.TFTPClient}
206         * class.  Under normal circumstances, you should not have a need to call
207         * this method.
208         * <p>
209         * @return A UDP datagram containing the TFTP request packet.
210         ***/
211        public final DatagramPacket newDatagram()
212        {
213            int fileLength, modeLength;
214            byte[] data;
215    
216            fileLength = _filename.length();
217            modeLength = _modeBytes[_mode].length;
218    
219            data = new byte[fileLength + modeLength + 4];
220            data[0] = 0;
221            data[1] = (byte)_type;
222            System.arraycopy(_filename.getBytes(), 0, data, 2, fileLength);
223            data[fileLength + 2] = 0;
224            System.arraycopy(_modeBytes[_mode], 0, data, fileLength + 3,
225                             modeLength);
226    
227            return new DatagramPacket(data, data.length, _address, _port);
228        }
229    
230        /***
231         * Returns the transfer mode of the request.
232         * <p>
233         * @return The transfer mode of the request.
234         ***/
235        public final int getMode()
236        {
237            return _mode;
238        }
239    
240        /***
241         * Returns the requested filename.
242         * <p>
243         * @return The requested filename.
244         ***/
245        public final String getFilename()
246        {
247            return _filename;
248        }
249    }