ObexHelper.java revision 2e0da96e757a977154063f980d3f4e1abd41cf09
19439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly/*
29439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Copyright (c) 2008-2009, Motorola, Inc.
39439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
49439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * All rights reserved.
59439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
69439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Redistribution and use in source and binary forms, with or without
79439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * modification, are permitted provided that the following conditions are met:
89439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
99439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Redistributions of source code must retain the above copyright notice,
109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * this list of conditions and the following disclaimer.
119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Redistributions in binary form must reproduce the above copyright notice,
139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * this list of conditions and the following disclaimer in the documentation
149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * and/or other materials provided with the distribution.
159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Neither the name of the Motorola, Inc. nor the names of its contributors
179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * may be used to endorse or promote products derived from this software
189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * without specific prior written permission.
199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * POSSIBILITY OF SUCH DAMAGE.
319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */
329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellypackage javax.obex;
349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
352e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellyimport android.security.Md5MessageDigest;
362e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly
372e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellyimport java.io.ByteArrayOutputStream;
382e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellyimport java.io.IOException;
392e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellyimport java.io.UnsupportedEncodingException;
402e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellyimport java.util.Calendar;
412e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellyimport java.util.Date;
422e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellyimport java.util.TimeZone;
439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly/**
452e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * This class defines a set of helper methods for the implementation of Obex.
469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly *
472e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @hide
489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */
492e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellypublic final class ObexHelper {
502e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly
512e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly    /** Prevent object construction of helper class */
522e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly    private ObexHelper() {}
539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
552e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * Defines the OBEX CONTINUE response code.
562e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * <P>
572e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * The value of <code>OBEX_HTTP_CONTINUE</code> is 0x90 (144).
589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
592e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly    public static final int OBEX_HTTP_CONTINUE = 0x90;
609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
612e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly    /**
622e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * The maximum packet size for OBEX packets that this client can handle.
632e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * At present, this must be changed for each port.
642e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     *
652e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * OPTIMIZATION: The max packet size should be the Max incoming MTU minus
662e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * OPTIMIZATION: L2CAP package headers and RFCOMM package headers.
672e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     *
682e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * OPTIMIZATION: Retrieve the max incoming MTU from
692e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * OPTIMIZATION: LocalDevice.getProperty().
702e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     */
712e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly    /** android note
722e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     *  set as 0xFFFE to match remote MPS
732e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     */
742e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly    public static final int MAX_PACKET_SIZE_INT = 0xFFFE;
759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Updates the HeaderSet with the headers received in the byte array
789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * provided.  Invalid headers are ignored.
799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * <P>
809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * The first two bits of an OBEX Header specifies the type of object that
819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * is being sent.  The table below specifies the meaning of the high
829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * bits.
839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * <TABLE>
849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * <TR><TH>Bits 8 and 7</TH><TH>Value</TH><TH>Description</TH></TR>
859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * <TR><TD>00</TD><TD>0x00</TD><TD>Null Terminated Unicode text, prefixed
869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * with 2 byte unsigned integer</TD></TR>
879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * <TR><TD>01</TD><TD>0x40</TD><TD>Byte Sequence, length prefixed with
889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * 2 byte unsigned integer</TD></TR>
899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * <TR><TD>10</TD><TD>0x80</TD><TD>1 byte quantity</TD></TR>
909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * <TR><TD>11</TD><TD>0xC0</TD><TD>4 byte quantity - transmitted in
919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * network byte order (high byte first</TD></TR>
929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * </TABLE>
939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * This method uses the information in this table to determine the type of
949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Java object to create and passes that object with the full header
959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * to setHeader() to update the HeaderSet object.  Invalid headers will
969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * cause an exception to be thrown.  When it is thrown, it is ignored.
979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param header the HeaderSet to update
999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
1009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param headerArray the byte array containing headers
1019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
1029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @return the result of the last start body or end body header provided;
1039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * the first byte in the result will specify if a body or end of body is
1049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * received
1059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
1062e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * @throws IOException if an invalid header was found
1079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
1089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throws IOException {
1099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int index = 0;
1109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int length = 0;
1119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int headerID;
1129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] value = null;
1139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] body = null;
1149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        HeaderSet headerImpl = header;
1159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        try {
1169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            while (index < headerArray.length) {
1179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                headerID = 0xFF & headerArray[index];
1189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                switch (headerID & (0xC0)) {
1199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    /*
1219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                     * 0x00 is a unicode null terminate string with the first
1229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                     * two bytes after the header identifier being the length
1239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                     */
1249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    case 0x00:
1259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        // Fall through
1269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        /*
1279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                         * 0x40 is a byte sequence with the first
1289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                         * two bytes after the header identifier being the length
1299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                         */
1309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    case 0x40:
1319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        boolean trimTail = true;
1329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        index++;
1339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        length = 0xFF & headerArray[index];
1349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        length = length << 8;
1359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        index++;
1369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        length += 0xFF & headerArray[index];
1379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        length -= 3;
1389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        index++;
1399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        value = new byte[length];
1409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        System.arraycopy(headerArray, index, value, 0, length);
1419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
1429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            trimTail = false;
1439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        }
1449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        switch (headerID) {
1459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            case HeaderSet.TYPE:
1469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                try {
1479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    // Remove trailing null
1489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    if (trimTail == false) {
1499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                        headerImpl.setHeader(headerID, new String(value, 0,
1509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                                value.length, "ISO8859_1"));
1519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    } else {
1529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                        headerImpl.setHeader(headerID, new String(value, 0,
1539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                                value.length - 1, "ISO8859_1"));
1549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    }
1559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                } catch (UnsupportedEncodingException e) {
1569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    throw new RuntimeException("ISO8859_1 is not supported"
1579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            + e.getMessage());
1589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                }
1599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                break;
1609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            // This is the constant for the authentication challenge header
1629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            // This header does not have a constant defined in the Java
1639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            // OBEX API
1649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            case 0x4D:
1659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                headerImpl.authChall = new byte[length];
1669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                System.arraycopy(headerArray, index, headerImpl.authChall, 0,
1679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                        length);
1689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                break;
1699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            // This is the constant for the authentication response header
1719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            // This header does not have a constant defined in the Java
1729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            // OBEX API
1739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            case 0x4E:
1749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                headerImpl.authResp = new byte[length];
1759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                System
1769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                        .arraycopy(headerArray, index, headerImpl.authResp, 0,
1779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                                length);
1789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                break;
1799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            /*
1819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            * These two case statements are for the body (0x48)
1829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                             * and end of body (0x49) headers.
1839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            */
1849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            case 0x48:
1859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                /* Fall Through */
1869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            case 0x49:
1879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                body = new byte[length + 1];
1889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                body[0] = (byte)headerID;
1899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                System.arraycopy(headerArray, index, body, 1, length);
1909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                break;
1919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
1929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            case HeaderSet.TIME_ISO_8601:
1939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                try {
1949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    String dateString = new String(value, "ISO8859_1");
1959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    Calendar temp = Calendar.getInstance();
1969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    if ((dateString.length() == 16)
1979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            && (dateString.charAt(15) == 'Z')) {
1989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                        temp.setTimeZone(TimeZone.getTimeZone("UTC"));
1999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    }
2009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    temp.set(Calendar.YEAR, Integer.parseInt(dateString.substring(
2019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            0, 4)));
2029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    temp.set(Calendar.MONTH, Integer.parseInt(dateString.substring(
2039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            4, 6)));
2049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    temp.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateString
2059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            .substring(6, 8)));
2069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    temp.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateString
2079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            .substring(9, 11)));
2089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    temp.set(Calendar.MINUTE, Integer.parseInt(dateString
2099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            .substring(11, 13)));
2109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    temp.set(Calendar.SECOND, Integer.parseInt(dateString
2119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            .substring(13, 15)));
2129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    headerImpl.setHeader(HeaderSet.TIME_ISO_8601, temp);
2139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                } catch (UnsupportedEncodingException e) {
2149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    throw new RuntimeException("ISO8859_1 is not supported"
2159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            + e.getMessage());
2169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                } catch (Exception e) {
2179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    throw new IOException(
2189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            "Time Header does not follow ISO 8601 standard");
2199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                }
2209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                break;
2219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            default:
2239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                try {
2249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    if ((headerID & 0xC0) == 0x00) {
2252e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly                                        headerImpl.setHeader(headerID, ObexHelper.convertToUnicode(
2269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                                value, true));
2279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    } else {
2289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                        headerImpl.setHeader(headerID, value);
2299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    }
2309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                } catch (Exception e) {
2319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    // Not a valid header so ignore
2329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                }
2339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        }
2349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        index += length;
2369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        break;
2379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    /*
2399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                     * 0x80 is a byte header.  The only valid byte headers are
2409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                     * the 16 user defined byte headers.
2419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                     */
2429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    case 0x80:
2439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        index++;
2449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        try {
2459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            headerImpl.setHeader(headerID, Byte.valueOf(headerArray[index]));
2469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        } catch (Exception e) {
2479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            // Not a valid header so ignore
2489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        }
2499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        index++;
2509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        break;
2519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    /*
2539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                     * 0xC0 is a 4 byte unsigned integer header and with the
2549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                     * exception of TIME_4_BYTE will be converted to a Long
2559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                     * and added.
2569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                     */
2579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    case 0xC0:
2589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        index++;
2599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        value = new byte[4];
2609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        System.arraycopy(headerArray, index, value, 0, 4);
2619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        try {
2629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            if (headerID != HeaderSet.TIME_4_BYTE) {
2639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                // Determine if it is a connection ID.  These
2649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                // need to be handled differently
2659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                if (headerID == 0xCB) {
2669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    headerImpl.connectionID = new byte[4];
2679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    System.arraycopy(value, 0, headerImpl.connectionID, 0, 4);
2689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                } else {
2699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                    headerImpl.setHeader(headerID, Long
2709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                            .valueOf(convertToLong(value)));
2719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                }
2729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            } else {
2739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                Calendar temp = Calendar.getInstance();
2749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                temp.setTime(new Date(convertToLong(value) * 1000L));
2759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                                headerImpl.setHeader(HeaderSet.TIME_4_BYTE, temp);
2769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            }
2779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        } catch (Exception e) {
2789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            // Not a valid header so ignore
2799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            throw new IOException("Header was not formatted properly");
2809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        }
2819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        index += 4;
2829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        break;
2839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
2849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
2869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        } catch (IOException e) {
2879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IOException("Header was not formatted properly");
2889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
2899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return body;
2919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
2929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
2939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
2949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Creates the header part of OBEX packet based on the header provided.
2959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
2969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * OPTIMIZATION: Could use getHeaderList() to get the array of headers to
2979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * OPTIMIZATION: include and then use the high two bits to determine the
2989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * OPTIMIZATION: the type of the object and construct the byte array from
2999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * OPTIMIZATION: that.  This will make the size smaller.
3009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
3019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param head the header used to construct the byte array
3029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
3039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param nullOut <code>true</code> if the header should be set to
3049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * <code>null</code> once it is added to the array or <code>false</code>
3059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * if it should not be nulled out
3069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
3079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @return the header of an OBEX packet
3089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
3099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public static byte[] createHeader(HeaderSet head, boolean nullOut) {
3109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        Long intHeader = null;
3119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        String stringHeader = null;
3129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        Calendar dateHeader = null;
3139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        Byte byteHeader = null;
3149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        StringBuffer buffer = null;
3159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] value = null;
3169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] result = null;
3179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] lengthArray = new byte[2];
3189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int length;
3199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        HeaderSet headImpl = null;
3209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        ByteArrayOutputStream out = new ByteArrayOutputStream();
3219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (!(head instanceof HeaderSet)) {
3229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IllegalArgumentException("Header not created by createHeaderSet");
3239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
3249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        headImpl = head;
3259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        try {
3279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            /*
3289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly             * Determine if there is a connection ID to send.  If there is,
3299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly             * then it should be the first header in the packet.
3309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly             */
3319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if ((headImpl.connectionID != null) && (headImpl.getHeader(HeaderSet.TARGET) == null)) {
3329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)0xCB);
3349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(headImpl.connectionID);
3359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
3369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Count Header
3389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            intHeader = (Long)headImpl.getHeader(HeaderSet.COUNT);
3399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (intHeader != null) {
3409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)HeaderSet.COUNT);
3412e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly                value = ObexHelper.convertToByteArray(intHeader.longValue());
3429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
3439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
3449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.COUNT, null);
3459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
3469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
3479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Name Header
3499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            stringHeader = (String)headImpl.getHeader(HeaderSet.NAME);
3509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (stringHeader != null) {
3519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)HeaderSet.NAME);
3522e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly                value = ObexHelper.convertToUnicodeByteArray(stringHeader);
3539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = value.length + 3;
3549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
3559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
3569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
3579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
3589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
3599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.NAME, null);
3609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
3619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
3629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Type Header
3649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            stringHeader = (String)headImpl.getHeader(HeaderSet.TYPE);
3659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (stringHeader != null) {
3669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)HeaderSet.TYPE);
3679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                try {
3689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    value = stringHeader.getBytes("ISO8859_1");
3699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                } catch (UnsupportedEncodingException e) {
3709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    throw new RuntimeException("Unsupported Encoding Scheme: " + e.getMessage());
3719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
3729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = value.length + 4;
3749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
3759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
3769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
3779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
3789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(0x00);
3799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
3809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.TYPE, null);
3819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
3829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
3839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Length Header
3859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            intHeader = (Long)headImpl.getHeader(HeaderSet.LENGTH);
3869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (intHeader != null) {
3879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)HeaderSet.LENGTH);
3882e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly                value = ObexHelper.convertToByteArray(intHeader.longValue());
3899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
3909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
3919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.LENGTH, null);
3929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
3939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
3949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Time ISO Header
3969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_ISO_8601);
3979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (dateHeader != null) {
3989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
3999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                /*
4009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                 * The ISO Header should take the form YYYYMMDDTHHMMSSZ.  The
4019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                 * 'Z' will only be included if it is a UTC time.
4029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                 */
4039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                buffer = new StringBuffer();
4049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                int temp = dateHeader.get(Calendar.YEAR);
4059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                for (int i = temp; i < 1000; i = i * 10) {
4069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    buffer.append("0");
4079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                buffer.append(temp);
4099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                temp = dateHeader.get(Calendar.MONTH);
4109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (temp < 10) {
4119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    buffer.append("0");
4129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                buffer.append(temp);
4149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                temp = dateHeader.get(Calendar.DAY_OF_MONTH);
4159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (temp < 10) {
4169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    buffer.append("0");
4179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                buffer.append(temp);
4199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                buffer.append("T");
4209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                temp = dateHeader.get(Calendar.HOUR_OF_DAY);
4219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (temp < 10) {
4229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    buffer.append("0");
4239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                buffer.append(temp);
4259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                temp = dateHeader.get(Calendar.MINUTE);
4269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (temp < 10) {
4279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    buffer.append("0");
4289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                buffer.append(temp);
4309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                temp = dateHeader.get(Calendar.SECOND);
4319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (temp < 10) {
4329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    buffer.append("0");
4339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                buffer.append(temp);
4359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (dateHeader.getTimeZone().getID().equals("UTC")) {
4379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    buffer.append("Z");
4389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                try {
4419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    value = buffer.toString().getBytes("ISO8859_1");
4429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                } catch (UnsupportedEncodingException e) {
4439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    throw new RuntimeException("UnsupportedEncodingException: " + e.getMessage());
4449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = value.length + 3;
4479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
4489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
4499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(HeaderSet.TIME_ISO_8601);
4509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
4519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
4529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
4539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.TIME_ISO_8601, null);
4549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
4569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Time 4 Byte Header
4589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_4_BYTE);
4599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (dateHeader != null) {
4609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(HeaderSet.TIME_4_BYTE);
4619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                /*
4639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                 * Need to call getTime() twice.  The first call will return
4649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                 * a java.util.Date object.  The second call returns the number
4659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                 * of milliseconds since January 1, 1970.  We need to convert
4669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                 * it to seconds since the TIME_4_BYTE expects the number of
4679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                 * seconds since January 1, 1970.
4689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                 */
4692e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly                value = ObexHelper.convertToByteArray(dateHeader.getTime().getTime() / 1000L);
4709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
4719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
4729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.TIME_4_BYTE, null);
4739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
4759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Description Header
4779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            stringHeader = (String)headImpl.getHeader(HeaderSet.DESCRIPTION);
4789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (stringHeader != null) {
4799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)HeaderSet.DESCRIPTION);
4802e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly                value = ObexHelper.convertToUnicodeByteArray(stringHeader);
4819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = value.length + 3;
4829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
4839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
4849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
4859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
4869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
4879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.DESCRIPTION, null);
4889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
4899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
4909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
4919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Target Header
4929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            value = (byte[])headImpl.getHeader(HeaderSet.TARGET);
4939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (value != null) {
4949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)HeaderSet.TARGET);
4959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = value.length + 3;
4969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
4979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
4989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
4999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
5009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
5019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.TARGET, null);
5029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
5039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
5049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
5059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // HTTP Header
5069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            value = (byte[])headImpl.getHeader(HeaderSet.HTTP);
5079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (value != null) {
5089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)HeaderSet.HTTP);
5099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = value.length + 3;
5109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
5119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
5129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
5139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
5149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
5159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.HTTP, null);
5169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
5179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
5189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
5199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Who Header
5209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            value = (byte[])headImpl.getHeader(HeaderSet.WHO);
5219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (value != null) {
5229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)HeaderSet.WHO);
5239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = value.length + 3;
5249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
5259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
5269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
5279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
5289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
5299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.WHO, null);
5309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
5319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
5329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
5339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Connection ID Header
5349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            value = (byte[])headImpl.getHeader(HeaderSet.APPLICATION_PARAMETER);
5359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (value != null) {
5369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)HeaderSet.APPLICATION_PARAMETER);
5379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = value.length + 3;
5389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
5399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
5409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
5419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
5429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
5439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.APPLICATION_PARAMETER, null);
5449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
5459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
5469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
5479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Object Class Header
5489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            value = (byte[])headImpl.getHeader(HeaderSet.OBJECT_CLASS);
5499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (value != null) {
5509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)HeaderSet.OBJECT_CLASS);
5519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = value.length + 3;
5529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
5539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
5549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
5559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(value);
5569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
5579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.setHeader(HeaderSet.OBJECT_CLASS, null);
5589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
5599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
5609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
5619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Check User Defined Headers
5629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            for (int i = 0; i < 16; i++) {
5639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
5649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                //Unicode String Header
5659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                stringHeader = (String)headImpl.getHeader(i + 0x30);
5669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (stringHeader != null) {
5679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    out.write((byte)i + 0x30);
5682e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly                    value = ObexHelper.convertToUnicodeByteArray(stringHeader);
5699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    length = value.length + 3;
5709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    lengthArray[0] = (byte)(255 & (length >> 8));
5719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    lengthArray[1] = (byte)(255 & length);
5729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    out.write(lengthArray);
5739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    out.write(value);
5749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    if (nullOut) {
5759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        headImpl.setHeader(i + 0x30, null);
5769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    }
5779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
5789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
5799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                // Byte Sequence Header
5809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                value = (byte[])headImpl.getHeader(i + 0x70);
5819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (value != null) {
5829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    out.write((byte)i + 0x70);
5839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    length = value.length + 3;
5849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    lengthArray[0] = (byte)(255 & (length >> 8));
5859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    lengthArray[1] = (byte)(255 & length);
5869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    out.write(lengthArray);
5879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    out.write(value);
5889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    if (nullOut) {
5899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        headImpl.setHeader(i + 0x70, null);
5909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    }
5919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
5929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
5939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                // Byte Header
5949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                byteHeader = (Byte)headImpl.getHeader(i + 0xB0);
5959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (byteHeader != null) {
5969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    out.write((byte)i + 0xB0);
5979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    out.write(byteHeader.byteValue());
5989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    if (nullOut) {
5999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        headImpl.setHeader(i + 0xB0, null);
6009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    }
6019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
6029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                // Integer header
6049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                intHeader = (Long)headImpl.getHeader(i + 0xF0);
6059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (intHeader != null) {
6069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    out.write((byte)i + 0xF0);
6072e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly                    out.write(ObexHelper.convertToByteArray(intHeader.longValue()));
6089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    if (nullOut) {
6099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                        headImpl.setHeader(i + 0xF0, null);
6109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    }
6119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
6129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
6139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Add the authentication challenge header
6159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (headImpl.authChall != null) {
6169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)0x4D);
6179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = headImpl.authChall.length + 3;
6189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
6199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
6209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
6219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(headImpl.authChall);
6229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
6239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.authChall = null;
6249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
6259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
6269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            // Add the authentication response header
6289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (headImpl.authResp != null) {
6299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write((byte)0x4E);
6309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                length = headImpl.authResp.length + 3;
6319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[0] = (byte)(255 & (length >> 8));
6329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lengthArray[1] = (byte)(255 & length);
6339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(lengthArray);
6349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.write(headImpl.authResp);
6359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                if (nullOut) {
6369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    headImpl.authResp = null;
6379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                }
6389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
6399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        } catch (IOException e) {
6419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        } finally {
6429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            result = out.toByteArray();
6439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            try {
6449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                out.close();
6459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            } catch (Exception ex) {
6469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
6479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
6489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return result;
6509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
6529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
6549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Determines where the maximum divide is between headers.  This method is
6559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * used by put and get operations to separate headers to a size that meets
6569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * the max packet size allowed.
6579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
6589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param headerArray the headers to separate
6599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
6609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param start the starting index to search
6619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
6629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param maxSize the maximum size of a packet
6639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
6649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @return the index of the end of the header block to send or -1 if the
6659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * header could not be divided because the header is too large
6669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
6679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public static int findHeaderEnd(byte[] headerArray, int start, int maxSize) {
6689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int fullLength = 0;
6709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int lastLength = -1;
6719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int index = start;
6729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int length = 0;
6739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        while ((fullLength < maxSize) && (index < headerArray.length)) {
6759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            int headerID = (headerArray[index] < 0 ? headerArray[index] + 256 : headerArray[index]);
6769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            lastLength = fullLength;
6779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            switch (headerID & (0xC0)) {
6799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                case 0x00:
6819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    // Fall through
6829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                case 0x40:
6839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    index++;
6859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    length = (headerArray[index] < 0 ? headerArray[index] + 256
6869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            : headerArray[index]);
6879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    length = length << 8;
6889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    index++;
6899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    length += (headerArray[index] < 0 ? headerArray[index] + 256
6909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                            : headerArray[index]);
6919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    length -= 3;
6929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    index++;
6939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    index += length;
6949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    fullLength += length + 3;
6959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    break;
6969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                case 0x80:
6989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
6999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    index++;
7009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    index++;
7019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    fullLength += 2;
7029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    break;
7039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                case 0xC0:
7059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    index += 5;
7079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    fullLength += 5;
7089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                    break;
7099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
7119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
7139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        /*
7159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * Determine if this is the last header or not
7169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         */
7179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (lastLength == 0) {
7189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            /*
7199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly             * Since this is the last header, check to see if the size of this
7209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly             * header is less then maxSize.  If it is, return the length of the
7219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly             * header, otherwise return -1.  The length of the header is
7229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly             * returned since it would be the start of the next header
7239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly             */
7249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (fullLength < maxSize) {
7259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                return headerArray.length;
7269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            } else {
7279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                return -1;
7289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
7299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        } else {
7309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            return lastLength + start;
7319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
7329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
7339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
7359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Converts the byte array to a long.
7369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
7379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param b the byte array to convert to a long
7389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
7399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @return the byte array as a long
7409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
7419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public static long convertToLong(byte[] b) {
7429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        long result = 0;
7439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        long value = 0;
7449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        long power = 0;
7459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        for (int i = (b.length - 1); i >= 0; i--) {
7479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            value = b[i];
7489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (value < 0) {
7499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                value += 256;
7509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
7519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            result = result | (value << power);
7539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            power += 8;
7549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
7559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return result;
7579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
7589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
7609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Converts the long to a 4 byte array.  The long must be non negative.
7619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
7629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param l the long to convert
7639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
7649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @return a byte array that is the same as the long
7659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
7669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public static byte[] convertToByteArray(long l) {
7679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] b = new byte[4];
7689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        b[0] = (byte)(255 & (l >> 24));
7709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        b[1] = (byte)(255 & (l >> 16));
7719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        b[2] = (byte)(255 & (l >> 8));
7729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        b[3] = (byte)(255 & l);
7739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return b;
7759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
7769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
7789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Converts the String to a UNICODE byte array.  It will also add the ending
7799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * null characters to the end of the string.
7809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
7819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param s the string to convert
7829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
7839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @return the unicode byte array of the string
7849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
7859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public static byte[] convertToUnicodeByteArray(String s) {
7869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (s == null) {
7879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            return null;
7889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
7899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        char c[] = s.toCharArray();
7919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] result = new byte[(c.length * 2) + 2];
7929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        for (int i = 0; i < c.length; i++) {
7939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            result[(i * 2)] = (byte)(c[i] >> 8);
7949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            result[((i * 2) + 1)] = (byte)c[i];
7959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
7969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
7979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // Add the UNICODE null character
7989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        result[result.length - 2] = 0;
7999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        result[result.length - 1] = 0;
8009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return result;
8029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
8039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
8059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Retrieves the value from the byte array for the tag value specified.  The
8069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * array should be of the form Tag - Length - Value triplet.
8079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
8089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param tag the tag to retrieve from the byte array
8099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
8109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param triplet the byte sequence containing the tag length value form
8119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
8129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @return the value of the specified tag
8139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
8149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public static byte[] getTagValue(byte tag, byte[] triplet) {
8159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int index = findTag(tag, triplet);
8179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (index == -1) {
8189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            return null;
8199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
8209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        index++;
8229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int length = triplet[index] & 0xFF;
8239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] result = new byte[length];
8259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        index++;
8269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        System.arraycopy(triplet, index, result, 0, length);
8279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return result;
8299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
8309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
8329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Finds the index that starts the tag value pair in the byte array provide.
8339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
8349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param tag the tag to look for
8359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
8369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param value the byte array to search
8379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
8389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @return the starting index of the tag or -1 if the tag could not be found
8399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
8409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public static int findTag(byte tag, byte[] value) {
8419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int length = 0;
8429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (value == null) {
8449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            return -1;
8459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
8469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int index = 0;
8489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        while ((index < value.length) && (value[index] != tag)) {
8509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            length = value[index + 1] & 0xFF;
8519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            index += length + 2;
8529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
8539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (index >= value.length) {
8559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            return -1;
8569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
8579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return index;
8599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
8609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
8629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Converts the byte array provided to a unicode string.
8639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
8649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param b the byte array to convert to a string
8659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
8669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param includesNull determine if the byte string provided contains the
8679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * UNICODE null character at the end or not;  if it does, it will be
8689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * removed
8699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
8709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @return a Unicode string
8719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
8729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param IllegalArgumentException if the byte array has an odd length
8739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
8749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public static String convertToUnicode(byte[] b, boolean includesNull) {
8759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (b == null) {
8769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            return null;
8779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
8789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        int arrayLength = b.length;
8799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (!((arrayLength % 2) == 0)) {
8809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IllegalArgumentException("Byte array not of a valid form");
8819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
8829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        arrayLength = (arrayLength >> 1);
8839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (includesNull) {
8849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            arrayLength -= 1;
8859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
8869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        char[] c = new char[arrayLength];
8889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        for (int i = 0; i < arrayLength; i++) {
8899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            int upper = b[2 * i];
8909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            int lower = b[(2 * i) + 1];
8919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (upper < 0) {
8929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                upper += 256;
8939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
8949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (lower < 0) {
8959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                lower += 256;
8969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
8979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
8989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            c[i] = (char)((upper << 8) | lower);
8999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
9009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
9019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return new String(c);
9029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
9039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
9049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
9052e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * Compute the MD5 hash of the byte array provided.
9062e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * Does not accumulate input.
9079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
9089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param in the byte array to hash
9099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @return the MD5 hash of the byte array
9109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
9112e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly    public static byte[] computeMd5Hash(byte[] in) {
9122e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly        Md5MessageDigest md5 = new Md5MessageDigest();
9132e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly        return md5.digest(in);
9149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
9159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
9169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    /**
9179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * Computes an authentication challenge header.
9189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
9199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
9209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param nonce the challenge that will be provided to the peer;  the
9219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * challenge must be 16 bytes long
9229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
9239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param realm a short description that describes what password to use
9249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
9259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param access if <code>true</code> then full access will be granted if
9269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * successful; if <code>false</code> then read only access will be granted
9279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * if successful
9289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
9299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * @param userID if <code>true</code>, a user ID is required in the reply;
9309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * if <code>false</code>, no user ID is required
9319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
9322e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * @throws IllegalArgumentException if the challenge is not 16 bytes
9339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     * long; if the realm can not be encoded in less then 255 bytes
9349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     *
9352e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly     * @throws IOException if the encoding scheme ISO 8859-1 is not supported
9369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly     */
9379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    public static byte[] computeAuthenticationChallenge(byte[] nonce, String realm, boolean access,
9389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            boolean userID) throws IOException {
9399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        byte[] authChall = null;
9409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
9419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (nonce.length != 16) {
9429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            throw new IllegalArgumentException("Nonce must be 16 bytes long");
9439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
9449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
9459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        /*
9469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * The authentication challenge is a byte sequence of the following form
9479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * byte 0: 0x00 - the tag for the challenge
9489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * byte 1: 0x10 - the length of the challenge; must be 16
9499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * byte 2-17: the authentication challenge
9509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * byte 18: 0x01 - the options tag; this is optional in the spec, but
9519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         *                 we are going to include it in every message
9529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * byte 19: 0x01 - length of the options; must be 1
9539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * byte 20: the value of the options; bit 0 is set if user ID is
9549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         *          required; bit 1 is set if access mode is read only
9559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * byte 21: 0x02 - the tag for authentication realm; only included if
9569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         *                 an authentication realm is specified
9579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * byte 22: the length of the authentication realm; only included if
9589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         *          the authentication realm is specified
9599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * byte 23: the encoding scheme of the authentication realm; we will use
9609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         *          the ISO 8859-1 encoding scheme since it is part of the KVM
9619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         * byte 24 & up: the realm if one is specified.
9629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly         */
9639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (realm == null) {
9649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            authChall = new byte[21];
9659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        } else {
9669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            if (realm.length() >= 255) {
9679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly                throw new IllegalArgumentException("Realm must be less then 255 bytes");
9689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            }
9699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            authChall = new byte[24 + realm.length()];
9709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            authChall[21] = 0x02;
9719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            authChall[22] = (byte)(realm.length() + 1);
9729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            authChall[23] = 0x01; // ISO 8859-1 Encoding
9739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            System.arraycopy(realm.getBytes("ISO8859_1"), 0, authChall, 24, realm.length());
9749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
9759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
9769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // Include the nonce field in the header
9779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        authChall[0] = 0x00;
9789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        authChall[1] = 0x10;
9799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        System.arraycopy(nonce, 0, authChall, 2, 16);
9809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
9819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        // Include the options header
9829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        authChall[18] = 0x01;
9839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        authChall[19] = 0x01;
9849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        authChall[20] = 0x00;
9859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
9869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (!access) {
9879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            authChall[20] = (byte)(authChall[20] | 0x02);
9889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
9899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        if (userID) {
9909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly            authChall[20] = (byte)(authChall[20] | 0x01);
9919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        }
9929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly
9939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly        return authChall;
9949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly    }
9959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly}
996