HeaderSet.java revision 9439a7fe517b858bc5e5c654b459315e4722feb2
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.*;
36import java.util.*;
37
38/**
39 * This class implements the javax.obex.HeaderSet interface for OBEX over
40 * RFCOMM.
41 *
42 * @version 0.3 November 28, 2008
43 */
44public final class HeaderSet {
45
46    /**
47     * Represents the OBEX Count header.  This allows the connection statement
48     * to tell the server how many objects it plans to send or retrieve.
49     * <P>
50     * The value of <code>COUNT</code> is 0xC0 (192).
51     */
52    public static final int COUNT = 0xC0;
53
54    /**
55     * Represents the OBEX Name header.  This specifies the name of the object.
56     * <P>
57     * The value of <code>NAME</code> is 0x01 (1).
58     */
59    public static final int NAME = 0x01;
60
61    /**
62     * Represents the OBEX Type header.  This allows a request to specify the
63     * type of the object (e.g. text, html, binary, etc.).
64     * <P>
65     * The value of <code>TYPE</code> is 0x42 (66).
66     */
67    public static final int TYPE = 0x42;
68
69    /**
70     * Represents the OBEX Length header.  This is the length of the object in
71     * bytes.
72     * <P>
73     * The value of <code>LENGTH</code> is 0xC3 (195).
74     */
75    public static final int LENGTH = 0xC3;
76
77    /**
78     * Represents the OBEX Time header using the ISO 8601 standards.  This is
79     * the preferred time header.
80     * <P>
81     * The value of <code>TIME_ISO_8601</code> is 0x44 (68).
82     */
83    public static final int TIME_ISO_8601 = 0x44;
84
85    /**
86     * Represents the OBEX Time header using the 4 byte representation.  This
87     * is only included for backwards compatibility.  It represents the number
88     * of seconds since January 1, 1970.
89     * <P>
90     * The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
91     */
92    public static final int TIME_4_BYTE = 0xC4;
93
94    /**
95     * Represents the OBEX Description header.  This is a text description of
96     * the object.
97     * <P>
98     * The value of <code>DESCRIPTION</code> is 0x05 (5).
99     */
100    public static final int DESCRIPTION = 0x05;
101
102    /**
103     * Represents the OBEX Target header.  This is the name of the service an
104     * operation is targeted to.
105     * <P>
106     * The value of <code>TARGET</code> is 0x46 (70).
107     */
108    public static final int TARGET = 0x46;
109
110    /**
111     * Represents the OBEX HTTP header.  This allows an HTTP 1.X header to be
112     * included in a request or reply.
113     * <P>
114     * The value of <code>HTTP</code> is 0x47 (71).
115     */
116    public static final int HTTP = 0x47;
117
118    /**
119     * Represents the OBEX Who header.  Identifies the OBEX application to
120     * determine if the two peers are talking to each other.
121     * <P>
122     * The value of <code>WHO</code> is 0x4A (74).
123     */
124    public static final int WHO = 0x4A;
125
126    /**
127     * Represents the OBEX Object Class header.  This header specifies the
128     * OBEX object class of the object.
129     * <P>
130     * The value of <code>OBJECT_CLASS</code> is 0x4F (79).
131     */
132    public static final int OBJECT_CLASS = 0x4F;
133
134    /**
135     * Represents the OBEX Application Parameter header.  This header specifies
136     * additional application request and response information.
137     * <P>
138     * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
139     */
140    public static final int APPLICATION_PARAMETER = 0x4C;
141
142    private Long count; // 4 byte unsigned integer
143
144    private String name; // null terminated Unicode text string
145
146    private String type; // null terminated ASCII text string
147
148    private Long length; // 4 byte unsigend integer
149
150    private Calendar isoTime; // String of the form YYYYMMDDTHHMMSSZ
151
152    private Calendar byteTime; // 4 byte unsigned integer
153
154    private String description; // null terminated Unicode text String
155
156    private byte[] target; // byte sequence
157
158    private byte[] http; // byte sequence
159
160    private byte[] who; // length prefixed byte sequence
161
162    private byte[] appParam; // byte sequence of the form tag length
163
164    //value
165    public byte[] authChall; // The authentication challenge header
166
167    public byte[] authResp; // The authentication response header
168
169    public byte[] connectionID; // THe connection ID
170
171    private byte[] objectClass; // byte sequence
172
173    private String[] unicodeUserDefined; //null terminated unicode string
174
175    private byte[][] sequenceUserDefined; // byte sequence user defined
176
177    private Byte[] byteUserDefined; // 1 byte
178
179    private Long[] integerUserDefined; // 4 byte unsigned integer
180
181    public int responseCode;
182
183    public byte[] nonce;
184
185    private Random random;
186
187    /**
188     * Creates new <code>HeaderSet</code> object.
189     *
190     * @param size the max packet size for this connection
191     */
192    public HeaderSet() {
193        unicodeUserDefined = new String[16];
194        sequenceUserDefined = new byte[16][];
195        byteUserDefined = new Byte[16];
196        integerUserDefined = new Long[16];
197        responseCode = -1;
198        random = new Random();
199    }
200
201    /**
202     * Sets the value of the header identifier to the value provided.  The type
203     * of object must correspond to the Java type defined in the description of
204     * this interface.  If <code>null</code> is passed as the
205     * <code>headerValue</code> then the header will be removed from the set of
206     * headers to include in the next request.
207     *
208     * @param headerID the identifier to include in the message
209     *
210     * @param headerValue the value of the header identifier
211     *
212     * @exception IllegalArgumentException if the header identifier provided is
213     * not one defined in this interface or a user-defined header; if the type of
214     * <code>headerValue</code> is not the correct Java type as defined in the
215     * description of this interface\
216     */
217    public void setHeader(int headerID, Object headerValue) {
218        long temp = -1;
219
220        switch (headerID) {
221            case COUNT:
222                if (!(headerValue instanceof Long)) {
223                    if (headerValue == null) {
224                        count = null;
225                        break;
226                    }
227                    throw new IllegalArgumentException("Count must be a Long");
228                }
229                temp = ((Long)headerValue).longValue();
230                if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
231                    throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF");
232                }
233                count = (Long)headerValue;
234                break;
235            case NAME:
236                if ((headerValue != null) && (!(headerValue instanceof String))) {
237                    throw new IllegalArgumentException("Name must be a String");
238                }
239                name = (String)headerValue;
240                break;
241            case TYPE:
242                if ((headerValue != null) && (!(headerValue instanceof String))) {
243                    throw new IllegalArgumentException("Type must be a String");
244                }
245                type = (String)headerValue;
246                break;
247            case LENGTH:
248                if (!(headerValue instanceof Long)) {
249                    if (headerValue == null) {
250                        length = null;
251                        break;
252                    }
253                    throw new IllegalArgumentException("Length must be a Long");
254                }
255                temp = ((Long)headerValue).longValue();
256                if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
257                    throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF");
258                }
259                length = (Long)headerValue;
260                break;
261            case TIME_ISO_8601:
262                if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
263                    throw new IllegalArgumentException("Time ISO 8601 must be a Calendar");
264                }
265                isoTime = (Calendar)headerValue;
266                break;
267            case TIME_4_BYTE:
268                if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
269                    throw new IllegalArgumentException("Time 4 Byte must be a Calendar");
270                }
271                byteTime = (Calendar)headerValue;
272                break;
273            case DESCRIPTION:
274                if ((headerValue != null) && (!(headerValue instanceof String))) {
275                    throw new IllegalArgumentException("Description must be a String");
276                }
277                description = (String)headerValue;
278                break;
279            case TARGET:
280                if (headerValue == null) {
281                    target = null;
282                } else {
283                    if (!(headerValue instanceof byte[])) {
284                        throw new IllegalArgumentException("Target must be a byte array");
285                    } else {
286                        target = new byte[((byte[])headerValue).length];
287                        System.arraycopy(headerValue, 0, target, 0, target.length);
288                    }
289                }
290                break;
291            case HTTP:
292                if (headerValue == null) {
293                    http = null;
294                } else {
295                    if (!(headerValue instanceof byte[])) {
296                        throw new IllegalArgumentException("HTTP must be a byte array");
297                    } else {
298                        http = new byte[((byte[])headerValue).length];
299                        System.arraycopy(headerValue, 0, http, 0, http.length);
300                    }
301                }
302                break;
303            case WHO:
304                if (headerValue == null) {
305                    who = null;
306                } else {
307                    if (!(headerValue instanceof byte[])) {
308                        throw new IllegalArgumentException("WHO must be a byte array");
309                    } else {
310                        who = new byte[((byte[])headerValue).length];
311                        System.arraycopy(headerValue, 0, who, 0, who.length);
312                    }
313                }
314                break;
315            case OBJECT_CLASS:
316                if (headerValue == null) {
317                    objectClass = null;
318                } else {
319                    if (!(headerValue instanceof byte[])) {
320                        throw new IllegalArgumentException("Object Class must be a byte array");
321                    } else {
322                        objectClass = new byte[((byte[])headerValue).length];
323                        System.arraycopy(headerValue, 0, objectClass, 0, objectClass.length);
324                    }
325                }
326                break;
327            case APPLICATION_PARAMETER:
328                if (headerValue == null) {
329                    appParam = null;
330                } else {
331                    if (!(headerValue instanceof byte[])) {
332                        throw new IllegalArgumentException(
333                                "Application Parameter must be a byte array");
334                    } else {
335                        appParam = new byte[((byte[])headerValue).length];
336                        System.arraycopy(headerValue, 0, appParam, 0, appParam.length);
337                    }
338                }
339                break;
340            default:
341                // Verify that it was not a Unicode String user Defined
342                if ((headerID >= 0x30) && (headerID <= 0x3F)) {
343                    if ((headerValue != null) && (!(headerValue instanceof String))) {
344                        throw new IllegalArgumentException(
345                                "Unicode String User Defined must be a String");
346                    }
347                    unicodeUserDefined[headerID - 0x30] = (String)headerValue;
348
349                    break;
350                }
351                // Verify that it was not a byte sequence user defined value
352                if ((headerID >= 0x70) && (headerID <= 0x7F)) {
353
354                    if (headerValue == null) {
355                        sequenceUserDefined[headerID - 0x70] = null;
356                    } else {
357                        if (!(headerValue instanceof byte[])) {
358                            throw new IllegalArgumentException(
359                                    "Byte Sequence User Defined must be a byte array");
360                        } else {
361                            sequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length];
362                            System.arraycopy(headerValue, 0, sequenceUserDefined[headerID - 0x70],
363                                    0, sequenceUserDefined[headerID - 0x70].length);
364                        }
365                    }
366                    break;
367                }
368                // Verify that it was not a Byte user Defined
369                if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
370                    if ((headerValue != null) && (!(headerValue instanceof Byte))) {
371                        throw new IllegalArgumentException("ByteUser Defined must be a Byte");
372                    }
373                    byteUserDefined[headerID - 0xB0] = (Byte)headerValue;
374
375                    break;
376                }
377                // Verify that is was not the 4 byte unsigned integer user
378                // defined header
379                if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
380                    if (!(headerValue instanceof Long)) {
381                        if (headerValue == null) {
382                            integerUserDefined[headerID - 0xF0] = null;
383                            break;
384                        }
385                        throw new IllegalArgumentException("Integer User Defined must be a Long");
386                    }
387                    temp = ((Long)headerValue).longValue();
388                    if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
389                        throw new IllegalArgumentException(
390                                "Integer User Defined must be between 0 and 0xFFFFFFFF");
391                    }
392                    integerUserDefined[headerID - 0xF0] = (Long)headerValue;
393                    break;
394                }
395                throw new IllegalArgumentException("Invalid Header Identifier");
396        }
397    }
398
399    /**
400     * Retrieves the value of the header identifier provided.  The type of the
401     * Object returned is defined in the description of this interface.
402     *
403     * @param headerID the header identifier whose value is to be returned
404     *
405     * @return the value of the header provided or <code>null</code> if the
406     * header identifier specified is not part of this <code>HeaderSet</code>
407     * object
408     *
409     * @exception IllegalArgumentException if the <code>headerID</code> is not
410     * one defined in this interface or any of the user-defined headers
411     *
412     * @exception IOException if an error occurred in the transport layer during
413     * the operation or if the connection has been closed
414     */
415    public Object getHeader(int headerID) throws IOException {
416
417        switch (headerID) {
418            case COUNT:
419                return count;
420            case NAME:
421                return name;
422            case TYPE:
423                return type;
424            case LENGTH:
425                return length;
426            case TIME_ISO_8601:
427                return isoTime;
428            case TIME_4_BYTE:
429                return byteTime;
430            case DESCRIPTION:
431                return description;
432            case TARGET:
433                return target;
434            case HTTP:
435                return http;
436            case WHO:
437                return who;
438            case OBJECT_CLASS:
439                return objectClass;
440            case APPLICATION_PARAMETER:
441                return appParam;
442            default:
443                // Verify that it was not a Unicode String user Defined
444                if ((headerID >= 0x30) && (headerID <= 0x3F)) {
445                    return unicodeUserDefined[headerID - 0x30];
446                }
447                // Verify that it was not a byte sequence user defined header
448                if ((headerID >= 0x70) && (headerID <= 0x7F)) {
449                    return sequenceUserDefined[headerID - 0x70];
450                }
451                // Verify that it was not a byte user defined header
452                if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
453                    return byteUserDefined[headerID - 0xB0];
454                }
455                // Verify that it was not a itneger user defined header
456                if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
457                    return integerUserDefined[headerID - 0xF0];
458                }
459                throw new IllegalArgumentException("Invalid Header Identifier");
460        }
461    }
462
463    /**
464     * Retrieves the list of headers that may be retrieved via the
465     * <code>getHeader</code> method that will not return <code>null</code>.
466     * In other words, this method returns all the headers that are available
467     * in this object.
468     *
469     * @see #getHeader
470     *
471     * @return the array of headers that are set in this object or
472     * <code>null</code> if no headers are available
473     *
474     * @exception IOException if an error occurred in the transport layer during
475     * the operation or the connection has been closed
476     */
477    public int[] getHeaderList() throws IOException {
478        ByteArrayOutputStream out = new ByteArrayOutputStream();
479
480        if (count != null) {
481            out.write(COUNT);
482        }
483        if (name != null) {
484            out.write(NAME);
485        }
486        if (type != null) {
487            out.write(TYPE);
488        }
489        if (length != null) {
490            out.write(LENGTH);
491        }
492        if (isoTime != null) {
493            out.write(TIME_ISO_8601);
494        }
495        if (byteTime != null) {
496            out.write(TIME_4_BYTE);
497        }
498        if (description != null) {
499            out.write(DESCRIPTION);
500        }
501        if (target != null) {
502            out.write(TARGET);
503        }
504        if (http != null) {
505            out.write(HTTP);
506        }
507        if (who != null) {
508            out.write(WHO);
509        }
510        if (appParam != null) {
511            out.write(APPLICATION_PARAMETER);
512        }
513        if (objectClass != null) {
514            out.write(OBJECT_CLASS);
515        }
516
517        for (int i = 0x30; i < 0x40; i++) {
518            if (unicodeUserDefined[i - 0x30] != null) {
519                out.write(i);
520            }
521        }
522
523        for (int i = 0x70; i < 0x80; i++) {
524            if (sequenceUserDefined[i - 0x70] != null) {
525                out.write(i);
526            }
527        }
528
529        for (int i = 0xB0; i < 0xC0; i++) {
530            if (byteUserDefined[i - 0xB0] != null) {
531                out.write(i);
532            }
533        }
534
535        for (int i = 0xF0; i < 0x100; i++) {
536            if (integerUserDefined[i - 0xF0] != null) {
537                out.write(i);
538            }
539        }
540
541        byte[] headers = out.toByteArray();
542        out.close();
543
544        if ((headers == null) || (headers.length == 0)) {
545            return null;
546        }
547
548        int[] result = new int[headers.length];
549        for (int i = 0; i < headers.length; i++) {
550            // Convert the byte to a positive integer.  That is, an integer
551            // between 0 and 256.
552            result[i] = headers[i] & 0xFF;
553        }
554
555        return result;
556    }
557
558    /**
559     * Sets the authentication challenge header.  The <code>realm</code> will
560     * be encoded based upon the default encoding scheme used by the
561     * implementation to encode strings.  Therefore, the encoding scheme used
562     * to encode the <code>realm</code> is application dependent.
563     *
564     * @param realm a short description that describes what password to use; if
565     * <code>null</code> no realm will be sent in the authentication challenge
566     * header
567     *
568     * @param userID if <code>true</code>, a user ID is required in the reply;
569     * if <code>false</code>, no user ID is required
570     *
571     * @param access if <code>true</code> then full access will be granted if
572     * successful; if <code>false</code> then read-only access will be granted
573     * if successful
574     */
575    public void createAuthenticationChallenge(String realm, boolean userID, boolean access) {
576
577        try {
578            nonce = new byte[16];
579            for (int i = 0; i < 16; i++) {
580                nonce[i] = (byte)random.nextInt();
581            }
582
583            authChall = OBEXHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
584        } catch (IOException e) {
585            throw new RuntimeException(e.getMessage());
586        }
587    }
588
589    /**
590     * Returns the response code received from the server.  Response codes
591     * are defined in the <code>ResponseCodes</code> class.
592     *
593     * @see ResponseCodes
594     *
595     * @return the response code retrieved from the server
596     *
597     * @exception IOException if an error occurred in the transport layer during
598     * the transaction; if this method is called on a <code>HeaderSet</code>
599     * object created by calling <code>createHeaderSet()</code> in a
600     * <code>ClientSession</code> object; if this object was created by an OBEX
601     * server
602     */
603    public int getResponseCode() throws IOException {
604        if (responseCode == -1) {
605            throw new IOException("May not be called on a server");
606        } else {
607            return responseCode;
608        }
609    }
610}
611