Home | History | Annotate | Download | only in obex
      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 
     33 package javax.obex;
     34 
     35 import java.io.ByteArrayOutputStream;
     36 import java.io.IOException;
     37 import java.util.Calendar;
     38 import java.security.SecureRandom;
     39 
     40 /**
     41  * This class implements the javax.obex.HeaderSet interface for OBEX over
     42  * RFCOMM.
     43  * @hide
     44  */
     45 public 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