• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
HeaderSet()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      */
setHeader(int headerID, Object headerValue)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      */
getHeader(int headerID)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      */
getHeaderList()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      */
createAuthenticationChallenge(String realm, boolean userID, boolean access)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      */
getResponseCode()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