2 * Copyright (c) 2008-2009, Motorola, Inc.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * - Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * - Neither the name of the Motorola, Inc. nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
35 import java.io.ByteArrayOutputStream;
36 import java.io.IOException;
37 import java.util.Calendar;
38 import java.security.SecureRandom;
41 * This class implements the javax.obex.HeaderSet interface for OBEX over
45 public final class HeaderSet {
48 * Represents the OBEX Count header. This allows the connection statement to
49 * tell the server how many objects it plans to send or retrieve.
51 * The value of <code>COUNT</code> is 0xC0 (192).
53 public static final int COUNT = 0xC0;
56 * Represents the OBEX Name header. This specifies the name of the object.
58 * The value of <code>NAME</code> is 0x01 (1).
60 public static final int NAME = 0x01;
63 * Represents the OBEX Type header. This allows a request to specify the
64 * type of the object (e.g. text, html, binary, etc.).
66 * The value of <code>TYPE</code> is 0x42 (66).
68 public static final int TYPE = 0x42;
71 * Represents the OBEX Length header. This is the length of the object in
74 * The value of <code>LENGTH</code> is 0xC3 (195).
76 public static final int LENGTH = 0xC3;
79 * Represents the OBEX Time header using the ISO 8601 standards. This is the
80 * preferred time header.
82 * The value of <code>TIME_ISO_8601</code> is 0x44 (68).
84 public static final int TIME_ISO_8601 = 0x44;
87 * Represents the OBEX Time header using the 4 byte representation. This is
88 * only included for backwards compatibility. It represents the number of
89 * seconds since January 1, 1970.
91 * The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
93 public static final int TIME_4_BYTE = 0xC4;
96 * Represents the OBEX Description header. This is a text description of the
99 * The value of <code>DESCRIPTION</code> is 0x05 (5).
101 public static final int DESCRIPTION = 0x05;
104 * Represents the OBEX Target header. This is the name of the service an
105 * operation is targeted to.
107 * The value of <code>TARGET</code> is 0x46 (70).
109 public static final int TARGET = 0x46;
112 * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be
113 * included in a request or reply.
115 * The value of <code>HTTP</code> is 0x47 (71).
117 public static final int HTTP = 0x47;
120 * Represents the OBEX BODY header.
122 * The value of <code>BODY</code> is 0x48 (72).
124 public static final int BODY = 0x48;
127 * Represents the OBEX End of BODY header.
129 * The value of <code>BODY</code> is 0x49 (73).
131 public static final int END_OF_BODY = 0x49;
134 * Represents the OBEX Who header. Identifies the OBEX application to
135 * determine if the two peers are talking to each other.
137 * The value of <code>WHO</code> is 0x4A (74).
139 public static final int WHO = 0x4A;
142 * Represents the OBEX Connection ID header. Identifies used for OBEX
143 * connection multiplexing.
145 * The value of <code>CONNECTION_ID</code> is 0xCB (203).
148 public static final int CONNECTION_ID = 0xCB;
151 * Represents the OBEX Application Parameter header. This header specifies
152 * additional application request and response information.
154 * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
156 public static final int APPLICATION_PARAMETER = 0x4C;
159 * Represents the OBEX authentication digest-challenge.
161 * The value of <code>AUTH_CHALLENGE</code> is 0x4D (77).
163 public static final int AUTH_CHALLENGE = 0x4D;
166 * Represents the OBEX authentication digest-response.
168 * The value of <code>AUTH_RESPONSE</code> is 0x4E (78).
170 public static final int AUTH_RESPONSE = 0x4E;
173 * Represents the OBEX Object Class header. This header specifies the OBEX
174 * object class of the object.
176 * The value of <code>OBJECT_CLASS</code> is 0x4F (79).
178 public static final int OBJECT_CLASS = 0x4F;
180 private Long mCount; // 4 byte unsigned integer
182 private String mName; // null terminated Unicode text string
184 private String mType; // null terminated ASCII text string
186 private Long mLength; // 4 byte unsigend integer
188 private Calendar mIsoTime; // String of the form YYYYMMDDTHHMMSSZ
190 private Calendar mByteTime; // 4 byte unsigned integer
192 private String mDescription; // null terminated Unicode text String
194 private byte[] mTarget; // byte sequence
196 private byte[] mHttpHeader; // byte sequence
198 private byte[] mWho; // length prefixed byte sequence
200 private byte[] mAppParam; // byte sequence of the form tag length value
202 private byte[] mObjectClass; // byte sequence
204 private String[] mUnicodeUserDefined; //null terminated unicode string
206 private byte[][] mSequenceUserDefined; // byte sequence user defined
208 private Byte[] mByteUserDefined; // 1 byte
210 private Long[] mIntegerUserDefined; // 4 byte unsigned integer
212 private final SecureRandom mRandom;
214 /*package*/ byte[] nonce;
216 public byte[] mAuthChall; // The authentication challenge header
218 public byte[] mAuthResp; // The authentication response header
220 public byte[] mConnectionID; // THe connection ID
222 public int responseCode;
225 * Creates new <code>HeaderSet</code> object.
226 * @param size the max packet size for this connection
229 mUnicodeUserDefined = new String[16];
230 mSequenceUserDefined = new byte[16][];
231 mByteUserDefined = new Byte[16];
232 mIntegerUserDefined = new Long[16];
234 mRandom = new SecureRandom();
238 * Sets the value of the header identifier to the value provided. The type
239 * of object must correspond to the Java type defined in the description of
240 * this interface. If <code>null</code> is passed as the
241 * <code>headerValue</code> then the header will be removed from the set of
242 * headers to include in the next request.
243 * @param headerID the identifier to include in the message
244 * @param headerValue the value of the header identifier
245 * @throws IllegalArgumentException if the header identifier provided is not
246 * one defined in this interface or a user-defined header; if the
247 * type of <code>headerValue</code> is not the correct Java type as
248 * defined in the description of this interface\
250 public void setHeader(int headerID, Object headerValue) {
255 if (!(headerValue instanceof Long)) {
256 if (headerValue == null) {
260 throw new IllegalArgumentException("Count must be a Long");
262 temp = ((Long)headerValue).longValue();
263 if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
264 throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF");
266 mCount = (Long)headerValue;
269 if ((headerValue != null) && (!(headerValue instanceof String))) {
270 throw new IllegalArgumentException("Name must be a String");
272 mName = (String)headerValue;
275 if ((headerValue != null) && (!(headerValue instanceof String))) {
276 throw new IllegalArgumentException("Type must be a String");
278 mType = (String)headerValue;
281 if (!(headerValue instanceof Long)) {
282 if (headerValue == null) {
286 throw new IllegalArgumentException("Length must be a Long");
288 temp = ((Long)headerValue).longValue();
289 if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
290 throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF");
292 mLength = (Long)headerValue;
295 if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
296 throw new IllegalArgumentException("Time ISO 8601 must be a Calendar");
298 mIsoTime = (Calendar)headerValue;
301 if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
302 throw new IllegalArgumentException("Time 4 Byte must be a Calendar");
304 mByteTime = (Calendar)headerValue;
307 if ((headerValue != null) && (!(headerValue instanceof String))) {
308 throw new IllegalArgumentException("Description must be a String");
310 mDescription = (String)headerValue;
313 if (headerValue == null) {
316 if (!(headerValue instanceof byte[])) {
317 throw new IllegalArgumentException("Target must be a byte array");
319 mTarget = new byte[((byte[])headerValue).length];
320 System.arraycopy(headerValue, 0, mTarget, 0, mTarget.length);
325 if (headerValue == null) {
328 if (!(headerValue instanceof byte[])) {
329 throw new IllegalArgumentException("HTTP must be a byte array");
331 mHttpHeader = new byte[((byte[])headerValue).length];
332 System.arraycopy(headerValue, 0, mHttpHeader, 0, mHttpHeader.length);
337 if (headerValue == null) {
340 if (!(headerValue instanceof byte[])) {
341 throw new IllegalArgumentException("WHO must be a byte array");
343 mWho = new byte[((byte[])headerValue).length];
344 System.arraycopy(headerValue, 0, mWho, 0, mWho.length);
349 if (headerValue == null) {
352 if (!(headerValue instanceof byte[])) {
353 throw new IllegalArgumentException("Object Class must be a byte array");
355 mObjectClass = new byte[((byte[])headerValue).length];
356 System.arraycopy(headerValue, 0, mObjectClass, 0, mObjectClass.length);
360 case APPLICATION_PARAMETER:
361 if (headerValue == null) {
364 if (!(headerValue instanceof byte[])) {
365 throw new IllegalArgumentException(
366 "Application Parameter must be a byte array");
368 mAppParam = new byte[((byte[])headerValue).length];
369 System.arraycopy(headerValue, 0, mAppParam, 0, mAppParam.length);
374 // Verify that it was not a Unicode String user Defined
375 if ((headerID >= 0x30) && (headerID <= 0x3F)) {
376 if ((headerValue != null) && (!(headerValue instanceof String))) {
377 throw new IllegalArgumentException(
378 "Unicode String User Defined must be a String");
380 mUnicodeUserDefined[headerID - 0x30] = (String)headerValue;
384 // Verify that it was not a byte sequence user defined value
385 if ((headerID >= 0x70) && (headerID <= 0x7F)) {
387 if (headerValue == null) {
388 mSequenceUserDefined[headerID - 0x70] = null;
390 if (!(headerValue instanceof byte[])) {
391 throw new IllegalArgumentException(
392 "Byte Sequence User Defined must be a byte array");
394 mSequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length];
395 System.arraycopy(headerValue, 0, mSequenceUserDefined[headerID - 0x70],
396 0, mSequenceUserDefined[headerID - 0x70].length);
401 // Verify that it was not a Byte user Defined
402 if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
403 if ((headerValue != null) && (!(headerValue instanceof Byte))) {
404 throw new IllegalArgumentException("ByteUser Defined must be a Byte");
406 mByteUserDefined[headerID - 0xB0] = (Byte)headerValue;
410 // Verify that is was not the 4 byte unsigned integer user
412 if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
413 if (!(headerValue instanceof Long)) {
414 if (headerValue == null) {
415 mIntegerUserDefined[headerID - 0xF0] = null;
418 throw new IllegalArgumentException("Integer User Defined must be a Long");
420 temp = ((Long)headerValue).longValue();
421 if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
422 throw new IllegalArgumentException(
423 "Integer User Defined must be between 0 and 0xFFFFFFFF");
425 mIntegerUserDefined[headerID - 0xF0] = (Long)headerValue;
428 throw new IllegalArgumentException("Invalid Header Identifier");
433 * Retrieves the value of the header identifier provided. The type of the
434 * Object returned is defined in the description of this interface.
435 * @param headerID the header identifier whose value is to be returned
436 * @return the value of the header provided or <code>null</code> if the
437 * header identifier specified is not part of this
438 * <code>HeaderSet</code> object
439 * @throws IllegalArgumentException if the <code>headerID</code> is not one
440 * defined in this interface or any of the user-defined headers
441 * @throws IOException if an error occurred in the transport layer during
442 * the operation or if the connection has been closed
444 public Object getHeader(int headerID) throws IOException {
469 case APPLICATION_PARAMETER:
472 // Verify that it was not a Unicode String user Defined
473 if ((headerID >= 0x30) && (headerID <= 0x3F)) {
474 return mUnicodeUserDefined[headerID - 0x30];
476 // Verify that it was not a byte sequence user defined header
477 if ((headerID >= 0x70) && (headerID <= 0x7F)) {
478 return mSequenceUserDefined[headerID - 0x70];
480 // Verify that it was not a byte user defined header
481 if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
482 return mByteUserDefined[headerID - 0xB0];
484 // Verify that it was not a integer user defined header
485 if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
486 return mIntegerUserDefined[headerID - 0xF0];
488 throw new IllegalArgumentException("Invalid Header Identifier");
493 * Retrieves the list of headers that may be retrieved via the
494 * <code>getHeader</code> method that will not return <code>null</code>. In
495 * other words, this method returns all the headers that are available in
498 * @return the array of headers that are set in this object or
499 * <code>null</code> if no headers are available
500 * @throws IOException if an error occurred in the transport layer during
501 * the operation or the connection has been closed
503 public int[] getHeaderList() throws IOException {
504 ByteArrayOutputStream out = new ByteArrayOutputStream();
506 if (mCount != null) {
515 if (mLength != null) {
518 if (mIsoTime != null) {
519 out.write(TIME_ISO_8601);
521 if (mByteTime != null) {
522 out.write(TIME_4_BYTE);
524 if (mDescription != null) {
525 out.write(DESCRIPTION);
527 if (mTarget != null) {
530 if (mHttpHeader != null) {
536 if (mAppParam != null) {
537 out.write(APPLICATION_PARAMETER);
539 if (mObjectClass != null) {
540 out.write(OBJECT_CLASS);
543 for (int i = 0x30; i < 0x40; i++) {
544 if (mUnicodeUserDefined[i - 0x30] != null) {
549 for (int i = 0x70; i < 0x80; i++) {
550 if (mSequenceUserDefined[i - 0x70] != null) {
555 for (int i = 0xB0; i < 0xC0; i++) {
556 if (mByteUserDefined[i - 0xB0] != null) {
561 for (int i = 0xF0; i < 0x100; i++) {
562 if (mIntegerUserDefined[i - 0xF0] != null) {
567 byte[] headers = out.toByteArray();
570 if ((headers == null) || (headers.length == 0)) {
574 int[] result = new int[headers.length];
575 for (int i = 0; i < headers.length; i++) {
576 // Convert the byte to a positive integer. That is, an integer
577 // between 0 and 256.
578 result[i] = headers[i] & 0xFF;
585 * Sets the authentication challenge header. The <code>realm</code> will be
586 * encoded based upon the default encoding scheme used by the implementation
587 * to encode strings. Therefore, the encoding scheme used to encode the
588 * <code>realm</code> is application dependent.
589 * @param realm a short description that describes what password to use; if
590 * <code>null</code> no realm will be sent in the authentication
592 * @param userID if <code>true</code>, a user ID is required in the reply;
593 * if <code>false</code>, no user ID is required
594 * @param access if <code>true</code> then full access will be granted if
595 * successful; if <code>false</code> then read-only access will be
596 * granted if successful
597 * @throws IOException
599 public void createAuthenticationChallenge(String realm, boolean userID, boolean access)
602 nonce = new byte[16];
603 for (int i = 0; i < 16; i++) {
604 nonce[i] = (byte)mRandom.nextInt();
607 mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
611 * Returns the response code received from the server. Response codes are
612 * defined in the <code>ResponseCodes</code> class.
614 * @return the response code retrieved from the server
615 * @throws IOException if an error occurred in the transport layer during
616 * the transaction; if this method is called on a
617 * <code>HeaderSet</code> object created by calling
618 * <code>createHeaderSet()</code> in a <code>ClientSession</code>
619 * object; if this object was created by an OBEX server
621 public int getResponseCode() throws IOException {
622 if (responseCode == -1) {
623 throw new IOException("May not be called on a server");