/* * Copyright (c) 2008-2009, Motorola, Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * - Neither the name of the Motorola, Inc. nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package javax.obex; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Calendar; import java.security.SecureRandom; /** * This class implements the javax.obex.HeaderSet interface for OBEX over * RFCOMM. * @hide */ public final class HeaderSet { /** * Represents the OBEX Count header. This allows the connection statement to * tell the server how many objects it plans to send or retrieve. *

* The value of COUNT is 0xC0 (192). */ public static final int COUNT = 0xC0; /** * Represents the OBEX Name header. This specifies the name of the object. *

* The value of NAME is 0x01 (1). */ public static final int NAME = 0x01; /** * Represents the OBEX Type header. This allows a request to specify the * type of the object (e.g. text, html, binary, etc.). *

* The value of TYPE is 0x42 (66). */ public static final int TYPE = 0x42; /** * Represents the OBEX Length header. This is the length of the object in * bytes. *

* The value of LENGTH is 0xC3 (195). */ public static final int LENGTH = 0xC3; /** * Represents the OBEX Time header using the ISO 8601 standards. This is the * preferred time header. *

* The value of TIME_ISO_8601 is 0x44 (68). */ public static final int TIME_ISO_8601 = 0x44; /** * Represents the OBEX Time header using the 4 byte representation. This is * only included for backwards compatibility. It represents the number of * seconds since January 1, 1970. *

* The value of TIME_4_BYTE is 0xC4 (196). */ public static final int TIME_4_BYTE = 0xC4; /** * Represents the OBEX Description header. This is a text description of the * object. *

* The value of DESCRIPTION is 0x05 (5). */ public static final int DESCRIPTION = 0x05; /** * Represents the OBEX Target header. This is the name of the service an * operation is targeted to. *

* The value of TARGET is 0x46 (70). */ public static final int TARGET = 0x46; /** * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be * included in a request or reply. *

* The value of HTTP is 0x47 (71). */ public static final int HTTP = 0x47; /** * Represents the OBEX BODY header. *

* The value of BODY is 0x48 (72). */ public static final int BODY = 0x48; /** * Represents the OBEX End of BODY header. *

* The value of BODY is 0x49 (73). */ public static final int END_OF_BODY = 0x49; /** * Represents the OBEX Who header. Identifies the OBEX application to * determine if the two peers are talking to each other. *

* The value of WHO is 0x4A (74). */ public static final int WHO = 0x4A; /** * Represents the OBEX Connection ID header. Identifies used for OBEX * connection multiplexing. *

* The value of CONNECTION_ID is 0xCB (203). */ public static final int CONNECTION_ID = 0xCB; /** * Represents the OBEX Application Parameter header. This header specifies * additional application request and response information. *

* The value of APPLICATION_PARAMETER is 0x4C (76). */ public static final int APPLICATION_PARAMETER = 0x4C; /** * Represents the OBEX authentication digest-challenge. *

* The value of AUTH_CHALLENGE is 0x4D (77). */ public static final int AUTH_CHALLENGE = 0x4D; /** * Represents the OBEX authentication digest-response. *

* The value of AUTH_RESPONSE is 0x4E (78). */ public static final int AUTH_RESPONSE = 0x4E; /** * Represents the OBEX Object Class header. This header specifies the OBEX * object class of the object. *

* The value of OBJECT_CLASS is 0x4F (79). */ public static final int OBJECT_CLASS = 0x4F; private Long mCount; // 4 byte unsigned integer private String mName; // null terminated Unicode text string private String mType; // null terminated ASCII text string private Long mLength; // 4 byte unsigend integer private Calendar mIsoTime; // String of the form YYYYMMDDTHHMMSSZ private Calendar mByteTime; // 4 byte unsigned integer private String mDescription; // null terminated Unicode text String private byte[] mTarget; // byte sequence private byte[] mHttpHeader; // byte sequence private byte[] mWho; // length prefixed byte sequence private byte[] mAppParam; // byte sequence of the form tag length value private byte[] mObjectClass; // byte sequence private String[] mUnicodeUserDefined; //null terminated unicode string private byte[][] mSequenceUserDefined; // byte sequence user defined private Byte[] mByteUserDefined; // 1 byte private Long[] mIntegerUserDefined; // 4 byte unsigned integer private final SecureRandom mRandom; /*package*/ byte[] nonce; public byte[] mAuthChall; // The authentication challenge header public byte[] mAuthResp; // The authentication response header public byte[] mConnectionID; // THe connection ID public int responseCode; /** * Creates new HeaderSet object. * @param size the max packet size for this connection */ public HeaderSet() { mUnicodeUserDefined = new String[16]; mSequenceUserDefined = new byte[16][]; mByteUserDefined = new Byte[16]; mIntegerUserDefined = new Long[16]; responseCode = -1; mRandom = new SecureRandom(); } /** * Sets the value of the header identifier to the value provided. The type * of object must correspond to the Java type defined in the description of * this interface. If null is passed as the * headerValue then the header will be removed from the set of * headers to include in the next request. * @param headerID the identifier to include in the message * @param headerValue the value of the header identifier * @throws IllegalArgumentException if the header identifier provided is not * one defined in this interface or a user-defined header; if the * type of headerValue is not the correct Java type as * defined in the description of this interface\ */ public void setHeader(int headerID, Object headerValue) { long temp = -1; switch (headerID) { case COUNT: if (!(headerValue instanceof Long)) { if (headerValue == null) { mCount = null; break; } throw new IllegalArgumentException("Count must be a Long"); } temp = ((Long)headerValue).longValue(); if ((temp < 0L) || (temp > 0xFFFFFFFFL)) { throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF"); } mCount = (Long)headerValue; break; case NAME: if ((headerValue != null) && (!(headerValue instanceof String))) { throw new IllegalArgumentException("Name must be a String"); } mName = (String)headerValue; break; case TYPE: if ((headerValue != null) && (!(headerValue instanceof String))) { throw new IllegalArgumentException("Type must be a String"); } mType = (String)headerValue; break; case LENGTH: if (!(headerValue instanceof Long)) { if (headerValue == null) { mLength = null; break; } throw new IllegalArgumentException("Length must be a Long"); } temp = ((Long)headerValue).longValue(); if ((temp < 0L) || (temp > 0xFFFFFFFFL)) { throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF"); } mLength = (Long)headerValue; break; case TIME_ISO_8601: if ((headerValue != null) && (!(headerValue instanceof Calendar))) { throw new IllegalArgumentException("Time ISO 8601 must be a Calendar"); } mIsoTime = (Calendar)headerValue; break; case TIME_4_BYTE: if ((headerValue != null) && (!(headerValue instanceof Calendar))) { throw new IllegalArgumentException("Time 4 Byte must be a Calendar"); } mByteTime = (Calendar)headerValue; break; case DESCRIPTION: if ((headerValue != null) && (!(headerValue instanceof String))) { throw new IllegalArgumentException("Description must be a String"); } mDescription = (String)headerValue; break; case TARGET: if (headerValue == null) { mTarget = null; } else { if (!(headerValue instanceof byte[])) { throw new IllegalArgumentException("Target must be a byte array"); } else { mTarget = new byte[((byte[])headerValue).length]; System.arraycopy(headerValue, 0, mTarget, 0, mTarget.length); } } break; case HTTP: if (headerValue == null) { mHttpHeader = null; } else { if (!(headerValue instanceof byte[])) { throw new IllegalArgumentException("HTTP must be a byte array"); } else { mHttpHeader = new byte[((byte[])headerValue).length]; System.arraycopy(headerValue, 0, mHttpHeader, 0, mHttpHeader.length); } } break; case WHO: if (headerValue == null) { mWho = null; } else { if (!(headerValue instanceof byte[])) { throw new IllegalArgumentException("WHO must be a byte array"); } else { mWho = new byte[((byte[])headerValue).length]; System.arraycopy(headerValue, 0, mWho, 0, mWho.length); } } break; case OBJECT_CLASS: if (headerValue == null) { mObjectClass = null; } else { if (!(headerValue instanceof byte[])) { throw new IllegalArgumentException("Object Class must be a byte array"); } else { mObjectClass = new byte[((byte[])headerValue).length]; System.arraycopy(headerValue, 0, mObjectClass, 0, mObjectClass.length); } } break; case APPLICATION_PARAMETER: if (headerValue == null) { mAppParam = null; } else { if (!(headerValue instanceof byte[])) { throw new IllegalArgumentException( "Application Parameter must be a byte array"); } else { mAppParam = new byte[((byte[])headerValue).length]; System.arraycopy(headerValue, 0, mAppParam, 0, mAppParam.length); } } break; default: // Verify that it was not a Unicode String user Defined if ((headerID >= 0x30) && (headerID <= 0x3F)) { if ((headerValue != null) && (!(headerValue instanceof String))) { throw new IllegalArgumentException( "Unicode String User Defined must be a String"); } mUnicodeUserDefined[headerID - 0x30] = (String)headerValue; break; } // Verify that it was not a byte sequence user defined value if ((headerID >= 0x70) && (headerID <= 0x7F)) { if (headerValue == null) { mSequenceUserDefined[headerID - 0x70] = null; } else { if (!(headerValue instanceof byte[])) { throw new IllegalArgumentException( "Byte Sequence User Defined must be a byte array"); } else { mSequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length]; System.arraycopy(headerValue, 0, mSequenceUserDefined[headerID - 0x70], 0, mSequenceUserDefined[headerID - 0x70].length); } } break; } // Verify that it was not a Byte user Defined if ((headerID >= 0xB0) && (headerID <= 0xBF)) { if ((headerValue != null) && (!(headerValue instanceof Byte))) { throw new IllegalArgumentException("ByteUser Defined must be a Byte"); } mByteUserDefined[headerID - 0xB0] = (Byte)headerValue; break; } // Verify that is was not the 4 byte unsigned integer user // defined header if ((headerID >= 0xF0) && (headerID <= 0xFF)) { if (!(headerValue instanceof Long)) { if (headerValue == null) { mIntegerUserDefined[headerID - 0xF0] = null; break; } throw new IllegalArgumentException("Integer User Defined must be a Long"); } temp = ((Long)headerValue).longValue(); if ((temp < 0L) || (temp > 0xFFFFFFFFL)) { throw new IllegalArgumentException( "Integer User Defined must be between 0 and 0xFFFFFFFF"); } mIntegerUserDefined[headerID - 0xF0] = (Long)headerValue; break; } throw new IllegalArgumentException("Invalid Header Identifier"); } } /** * Retrieves the value of the header identifier provided. The type of the * Object returned is defined in the description of this interface. * @param headerID the header identifier whose value is to be returned * @return the value of the header provided or null if the * header identifier specified is not part of this * HeaderSet object * @throws IllegalArgumentException if the headerID is not one * defined in this interface or any of the user-defined headers * @throws IOException if an error occurred in the transport layer during * the operation or if the connection has been closed */ public Object getHeader(int headerID) throws IOException { switch (headerID) { case COUNT: return mCount; case NAME: return mName; case TYPE: return mType; case LENGTH: return mLength; case TIME_ISO_8601: return mIsoTime; case TIME_4_BYTE: return mByteTime; case DESCRIPTION: return mDescription; case TARGET: return mTarget; case HTTP: return mHttpHeader; case WHO: return mWho; case OBJECT_CLASS: return mObjectClass; case APPLICATION_PARAMETER: return mAppParam; default: // Verify that it was not a Unicode String user Defined if ((headerID >= 0x30) && (headerID <= 0x3F)) { return mUnicodeUserDefined[headerID - 0x30]; } // Verify that it was not a byte sequence user defined header if ((headerID >= 0x70) && (headerID <= 0x7F)) { return mSequenceUserDefined[headerID - 0x70]; } // Verify that it was not a byte user defined header if ((headerID >= 0xB0) && (headerID <= 0xBF)) { return mByteUserDefined[headerID - 0xB0]; } // Verify that it was not a integer user defined header if ((headerID >= 0xF0) && (headerID <= 0xFF)) { return mIntegerUserDefined[headerID - 0xF0]; } throw new IllegalArgumentException("Invalid Header Identifier"); } } /** * Retrieves the list of headers that may be retrieved via the * getHeader method that will not return null. In * other words, this method returns all the headers that are available in * this object. * @see #getHeader * @return the array of headers that are set in this object or * null if no headers are available * @throws IOException if an error occurred in the transport layer during * the operation or the connection has been closed */ public int[] getHeaderList() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); if (mCount != null) { out.write(COUNT); } if (mName != null) { out.write(NAME); } if (mType != null) { out.write(TYPE); } if (mLength != null) { out.write(LENGTH); } if (mIsoTime != null) { out.write(TIME_ISO_8601); } if (mByteTime != null) { out.write(TIME_4_BYTE); } if (mDescription != null) { out.write(DESCRIPTION); } if (mTarget != null) { out.write(TARGET); } if (mHttpHeader != null) { out.write(HTTP); } if (mWho != null) { out.write(WHO); } if (mAppParam != null) { out.write(APPLICATION_PARAMETER); } if (mObjectClass != null) { out.write(OBJECT_CLASS); } for (int i = 0x30; i < 0x40; i++) { if (mUnicodeUserDefined[i - 0x30] != null) { out.write(i); } } for (int i = 0x70; i < 0x80; i++) { if (mSequenceUserDefined[i - 0x70] != null) { out.write(i); } } for (int i = 0xB0; i < 0xC0; i++) { if (mByteUserDefined[i - 0xB0] != null) { out.write(i); } } for (int i = 0xF0; i < 0x100; i++) { if (mIntegerUserDefined[i - 0xF0] != null) { out.write(i); } } byte[] headers = out.toByteArray(); out.close(); if ((headers == null) || (headers.length == 0)) { return null; } int[] result = new int[headers.length]; for (int i = 0; i < headers.length; i++) { // Convert the byte to a positive integer. That is, an integer // between 0 and 256. result[i] = headers[i] & 0xFF; } return result; } /** * Sets the authentication challenge header. The realm will be * encoded based upon the default encoding scheme used by the implementation * to encode strings. Therefore, the encoding scheme used to encode the * realm is application dependent. * @param realm a short description that describes what password to use; if * null no realm will be sent in the authentication * challenge header * @param userID if true, a user ID is required in the reply; * if false, no user ID is required * @param access if true then full access will be granted if * successful; if false then read-only access will be * granted if successful * @throws IOException */ public void createAuthenticationChallenge(String realm, boolean userID, boolean access) throws IOException { nonce = new byte[16]; for (int i = 0; i < 16; i++) { nonce[i] = (byte)mRandom.nextInt(); } mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID); } /** * Returns the response code received from the server. Response codes are * defined in the ResponseCodes class. * @see ResponseCodes * @return the response code retrieved from the server * @throws IOException if an error occurred in the transport layer during * the transaction; if this method is called on a * HeaderSet object created by calling * createHeaderSet() in a ClientSession * object; if this object was created by an OBEX server */ public int getResponseCode() throws IOException { if (responseCode == -1) { throw new IOException("May not be called on a server"); } else { return responseCode; } } }