1 /*
   2  * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  *******************************************************************************
  28  * (C) Copyright IBM Corp. 1996-2005 - All Rights Reserved                     *
  29  *                                                                             *
  30  * The original version of this source code and documentation is copyrighted   *
  31  * and owned by IBM, These materials are provided under terms of a License     *
  32  * Agreement between IBM and Sun. This technology is protected by multiple     *
  33  * US and International patents. This notice and attribution to IBM may not    *
  34  * to removed.                                                                 *
  35  *******************************************************************************
  36  */
  37 
  38 package sun.text.normalizer;
  39 
  40 import java.io.InputStream;
  41 import java.io.DataInputStream;
  42 import java.io.IOException;
  43 import java.util.Arrays;
  44 
  45 public final class ICUBinary
  46 {
  47     // public inner interface ------------------------------------------------
  48 
  49     /**
  50      * Special interface for data authentication
  51      */
  52     public static interface Authenticate
  53     {
  54         /**
  55          * Method used in ICUBinary.readHeader() to provide data format
  56          * authentication.
  57          * @param version version of the current data
  58          * @return true if dataformat is an acceptable version, false otherwise
  59          */
  60         public boolean isDataVersionAcceptable(byte version[]);
  61     }
  62 
  63     // public methods --------------------------------------------------------
  64 
  65     /**
  66     * <p>ICU data header reader method.
  67     * Takes a ICU generated big-endian input stream, parse the ICU standard
  68     * file header and authenticates them.</p>
  69     * <p>Header format:
  70     * <ul>
  71     *     <li> Header size (char)
  72     *     <li> Magic number 1 (byte)
  73     *     <li> Magic number 2 (byte)
  74     *     <li> Rest of the header size (char)
  75     *     <li> Reserved word (char)
  76     *     <li> Big endian indicator (byte)
  77     *     <li> Character set family indicator (byte)
  78     *     <li> Size of a char (byte) for c++ and c use
  79     *     <li> Reserved byte (byte)
  80     *     <li> Data format identifier (4 bytes), each ICU data has its own
  81     *          identifier to distinguish them. [0] major [1] minor
  82     *                                          [2] milli [3] micro
  83     *     <li> Data version (4 bytes), the change version of the ICU data
  84     *                             [0] major [1] minor [2] milli [3] micro
  85     *     <li> Unicode version (4 bytes) this ICU is based on.
  86     * </ul>
  87     * </p>
  88     * <p>
  89     * Example of use:<br>
  90     * <pre>
  91     * try {
  92     *    FileInputStream input = new FileInputStream(filename);
  93     *    If (Utility.readICUDataHeader(input, dataformat, dataversion,
  94     *                                  unicode) {
  95     *        System.out.println("Verified file header, this is a ICU data file");
  96     *    }
  97     * } catch (IOException e) {
  98     *    System.out.println("This is not a ICU data file");
  99     * }
 100     * </pre>
 101     * </p>
 102     * @param inputStream input stream that contains the ICU data header
 103     * @param dataFormatIDExpected Data format expected. An array of 4 bytes
 104     *                     information about the data format.
 105     *                     E.g. data format ID 1.2.3.4. will became an array of
 106     *                     {1, 2, 3, 4}
 107     * @param authenticate user defined extra data authentication. This value
 108     *                     can be null, if no extra authentication is needed.
 109     * @exception IOException thrown if there is a read error or
 110     *            when header authentication fails.
 111     * @draft 2.1
 112     */
 113     public static final byte[] readHeader(InputStream inputStream,
 114                                         byte dataFormatIDExpected[],
 115                                         Authenticate authenticate)
 116                                                           throws IOException
 117     {
 118         DataInputStream input = new DataInputStream(inputStream);
 119         char headersize = input.readChar();
 120         int readcount = 2;
 121         //reading the header format
 122         byte magic1 = input.readByte();
 123         readcount ++;
 124         byte magic2 = input.readByte();
 125         readcount ++;
 126         if (magic1 != MAGIC1 || magic2 != MAGIC2) {
 127             throw new IOException(MAGIC_NUMBER_AUTHENTICATION_FAILED_);
 128         }
 129 
 130         input.readChar(); // reading size
 131         readcount += 2;
 132         input.readChar(); // reading reserved word
 133         readcount += 2;
 134         byte bigendian    = input.readByte();
 135         readcount ++;
 136         byte charset      = input.readByte();
 137         readcount ++;
 138         byte charsize     = input.readByte();
 139         readcount ++;
 140         input.readByte(); // reading reserved byte
 141         readcount ++;
 142 
 143         byte dataFormatID[] = new byte[4];
 144         input.readFully(dataFormatID);
 145         readcount += 4;
 146         byte dataVersion[] = new byte[4];
 147         input.readFully(dataVersion);
 148         readcount += 4;
 149         byte unicodeVersion[] = new byte[4];
 150         input.readFully(unicodeVersion);
 151         readcount += 4;
 152         if (headersize < readcount) {
 153             throw new IOException("Internal Error: Header size error");
 154         }
 155         input.skipBytes(headersize - readcount);
 156 
 157         if (bigendian != BIG_ENDIAN_ || charset != CHAR_SET_
 158             || charsize != CHAR_SIZE_
 159             || !Arrays.equals(dataFormatIDExpected, dataFormatID)
 160             || (authenticate != null
 161                 && !authenticate.isDataVersionAcceptable(dataVersion))) {
 162             throw new IOException(HEADER_AUTHENTICATION_FAILED_);
 163         }
 164         return unicodeVersion;
 165     }
 166 
 167     // private variables -------------------------------------------------
 168 
 169     /**
 170     * Magic numbers to authenticate the data file
 171     */
 172     private static final byte MAGIC1 = (byte)0xda;
 173     private static final byte MAGIC2 = (byte)0x27;
 174 
 175     /**
 176     * File format authentication values
 177     */
 178     private static final byte BIG_ENDIAN_ = 1;
 179     private static final byte CHAR_SET_ = 0;
 180     private static final byte CHAR_SIZE_ = 2;
 181 
 182     /**
 183     * Error messages
 184     */
 185     private static final String MAGIC_NUMBER_AUTHENTICATION_FAILED_ =
 186                        "ICU data file error: Not an ICU data file";
 187     private static final String HEADER_AUTHENTICATION_FAILED_ =
 188         "ICU data file error: Header authentication failed, please check if you have a valid ICU data file";
 189 }