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