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