1/*
2 * Copyright (c) 2014 The Android Open Source Project
3 * Copyright (c) 2008-2009, Motorola, Inc.
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * - Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * - Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * - Neither the name of the Motorola, Inc. nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34package javax.obex;
35
36import java.io.ByteArrayOutputStream;
37import java.io.IOException;
38import java.util.Calendar;
39import java.security.SecureRandom;
40
41/**
42 * This class implements the javax.obex.HeaderSet interface for OBEX over
43 * RFCOMM.
44 * @hide
45 */
46public final class HeaderSet {
47
48    /**
49     * Represents the OBEX Count header. This allows the connection statement to
50     * tell the server how many objects it plans to send or retrieve.
51     * <P>
52     * The value of <code>COUNT</code> is 0xC0 (192).
53     */
54    public static final int COUNT = 0xC0;
55
56    /**
57     * Represents the OBEX Name header. This specifies the name of the object.
58     * <P>
59     * The value of <code>NAME</code> is 0x01 (1).
60     */
61    public static final int NAME = 0x01;
62
63    /**
64     * Represents the OBEX Type header. This allows a request to specify the
65     * type of the object (e.g. text, html, binary, etc.).
66     * <P>
67     * The value of <code>TYPE</code> is 0x42 (66).
68     */
69    public static final int TYPE = 0x42;
70
71    /**
72     * Represents the OBEX Length header. This is the length of the object in
73     * bytes.
74     * <P>
75     * The value of <code>LENGTH</code> is 0xC3 (195).
76     */
77    public static final int LENGTH = 0xC3;
78
79    /**
80     * Represents the OBEX Time header using the ISO 8601 standards. This is the
81     * preferred time header.
82     * <P>
83     * The value of <code>TIME_ISO_8601</code> is 0x44 (68).
84     */
85    public static final int TIME_ISO_8601 = 0x44;
86
87    /**
88     * Represents the OBEX Time header using the 4 byte representation. This is
89     * only included for backwards compatibility. It represents the number of
90     * seconds since January 1, 1970.
91     * <P>
92     * The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
93     */
94    public static final int TIME_4_BYTE = 0xC4;
95
96    /**
97     * Represents the OBEX Description header. This is a text description of the
98     * object.
99     * <P>
100     * The value of <code>DESCRIPTION</code> is 0x05 (5).
101     */
102    public static final int DESCRIPTION = 0x05;
103
104    /**
105     * Represents the OBEX Target header. This is the name of the service an
106     * operation is targeted to.
107     * <P>
108     * The value of <code>TARGET</code> is 0x46 (70).
109     */
110    public static final int TARGET = 0x46;
111
112    /**
113     * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be
114     * included in a request or reply.
115     * <P>
116     * The value of <code>HTTP</code> is 0x47 (71).
117     */
118    public static final int HTTP = 0x47;
119
120    /**
121     * Represents the OBEX BODY header.
122     * <P>
123     * The value of <code>BODY</code> is 0x48 (72).
124     */
125    public static final int BODY = 0x48;
126
127    /**
128     * Represents the OBEX End of BODY header.
129     * <P>
130     * The value of <code>BODY</code> is 0x49 (73).
131     */
132    public static final int END_OF_BODY = 0x49;
133
134    /**
135     * Represents the OBEX Who header. Identifies the OBEX application to
136     * determine if the two peers are talking to each other.
137     * <P>
138     * The value of <code>WHO</code> is 0x4A (74).
139     */
140    public static final int WHO = 0x4A;
141
142    /**
143     * Represents the OBEX Connection ID header. Identifies used for OBEX
144     * connection multiplexing.
145     * <P>
146     * The value of <code>CONNECTION_ID</code> is 0xCB (203).
147     */
148
149    public static final int CONNECTION_ID = 0xCB;
150
151    /**
152     * Represents the OBEX Application Parameter header. This header specifies
153     * additional application request and response information.
154     * <P>
155     * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
156     */
157    public static final int APPLICATION_PARAMETER = 0x4C;
158
159    /**
160     * Represents the OBEX authentication digest-challenge.
161     * <P>
162     * The value of <code>AUTH_CHALLENGE</code> is 0x4D (77).
163     */
164    public static final int AUTH_CHALLENGE = 0x4D;
165
166    /**
167     * Represents the OBEX authentication digest-response.
168     * <P>
169     * The value of <code>AUTH_RESPONSE</code> is 0x4E (78).
170     */
171    public static final int AUTH_RESPONSE = 0x4E;
172
173    /**
174     * Represents the OBEX Object Class header. This header specifies the OBEX
175     * object class of the object.
176     * <P>
177     * The value of <code>OBJECT_CLASS</code> is 0x4F (79).
178     */
179    public static final int OBJECT_CLASS = 0x4F;
180
181    private Long mCount; // 4 byte unsigned integer
182
183    private String mName; // null terminated Unicode text string
184
185    private boolean mEmptyName;
186
187    private String mType; // null terminated ASCII text string
188
189    private Long mLength; // 4 byte unsigend integer
190
191    private Calendar mIsoTime; // String of the form YYYYMMDDTHHMMSSZ
192
193    private Calendar mByteTime; // 4 byte unsigned integer
194
195    private String mDescription; // null terminated Unicode text String
196
197    private byte[] mTarget; // byte sequence
198
199    private byte[] mHttpHeader; // byte sequence
200
201    private byte[] mWho; // length prefixed byte sequence
202
203    private byte[] mAppParam; // byte sequence of the form tag length value
204
205    private byte[] mObjectClass; // byte sequence
206
207    private String[] mUnicodeUserDefined; //null terminated unicode string
208
209    private byte[][] mSequenceUserDefined; // byte sequence user defined
210
211    private Byte[] mByteUserDefined; // 1 byte
212
213    private Long[] mIntegerUserDefined; // 4 byte unsigned integer
214
215    private final SecureRandom mRandom;
216
217    /*package*/ byte[] nonce;
218
219    public byte[] mAuthChall; // The authentication challenge header
220
221    public byte[] mAuthResp; // The authentication response header
222
223    public byte[] mConnectionID; // THe connection ID
224
225    public int responseCode;
226
227    /**
228     * Creates new <code>HeaderSet</code> object.
229     * @param size the max packet size for this connection
230     */
231    public HeaderSet() {
232        mUnicodeUserDefined = new String[16];
233        mSequenceUserDefined = new byte[16][];
234        mByteUserDefined = new Byte[16];
235        mIntegerUserDefined = new Long[16];
236        responseCode = -1;
237        mRandom = new SecureRandom();
238    }
239
240    /**
241     * Sets flag for special "value" of NAME header which should be empty. This
242     * is not the same as NAME header with empty string in which case it will
243     * have length of 5 bytes. It should be 3 bytes with only header id and
244     * length field.
245     */
246    public void setEmptyNameHeader() {
247        mName = null;
248        mEmptyName = true;
249    }
250
251    /**
252     * Gets flag for special "value" of NAME header which should be empty. See
253     * above.
254     */
255    public boolean getEmptyNameHeader() {
256        return mEmptyName;
257    }
258
259    /**
260     * Sets the value of the header identifier to the value provided. The type
261     * of object must correspond to the Java type defined in the description of
262     * this interface. If <code>null</code> is passed as the
263     * <code>headerValue</code> then the header will be removed from the set of
264     * headers to include in the next request.
265     * @param headerID the identifier to include in the message
266     * @param headerValue the value of the header identifier
267     * @throws IllegalArgumentException if the header identifier provided is not
268     *         one defined in this interface or a user-defined header; if the
269     *         type of <code>headerValue</code> is not the correct Java type as
270     *         defined in the description of this interface\
271     */
272    public void setHeader(int headerID, Object headerValue) {
273        long temp = -1;
274
275        switch (headerID) {
276            case COUNT:
277                if (!(headerValue instanceof Long)) {
278                    if (headerValue == null) {
279                        mCount = null;
280                        break;
281                    }
282                    throw new IllegalArgumentException("Count must be a Long");
283                }
284                temp = ((Long)headerValue).longValue();
285                if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
286                    throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF");
287                }
288                mCount = (Long)headerValue;
289                break;
290            case NAME:
291                if ((headerValue != null) && (!(headerValue instanceof String))) {
292                    throw new IllegalArgumentException("Name must be a String");
293                }
294                mEmptyName = false;
295                mName = (String)headerValue;
296                break;
297            case TYPE:
298                if ((headerValue != null) && (!(headerValue instanceof String))) {
299                    throw new IllegalArgumentException("Type must be a String");
300                }
301                mType = (String)headerValue;
302                break;
303            case LENGTH:
304                if (!(headerValue instanceof Long)) {
305                    if (headerValue == null) {
306                        mLength = null;
307                        break;
308                    }
309                    throw new IllegalArgumentException("Length must be a Long");
310                }
311                temp = ((Long)headerValue).longValue();
312                if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
313                    throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF");
314                }
315                mLength = (Long)headerValue;
316                break;
317            case TIME_ISO_8601:
318                if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
319                    throw new IllegalArgumentException("Time ISO 8601 must be a Calendar");
320                }
321                mIsoTime = (Calendar)headerValue;
322                break;
323            case TIME_4_BYTE:
324                if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
325                    throw new IllegalArgumentException("Time 4 Byte must be a Calendar");
326                }
327                mByteTime = (Calendar)headerValue;
328                break;
329            case DESCRIPTION:
330                if ((headerValue != null) && (!(headerValue instanceof String))) {
331                    throw new IllegalArgumentException("Description must be a String");
332                }
333                mDescription = (String)headerValue;
334                break;
335            case TARGET:
336                if (headerValue == null) {
337                    mTarget = null;
338                } else {
339                    if (!(headerValue instanceof byte[])) {
340                        throw new IllegalArgumentException("Target must be a byte array");
341                    } else {
342                        mTarget = new byte[((byte[])headerValue).length];
343                        System.arraycopy(headerValue, 0, mTarget, 0, mTarget.length);
344                    }
345                }
346                break;
347            case HTTP:
348                if (headerValue == null) {
349                    mHttpHeader = null;
350                } else {
351                    if (!(headerValue instanceof byte[])) {
352                        throw new IllegalArgumentException("HTTP must be a byte array");
353                    } else {
354                        mHttpHeader = new byte[((byte[])headerValue).length];
355                        System.arraycopy(headerValue, 0, mHttpHeader, 0, mHttpHeader.length);
356                    }
357                }
358                break;
359            case WHO:
360                if (headerValue == null) {
361                    mWho = null;
362                } else {
363                    if (!(headerValue instanceof byte[])) {
364                        throw new IllegalArgumentException("WHO must be a byte array");
365                    } else {
366                        mWho = new byte[((byte[])headerValue).length];
367                        System.arraycopy(headerValue, 0, mWho, 0, mWho.length);
368                    }
369                }
370                break;
371            case OBJECT_CLASS:
372                if (headerValue == null) {
373                    mObjectClass = null;
374                } else {
375                    if (!(headerValue instanceof byte[])) {
376                        throw new IllegalArgumentException("Object Class must be a byte array");
377                    } else {
378                        mObjectClass = new byte[((byte[])headerValue).length];
379                        System.arraycopy(headerValue, 0, mObjectClass, 0, mObjectClass.length);
380                    }
381                }
382                break;
383            case APPLICATION_PARAMETER:
384                if (headerValue == null) {
385                    mAppParam = null;
386                } else {
387                    if (!(headerValue instanceof byte[])) {
388                        throw new IllegalArgumentException(
389                                "Application Parameter must be a byte array");
390                    } else {
391                        mAppParam = new byte[((byte[])headerValue).length];
392                        System.arraycopy(headerValue, 0, mAppParam, 0, mAppParam.length);
393                    }
394                }
395                break;
396            default:
397                // Verify that it was not a Unicode String user Defined
398                if ((headerID >= 0x30) && (headerID <= 0x3F)) {
399                    if ((headerValue != null) && (!(headerValue instanceof String))) {
400                        throw new IllegalArgumentException(
401                                "Unicode String User Defined must be a String");
402                    }
403                    mUnicodeUserDefined[headerID - 0x30] = (String)headerValue;
404
405                    break;
406                }
407                // Verify that it was not a byte sequence user defined value
408                if ((headerID >= 0x70) && (headerID <= 0x7F)) {
409
410                    if (headerValue == null) {
411                        mSequenceUserDefined[headerID - 0x70] = null;
412                    } else {
413                        if (!(headerValue instanceof byte[])) {
414                            throw new IllegalArgumentException(
415                                    "Byte Sequence User Defined must be a byte array");
416                        } else {
417                            mSequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length];
418                            System.arraycopy(headerValue, 0, mSequenceUserDefined[headerID - 0x70],
419                                    0, mSequenceUserDefined[headerID - 0x70].length);
420                        }
421                    }
422                    break;
423                }
424                // Verify that it was not a Byte user Defined
425                if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
426                    if ((headerValue != null) && (!(headerValue instanceof Byte))) {
427                        throw new IllegalArgumentException("ByteUser Defined must be a Byte");
428                    }
429                    mByteUserDefined[headerID - 0xB0] = (Byte)headerValue;
430
431                    break;
432                }
433                // Verify that is was not the 4 byte unsigned integer user
434                // defined header
435                if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
436                    if (!(headerValue instanceof Long)) {
437                        if (headerValue == null) {
438                            mIntegerUserDefined[headerID - 0xF0] = null;
439                            break;
440                        }
441                        throw new IllegalArgumentException("Integer User Defined must be a Long");
442                    }
443                    temp = ((Long)headerValue).longValue();
444                    if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
445                        throw new IllegalArgumentException(
446                                "Integer User Defined must be between 0 and 0xFFFFFFFF");
447                    }
448                    mIntegerUserDefined[headerID - 0xF0] = (Long)headerValue;
449                    break;
450                }
451                throw new IllegalArgumentException("Invalid Header Identifier");
452        }
453    }
454
455    /**
456     * Retrieves the value of the header identifier provided. The type of the
457     * Object returned is defined in the description of this interface.
458     * @param headerID the header identifier whose value is to be returned
459     * @return the value of the header provided or <code>null</code> if the
460     *         header identifier specified is not part of this
461     *         <code>HeaderSet</code> object
462     * @throws IllegalArgumentException if the <code>headerID</code> is not one
463     *         defined in this interface or any of the user-defined headers
464     * @throws IOException if an error occurred in the transport layer during
465     *         the operation or if the connection has been closed
466     */
467    public Object getHeader(int headerID) throws IOException {
468
469        switch (headerID) {
470            case COUNT:
471                return mCount;
472            case NAME:
473                return mName;
474            case TYPE:
475                return mType;
476            case LENGTH:
477                return mLength;
478            case TIME_ISO_8601:
479                return mIsoTime;
480            case TIME_4_BYTE:
481                return mByteTime;
482            case DESCRIPTION:
483                return mDescription;
484            case TARGET:
485                return mTarget;
486            case HTTP:
487                return mHttpHeader;
488            case WHO:
489                return mWho;
490            case CONNECTION_ID:
491                return mConnectionID;
492            case OBJECT_CLASS:
493                return mObjectClass;
494            case APPLICATION_PARAMETER:
495                return mAppParam;
496            default:
497                // Verify that it was not a Unicode String user Defined
498                if ((headerID >= 0x30) && (headerID <= 0x3F)) {
499                    return mUnicodeUserDefined[headerID - 0x30];
500                }
501                // Verify that it was not a byte sequence user defined header
502                if ((headerID >= 0x70) && (headerID <= 0x7F)) {
503                    return mSequenceUserDefined[headerID - 0x70];
504                }
505                // Verify that it was not a byte user defined header
506                if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
507                    return mByteUserDefined[headerID - 0xB0];
508                }
509                // Verify that it was not a integer user defined header
510                if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
511                    return mIntegerUserDefined[headerID - 0xF0];
512                }
513                throw new IllegalArgumentException("Invalid Header Identifier");
514        }
515    }
516
517    /**
518     * Retrieves the list of headers that may be retrieved via the
519     * <code>getHeader</code> method that will not return <code>null</code>. In
520     * other words, this method returns all the headers that are available in
521     * this object.
522     * @see #getHeader
523     * @return the array of headers that are set in this object or
524     *         <code>null</code> if no headers are available
525     * @throws IOException if an error occurred in the transport layer during
526     *         the operation or the connection has been closed
527     */
528    public int[] getHeaderList() throws IOException {
529        ByteArrayOutputStream out = new ByteArrayOutputStream();
530
531        if (mCount != null) {
532            out.write(COUNT);
533        }
534        if (mName != null) {
535            out.write(NAME);
536        }
537        if (mType != null) {
538            out.write(TYPE);
539        }
540        if (mLength != null) {
541            out.write(LENGTH);
542        }
543        if (mIsoTime != null) {
544            out.write(TIME_ISO_8601);
545        }
546        if (mByteTime != null) {
547            out.write(TIME_4_BYTE);
548        }
549        if (mDescription != null) {
550            out.write(DESCRIPTION);
551        }
552        if (mTarget != null) {
553            out.write(TARGET);
554        }
555        if (mHttpHeader != null) {
556            out.write(HTTP);
557        }
558        if (mWho != null) {
559            out.write(WHO);
560        }
561        if (mAppParam != null) {
562            out.write(APPLICATION_PARAMETER);
563        }
564        if (mObjectClass != null) {
565            out.write(OBJECT_CLASS);
566        }
567
568        for (int i = 0x30; i < 0x40; i++) {
569            if (mUnicodeUserDefined[i - 0x30] != null) {
570                out.write(i);
571            }
572        }
573
574        for (int i = 0x70; i < 0x80; i++) {
575            if (mSequenceUserDefined[i - 0x70] != null) {
576                out.write(i);
577            }
578        }
579
580        for (int i = 0xB0; i < 0xC0; i++) {
581            if (mByteUserDefined[i - 0xB0] != null) {
582                out.write(i);
583            }
584        }
585
586        for (int i = 0xF0; i < 0x100; i++) {
587            if (mIntegerUserDefined[i - 0xF0] != null) {
588                out.write(i);
589            }
590        }
591
592        byte[] headers = out.toByteArray();
593        out.close();
594
595        if ((headers == null) || (headers.length == 0)) {
596            return null;
597        }
598
599        int[] result = new int[headers.length];
600        for (int i = 0; i < headers.length; i++) {
601            // Convert the byte to a positive integer.  That is, an integer
602            // between 0 and 256.
603            result[i] = headers[i] & 0xFF;
604        }
605
606        return result;
607    }
608
609    /**
610     * Sets the authentication challenge header. The <code>realm</code> will be
611     * encoded based upon the default encoding scheme used by the implementation
612     * to encode strings. Therefore, the encoding scheme used to encode the
613     * <code>realm</code> is application dependent.
614     * @param realm a short description that describes what password to use; if
615     *        <code>null</code> no realm will be sent in the authentication
616     *        challenge header
617     * @param userID if <code>true</code>, a user ID is required in the reply;
618     *        if <code>false</code>, no user ID is required
619     * @param access if <code>true</code> then full access will be granted if
620     *        successful; if <code>false</code> then read-only access will be
621     *        granted if successful
622     * @throws IOException
623     */
624    public void createAuthenticationChallenge(String realm, boolean userID, boolean access)
625            throws IOException {
626
627        nonce = new byte[16];
628        for (int i = 0; i < 16; i++) {
629            nonce[i] = (byte)mRandom.nextInt();
630        }
631
632        mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
633    }
634
635    /**
636     * Returns the response code received from the server. Response codes are
637     * defined in the <code>ResponseCodes</code> class.
638     * @see ResponseCodes
639     * @return the response code retrieved from the server
640     * @throws IOException if an error occurred in the transport layer during
641     *         the transaction; if this method is called on a
642     *         <code>HeaderSet</code> object created by calling
643     *         <code>createHeaderSet()</code> in a <code>ClientSession</code>
644     *         object; if this object was created by an OBEX server
645     */
646    public int getResponseCode() throws IOException {
647        if (responseCode == -1) {
648            throw new IOException("May not be called on a server");
649        } else {
650            return responseCode;
651        }
652    }
653}
654