001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.kerberos.shared.keytab;
021    
022    
023    import java.io.File;
024    import java.io.FileInputStream;
025    import java.io.FileOutputStream;
026    import java.io.IOException;
027    import java.io.InputStream;
028    import java.nio.channels.FileChannel;
029    import java.util.ArrayList;
030    import java.util.Collections;
031    import java.util.List;
032    
033    import org.apache.mina.core.buffer.IoBuffer;
034    
035    
036    /**
037     * Keytab file.
038     *
039     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
040     * @version $Rev$, $Date$
041     */
042    public class Keytab
043    {
044        /**
045         * Byte array constant for keytab file format 5.1.
046         */
047        public static final byte[] VERSION_51 = new byte[]
048            { ( byte ) 0x05, ( byte ) 0x01 };
049    
050        /**
051         * Byte array constant for keytab file format 5.2.
052         */
053        public static final byte[] VERSION_52 = new byte[]
054            { ( byte ) 0x05, ( byte ) 0x02 };
055    
056        private byte[] keytabVersion = VERSION_52;
057        private List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
058    
059    
060        /**
061         * Read a keytab file.
062         *
063         * @param file
064         * @return The keytab.
065         * @throws IOException
066         */
067        public static Keytab read( File file ) throws IOException
068        {
069            IoBuffer buffer = IoBuffer.wrap( getBytesFromFile( file ) );
070            return readKeytab( buffer );
071        }
072    
073    
074        /**
075         * Returns a new instance of a keytab with the version
076         * defaulted to 5.2.
077         *
078         * @return The keytab.
079         */
080        public static Keytab getInstance()
081        {
082            return new Keytab();
083        }
084    
085    
086        /**
087         * Write the keytab to a {@link File}.
088         *
089         * @param file
090         * @throws IOException
091         */
092        public void write( File file ) throws IOException
093        {
094            KeytabEncoder writer = new KeytabEncoder();
095            IoBuffer buffer = writer.write( keytabVersion, entries );
096            writeFile( buffer, file );
097        }
098    
099    
100        /**
101         * @param entries The entries to set.
102         */
103        public void setEntries( List<KeytabEntry> entries )
104        {
105            this.entries = entries;
106        }
107    
108    
109        /**
110         * @param keytabVersion The keytabVersion to set.
111         */
112        public void setKeytabVersion( byte[] keytabVersion )
113        {
114            this.keytabVersion = keytabVersion;
115        }
116    
117    
118        /**
119         * @return The entries.
120         */
121        public List<KeytabEntry> getEntries()
122        {
123            return Collections.unmodifiableList( entries );
124        }
125    
126    
127        /**
128         * @return The keytabVersion.
129         */
130        public byte[] getKeytabVersion()
131        {
132            return keytabVersion;
133        }
134    
135    
136        /**
137         * Read bytes into a keytab.
138         *
139         * @param bytes
140         * @return The keytab.
141         */
142        static Keytab read( byte[] bytes )
143        {
144            IoBuffer buffer = IoBuffer.wrap( bytes );
145            return readKeytab( buffer );
146        }
147    
148    
149        /**
150         * Write the keytab to a {@link ByteBuffer}.
151         * @return The buffer.
152         */
153        IoBuffer write()
154        {
155            KeytabEncoder writer = new KeytabEncoder();
156            return writer.write( keytabVersion, entries );
157        }
158    
159    
160        /**
161         * Read the contents of the buffer into a keytab.
162         *
163         * @param buffer
164         * @return The keytab.
165         */
166        private static Keytab readKeytab( IoBuffer buffer )
167        {
168            KeytabDecoder reader = new KeytabDecoder();
169            byte[] keytabVersion = reader.getKeytabVersion( buffer );
170            List<KeytabEntry> entries = reader.getKeytabEntries( buffer );
171    
172            Keytab keytab = new Keytab();
173    
174            keytab.setKeytabVersion( keytabVersion );
175            keytab.setEntries( entries );
176    
177            return keytab;
178        }
179    
180    
181        /**
182         * Returns the contents of the {@link File} in a byte array.
183         *
184         * @param file
185         * @return The byte array of the file contents.
186         * @throws IOException
187         */
188        protected static byte[] getBytesFromFile( File file ) throws IOException
189        {
190            InputStream is = new FileInputStream( file );
191    
192            long length = file.length();
193    
194            // Check to ensure that file is not larger than Integer.MAX_VALUE.
195            if ( length > Integer.MAX_VALUE )
196            {
197                throw new IOException( "File is too large " + file.getName() );
198            }
199    
200            // Create the byte array to hold the data.
201            byte[] bytes = new byte[( int ) length];
202    
203            // Read in the bytes
204            int offset = 0;
205            int numRead = 0;
206            while ( offset < bytes.length && ( numRead = is.read( bytes, offset, bytes.length - offset ) ) >= 0 )
207            {
208                offset += numRead;
209            }
210    
211            // Ensure all the bytes have been read in.
212            if ( offset < bytes.length )
213            {
214                throw new IOException( "Could not completely read file " + file.getName() );
215            }
216    
217            // Close the input stream and return bytes.
218            is.close();
219            return bytes;
220        }
221    
222    
223        /**
224         * Write the contents of the {@link ByteBuffer} to a {@link File}.
225         *
226         * @param buffer
227         * @param file
228         * @throws IOException
229         */
230        protected void writeFile( IoBuffer buffer, File file ) throws IOException
231        {
232            // Set append false to replace existing.
233            FileChannel wChannel = new FileOutputStream( file, false ).getChannel();
234    
235            // Write the bytes between the position and limit.
236            wChannel.write( buffer.buf() );
237    
238            wChannel.close();
239        }
240    }