1/*
2 * Copyright (c) 2008-2009, Motorola, Inc.
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * - Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
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.
15 *
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.
19 *
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.
31 */
32
33package javax.obex;
34
35import java.io.ByteArrayOutputStream;
36import java.io.IOException;
37import java.util.Calendar;
38import java.security.SecureRandom;
39
40/**
41 * This class implements the javax.obex.HeaderSet interface for OBEX over
42 * RFCOMM.
43 * @hide
44 */
45public final class HeaderSet {
46
47    /**
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.
50     * <P>
51     * The value of <code>COUNT</code> is 0xC0 (192).
52     */
53    public static final int COUNT = 0xC0;
54
55    /**
56     * Represents the OBEX Name header. This specifies the name of the object.
57     * <P>
58     * The value of <code>NAME</code> is 0x01 (1).
59     */
60    public static final int NAME = 0x01;
61
62    /**
63     * Represents the OBEX Type header. This allows a request to specify the
64     * type of the object (e.g. text, html, binary, etc.).
65     * <P>
66     * The value of <code>TYPE</code> is 0x42 (66).
67     */
68    public static final int TYPE = 0x42;
69
70    /**
71     * Represents the OBEX Length header. This is the length of the object in
72     * bytes.
73     * <P>
74     * The value of <code>LENGTH</code> is 0xC3 (195).
75     */
76    public static final int LENGTH = 0xC3;
77
78    /**
79     * Represents the OBEX Time header using the ISO 8601 standards. This is the
80     * preferred time header.
81     * <P>
82     * The value of <code>TIME_ISO_8601</code> is 0x44 (68).
83     */
84    public static final int TIME_ISO_8601 = 0x44;
85
86    /**
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.
90     * <P>
91     * The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
92     */
93    public static final int TIME_4_BYTE = 0xC4;
94
95    /**
96     * Represents the OBEX Description header. This is a text description of the
97     * object.
98     * <P>
99     * The value of <code>DESCRIPTION</code> is 0x05 (5).
100     */
101    public static final int DESCRIPTION = 0x05;
102
103    /**
104     * Represents the OBEX Target header. This is the name of the service an
105     * operation is targeted to.
106     * <P>
107     * The value of <code>TARGET</code> is 0x46 (70).
108     */
109    public static final int TARGET = 0x46;
110
111    /**
112     * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be
113     * included in a request or reply.
114     * <P>
115     * The value of <code>HTTP</code> is 0x47 (71).
116     */
117    public static final int HTTP = 0x47;
118
119    /**
120     * Represents the OBEX BODY header.
121     * <P>
122     * The value of <code>BODY</code> is 0x48 (72).
123     */
124    public static final int BODY = 0x48;
125
126    /**
127     * Represents the OBEX End of BODY header.
128     * <P>
129     * The value of <code>BODY</code> is 0x49 (73).
130     */
131    public static final int END_OF_BODY = 0x49;
132
133    /**
134     * Represents the OBEX Who header. Identifies the OBEX application to
135     * determine if the two peers are talking to each other.
136     * <P>
137     * The value of <code>WHO</code> is 0x4A (74).
138     */
139    public static final int WHO = 0x4A;
140
141    /**
142     * Represents the OBEX Connection ID header. Identifies used for OBEX
143     * connection multiplexing.
144     * <P>
145     * The value of <code>CONNECTION_ID</code> is 0xCB (203).
146     */
147
148    public static final int CONNECTION_ID = 0xCB;
149
150    /**
151     * Represents the OBEX Application Parameter header. This header specifies
152     * additional application request and response information.
153     * <P>
154     * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
155     */
156    public static final int APPLICATION_PARAMETER = 0x4C;
157
158    /**
159     * Represents the OBEX authentication digest-challenge.
160     * <P>
161     * The value of <code>AUTH_CHALLENGE</code> is 0x4D (77).
162     */
163    public static final int AUTH_CHALLENGE = 0x4D;
164
165    /**
166     * Represents the OBEX authentication digest-response.
167     * <P>
168     * The value of <code>AUTH_RESPONSE</code> is 0x4E (78).
169     */
170    public static final int AUTH_RESPONSE = 0x4E;
171
172    /**
173     * Represents the OBEX Object Class header. This header specifies the OBEX
174     * object class of the object.
175     * <P>
176     * The value of <code>OBJECT_CLASS</code> is 0x4F (79).
177     */
178    public static final int OBJECT_CLASS = 0x4F;
179
180    private Long mCount; // 4 byte unsigned integer
181
182    private String mName; // null terminated Unicode text string
183
184    private String mType; // null terminated ASCII text string
185
186    private Long mLength; // 4 byte unsigend integer
187
188    private Calendar mIsoTime; // String of the form YYYYMMDDTHHMMSSZ
189
190    private Calendar mByteTime; // 4 byte unsigned integer
191
192    private String mDescription; // null terminated Unicode text String
193
194    private byte[] mTarget; // byte sequence
195
196    private byte[] mHttpHeader; // byte sequence
197
198    private byte[] mWho; // length prefixed byte sequence
199
200    private byte[] mAppParam; // byte sequence of the form tag length value
201
202    private byte[] mObjectClass; // byte sequence
203
204    private String[] mUnicodeUserDefined; //null terminated unicode string
205
206    private byte[][] mSequenceUserDefined; // byte sequence user defined
207
208    private Byte[] mByteUserDefined; // 1 byte
209
210    private Long[] mIntegerUserDefined; // 4 byte unsigned integer
211
212    private final SecureRandom mRandom;
213
214    /*package*/ byte[] nonce;
215
216    public byte[] mAuthChall; // The authentication challenge header
217
218    public byte[] mAuthResp; // The authentication response header
219
220    public byte[] mConnectionID; // THe connection ID
221
222    public int responseCode;
223
224    /**
225     * Creates new <code>HeaderSet</code> object.
226     * @param size the max packet size for this connection
227     */
228    public HeaderSet() {
229        mUnicodeUserDefined = new String[16];
230        mSequenceUserDefined = new byte[16][];
231        mByteUserDefined = new Byte[16];
232        mIntegerUserDefined = new Long[16];
233        responseCode = -1;
234        mRandom = new SecureRandom();
235    }
236
237    /**
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\
249     */
250    public void setHeader(int headerID, Object headerValue) {
251        long temp = -1;
252
253        switch (headerID) {
254            case COUNT:
255                if (!(headerValue instanceof Long)) {
256                    if (headerValue == null) {
257                        mCount = null;
258                        break;
259                    }
260                    throw new IllegalArgumentException("Count must be a Long");
261                }
262                temp = ((Long)headerValue).longValue();
263                if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
264                    throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF");
265                }
266                mCount = (Long)headerValue;
267                break;
268            case NAME:
269                if ((headerValue != null) && (!(headerValue instanceof String))) {
270                    throw new IllegalArgumentException("Name must be a String");
271                }
272                mName = (String)headerValue;
273                break;
274            case TYPE:
275                if ((headerValue != null) && (!(headerValue instanceof String))) {
276                    throw new IllegalArgumentException("Type must be a String");
277                }
278                mType = (String)headerValue;
279                break;
280            case LENGTH:
281                if (!(headerValue instanceof Long)) {
282                    if (headerValue == null) {
283                        mLength = null;
284                        break;
285                    }
286                    throw new IllegalArgumentException("Length must be a Long");
287                }
288                temp = ((Long)headerValue).longValue();
289                if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
290                    throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF");
291                }
292                mLength = (Long)headerValue;
293                break;
294            case TIME_ISO_8601:
295                if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
296                    throw new IllegalArgumentException("Time ISO 8601 must be a Calendar");
297                }
298                mIsoTime = (Calendar)headerValue;
299                break;
300            case TIME_4_BYTE:
301                if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
302                    throw new IllegalArgumentException("Time 4 Byte must be a Calendar");
303                }
304                mByteTime = (Calendar)headerValue;
305                break;
306            case DESCRIPTION:
307                if ((headerValue != null) && (!(headerValue instanceof String))) {
308                    throw new IllegalArgumentException("Description must be a String");
309                }
310                mDescription = (String)headerValue;
311                break;
312            case TARGET:
313                if (headerValue == null) {
314                    mTarget = null;
315                } else {
316                    if (!(headerValue instanceof byte[])) {
317                        throw new IllegalArgumentException("Target must be a byte array");
318                    } else {
319                        mTarget = new byte[((byte[])headerValue).length];
320                        System.arraycopy(headerValue, 0, mTarget, 0, mTarget.length);
321                    }
322                }
323                break;
324            case HTTP:
325                if (headerValue == null) {
326                    mHttpHeader = null;
327                } else {
328                    if (!(headerValue instanceof byte[])) {
329                        throw new IllegalArgumentException("HTTP must be a byte array");
330                    } else {
331                        mHttpHeader = new byte[((byte[])headerValue).length];
332                        System.arraycopy(headerValue, 0, mHttpHeader, 0, mHttpHeader.length);
333                    }
334                }
335                break;
336            case WHO:
337                if (headerValue == null) {
338                    mWho = null;
339                } else {
340                    if (!(headerValue instanceof byte[])) {
341                        throw new IllegalArgumentException("WHO must be a byte array");
342                    } else {
343                        mWho = new byte[((byte[])headerValue).length];
344                        System.arraycopy(headerValue, 0, mWho, 0, mWho.length);
345                    }
346                }
347                break;
348            case OBJECT_CLASS:
349                if (headerValue == null) {
350                    mObjectClass = null;
351                } else {
352                    if (!(headerValue instanceof byte[])) {
353                        throw new IllegalArgumentException("Object Class must be a byte array");
354                    } else {
355                        mObjectClass = new byte[((byte[])headerValue).length];
356                        System.arraycopy(headerValue, 0, mObjectClass, 0, mObjectClass.length);
357                    }
358                }
359                break;
360            case APPLICATION_PARAMETER:
361                if (headerValue == null) {
362                    mAppParam = null;
363                } else {
364                    if (!(headerValue instanceof byte[])) {
365                        throw new IllegalArgumentException(
366                                "Application Parameter must be a byte array");
367                    } else {
368                        mAppParam = new byte[((byte[])headerValue).length];
369                        System.arraycopy(headerValue, 0, mAppParam, 0, mAppParam.length);
370                    }
371                }
372                break;
373            default:
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");
379                    }
380                    mUnicodeUserDefined[headerID - 0x30] = (String)headerValue;
381
382                    break;
383                }
384                // Verify that it was not a byte sequence user defined value
385                if ((headerID >= 0x70) && (headerID <= 0x7F)) {
386
387                    if (headerValue == null) {
388                        mSequenceUserDefined[headerID - 0x70] = null;
389                    } else {
390                        if (!(headerValue instanceof byte[])) {
391                            throw new IllegalArgumentException(
392                                    "Byte Sequence User Defined must be a byte array");
393                        } else {
394                            mSequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length];
395                            System.arraycopy(headerValue, 0, mSequenceUserDefined[headerID - 0x70],
396                                    0, mSequenceUserDefined[headerID - 0x70].length);
397                        }
398                    }
399                    break;
400                }
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");
405                    }
406                    mByteUserDefined[headerID - 0xB0] = (Byte)headerValue;
407
408                    break;
409                }
410                // Verify that is was not the 4 byte unsigned integer user
411                // defined header
412                if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
413                    if (!(headerValue instanceof Long)) {
414                        if (headerValue == null) {
415                            mIntegerUserDefined[headerID - 0xF0] = null;
416                            break;
417                        }
418                        throw new IllegalArgumentException("Integer User Defined must be a Long");
419                    }
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");
424                    }
425                    mIntegerUserDefined[headerID - 0xF0] = (Long)headerValue;
426                    break;
427                }
428                throw new IllegalArgumentException("Invalid Header Identifier");
429        }
430    }
431
432    /**
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
443     */
444    public Object getHeader(int headerID) throws IOException {
445
446        switch (headerID) {
447            case COUNT:
448                return mCount;
449            case NAME:
450                return mName;
451            case TYPE:
452                return mType;
453            case LENGTH:
454                return mLength;
455            case TIME_ISO_8601:
456                return mIsoTime;
457            case TIME_4_BYTE:
458                return mByteTime;
459            case DESCRIPTION:
460                return mDescription;
461            case TARGET:
462                return mTarget;
463            case HTTP:
464                return mHttpHeader;
465            case WHO:
466                return mWho;
467            case OBJECT_CLASS:
468                return mObjectClass;
469            case APPLICATION_PARAMETER:
470                return mAppParam;
471            default:
472                // Verify that it was not a Unicode String user Defined
473                if ((headerID >= 0x30) && (headerID <= 0x3F)) {
474                    return mUnicodeUserDefined[headerID - 0x30];
475                }
476                // Verify that it was not a byte sequence user defined header
477                if ((headerID >= 0x70) && (headerID <= 0x7F)) {
478                    return mSequenceUserDefined[headerID - 0x70];
479                }
480                // Verify that it was not a byte user defined header
481                if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
482                    return mByteUserDefined[headerID - 0xB0];
483                }
484                // Verify that it was not a integer user defined header
485                if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
486                    return mIntegerUserDefined[headerID - 0xF0];
487                }
488                throw new IllegalArgumentException("Invalid Header Identifier");
489        }
490    }
491
492    /**
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
496     * this object.
497     * @see #getHeader
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
502     */
503    public int[] getHeaderList() throws IOException {
504        ByteArrayOutputStream out = new ByteArrayOutputStream();
505
506        if (mCount != null) {
507            out.write(COUNT);
508        }
509        if (mName != null) {
510            out.write(NAME);
511        }
512        if (mType != null) {
513            out.write(TYPE);
514        }
515        if (mLength != null) {
516            out.write(LENGTH);
517        }
518        if (mIsoTime != null) {
519            out.write(TIME_ISO_8601);
520        }
521        if (mByteTime != null) {
522            out.write(TIME_4_BYTE);
523        }
524        if (mDescription != null) {
525            out.write(DESCRIPTION);
526        }
527        if (mTarget != null) {
528            out.write(TARGET);
529        }
530        if (mHttpHeader != null) {
531            out.write(HTTP);
532        }
533        if (mWho != null) {
534            out.write(WHO);
535        }
536        if (mAppParam != null) {
537            out.write(APPLICATION_PARAMETER);
538        }
539        if (mObjectClass != null) {
540            out.write(OBJECT_CLASS);
541        }
542
543        for (int i = 0x30; i < 0x40; i++) {
544            if (mUnicodeUserDefined[i - 0x30] != null) {
545                out.write(i);
546            }
547        }
548
549        for (int i = 0x70; i < 0x80; i++) {
550            if (mSequenceUserDefined[i - 0x70] != null) {
551                out.write(i);
552            }
553        }
554
555        for (int i = 0xB0; i < 0xC0; i++) {
556            if (mByteUserDefined[i - 0xB0] != null) {
557                out.write(i);
558            }
559        }
560
561        for (int i = 0xF0; i < 0x100; i++) {
562            if (mIntegerUserDefined[i - 0xF0] != null) {
563                out.write(i);
564            }
565        }
566
567        byte[] headers = out.toByteArray();
568        out.close();
569
570        if ((headers == null) || (headers.length == 0)) {
571            return null;
572        }
573
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;
579        }
580
581        return result;
582    }
583
584    /**
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
591     *        challenge header
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
598     */
599    public void createAuthenticationChallenge(String realm, boolean userID, boolean access)
600            throws IOException {
601
602        nonce = new byte[16];
603        for (int i = 0; i < 16; i++) {
604            nonce[i] = (byte)mRandom.nextInt();
605        }
606
607        mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
608    }
609
610    /**
611     * Returns the response code received from the server. Response codes are
612     * defined in the <code>ResponseCodes</code> class.
613     * @see ResponseCodes
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
620     */
621    public int getResponseCode() throws IOException {
622        if (responseCode == -1) {
623            throw new IOException("May not be called on a server");
624        } else {
625            return responseCode;
626        }
627    }
628}
629