ServerSession.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.*; 36 37/** 38 * This class in an implementation of the ServerSession interface. 39 * 40 * @version 0.3 November 28, 2008 41 */ 42public class ServerSession implements Runnable, ObexSession { 43 44 private ObexTransport client; 45 46 // private Socket client ; 47 private InputStream input; 48 49 private OutputStream output; 50 51 private ServerRequestHandler listener; 52 53 private Thread processThread; 54 55 private int maxPacketLength; 56 57 private Authenticator authenticator; 58 59 byte[] challengeDigest; 60 61 public boolean isClosed; 62 63 private static final String TAG = "ServerSession"; 64 65 /** 66 * Creates new ServerSession. 67 * 68 * @param conn 69 * the connection to the client 70 * 71 * @param handler 72 * the event listener that will process requests 73 * 74 * @param auth 75 * the authenticator to use with this connection 76 * 77 * @exception IOException 78 * if an error occurred while opening the input and output 79 * streams 80 */ 81 public ServerSession(ObexTransport conn, ServerRequestHandler handler, Authenticator auth) 82 throws IOException { 83 authenticator = auth; 84 client = conn; 85 input = client.openInputStream(); 86 output = client.openOutputStream(); 87 listener = handler; 88 maxPacketLength = 256; 89 90 isClosed = false; 91 processThread = new Thread(this); 92 processThread.start(); 93 } 94 95 /* removed as they're provided to the API user. Not used internally. */ 96 /* 97 public boolean isCreatedServer() { 98 if (client instanceof BTConnection) 99 return ((BTConnection)client).isServerCreated(); 100 else 101 return false; 102 } 103 104 public boolean isClosed() { 105 if (client instanceof BTConnection) 106 return ((BTConnection)client).isClosed(); 107 else 108 return false; 109 } 110 111 public int getConnectionHandle() { 112 if (client instanceof BTConnection) 113 return ((BTConnection)client).getConnectionHandle(); 114 else 115 return -1; 116 } 117 118 public RemoteDevice getRemoteDevice() { 119 if (client instanceof BTConnection) 120 return ((BTConnection)client).getRemoteDevice(); 121 else 122 return null; 123 }*/ 124 125 /** 126 * Processes requests made to the server and forwards them to the 127 * appropriate event listener. 128 */ 129 public void run() { 130 try { 131 132 boolean done = false; 133 while (!done && !isClosed) { 134 int requestType = input.read(); 135 switch (requestType) { 136 case 0x80: 137 handleConnectRequest(); 138 break; 139 140 case 0x81: 141 handleDisconnectRequest(); 142 done = true; 143 break; 144 145 case 0x03: 146 case 0x83: 147 handleGetRequest(requestType); 148 break; 149 150 case 0x02: 151 case 0x82: 152 handlePutRequest(requestType); 153 break; 154 155 case 0x85: 156 handleSetPathRequest(); 157 break; 158 159 case -1: 160 done = true; 161 break; 162 163 default: 164 165 /* 166 * Received a request type that is not recognized so I am 167 * just going to read the packet and send a not implemented 168 * to the client 169 */ 170 int length = input.read(); 171 length = (length << 8) + input.read(); 172 for (int i = 3; i < length; i++) { 173 input.read(); 174 } 175 sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null); 176 177 // done = true; 178 } 179 } 180 181 } catch (NullPointerException e) { 182 } catch (Exception e) { 183 } 184 close(); 185 } 186 187 /** 188 * Handles a PUT request from a client. This method will provide a 189 * <code>ServerOperation</code> object to the request handler. The 190 * <code>ServerOperation</code> object will handle the rest of the request. 191 * It will also send replies and receive requests until the final reply 192 * should be sent. When the final reply should be sent, this method will get 193 * the response code to use and send the reply. The 194 * <code>ServerOperation</code> object will always reply with a 195 * OBEX_HTTP_CONTINUE reply. It will only reply if further information is 196 * needed. 197 * 198 * @param type 199 * the type of request received; either 0x02 or 0x82 200 * 201 * @exception IOException 202 * if an error occurred at the transport layer 203 */ 204 private void handlePutRequest(int type) throws IOException { 205 ServerOperation client = new ServerOperation(this, input, type, maxPacketLength, listener); 206 try { 207 int response = -1; 208 209 if ((client.finalBitSet) && !client.isValidBody()) { 210 response = validateResponseCode(listener.onDelete(client.requestHeaders, 211 client.replyHeaders)); 212 } else { 213 response = validateResponseCode(listener.onPut(client)); 214 } 215 if (response != ResponseCodes.OBEX_HTTP_OK) { 216 client.sendReply(response); 217 } else if (!client.isAborted) { 218 // wait for the final bit 219 while (!client.finalBitSet) { 220 client.sendReply(OBEXConstants.OBEX_HTTP_CONTINUE); 221 } 222 client.sendReply(response); 223 } 224 } catch (Exception e) { 225 sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null); 226 } 227 } 228 229 /** 230 * Handles a GET request from a client. This method will provide a 231 * <code>ServerOperation</code> object to the request handler. The 232 * <code>ServerOperation</code> object will handle the rest of the request. 233 * It will also send replies and receive requests until the final reply 234 * should be sent. When the final reply should be sent, this method will get 235 * the response code to use and send the reply. The 236 * <code>ServerOperation</code> object will always reply with a 237 * OBEX_HTTP_CONTINUE reply. It will only reply if further information is 238 * needed. 239 * 240 * @param type 241 * the type of request received; either 0x03 or 0x83 242 * 243 * @exception IOException 244 * if an error occurred at the transport layer 245 */ 246 private void handleGetRequest(int type) throws IOException { 247 ServerOperation client = new ServerOperation(this, input, type, maxPacketLength, listener); 248 try { 249 int response = validateResponseCode(listener.onGet(client)); 250 251 if (!client.isAborted) { 252 client.sendReply(response); 253 } 254 } catch (Exception e) { 255 sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null); 256 } 257 } 258 259 /** 260 * Send standard response. 261 * 262 * @param code 263 * the response code to send 264 * 265 * @param header 266 * the headers to include in the response 267 * 268 * @exception IOException 269 * if an IO error occurs 270 */ 271 protected void sendResponse(int code, byte[] header) throws IOException { 272 int totalLength = 3; 273 byte[] data = null; 274 275 if (header != null) { 276 totalLength += header.length; 277 data = new byte[totalLength]; 278 data[0] = (byte)code; 279 data[1] = (byte)(totalLength >> 8); 280 data[2] = (byte)totalLength; 281 System.arraycopy(header, 0, data, 3, header.length); 282 } else { 283 data = new byte[totalLength]; 284 data[0] = (byte)code; 285 data[1] = (byte)0x00; 286 data[2] = (byte)totalLength; 287 } 288 output.write(data); 289 output.flush(); 290 } 291 292 /** 293 * Handles a SETPATH request from a client. This method will read the rest 294 * of the request from the client. Assuming the request is valid, it will 295 * create a <code>HeaderSet</code> object to pass to the 296 * <code>ServerRequestHandler</code> object. After the handler processes the 297 * request, this method will create a reply message to send to the server 298 * with the response code provided. 299 * 300 * @exception IOException 301 * if an error occurred at the transport layer 302 */ 303 private void handleSetPathRequest() throws IOException { 304 int length; 305 int flags; 306 int constants; 307 int totalLength = 3; 308 byte[] head = null; 309 int code = -1; 310 int bytesReceived; 311 HeaderSet request = new HeaderSet(); 312 HeaderSet reply = new HeaderSet(); 313 314 length = input.read(); 315 length = (length << 8) + input.read(); 316 flags = input.read(); 317 constants = input.read(); 318 319 if (length > OBEXConstants.MAX_PACKET_SIZE_INT) { 320 code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE; 321 totalLength = 3; 322 } else { 323 if (length > 5) { 324 byte[] headers = new byte[length - 5]; 325 bytesReceived = input.read(headers); 326 327 while (bytesReceived != headers.length) { 328 bytesReceived += input.read(headers, bytesReceived, headers.length 329 - bytesReceived); 330 } 331 332 OBEXHelper.updateHeaderSet(request, headers); 333 334 if (request.connectionID != null) { 335 listener.setConnectionID(OBEXHelper.convertToLong(request.connectionID)); 336 } else { 337 listener.setConnectionID(-1); 338 } 339 // the Auth chan is initiated by the server. 340 // client sent back the authResp . 341 if (request.authResp != null) { 342 if (!handleAuthResp(request.authResp)) { 343 code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED; 344 listener.onAuthenticationFailure(OBEXHelper.getTagValue((byte)0x01, 345 request.authResp)); 346 } 347 request.authResp = null; 348 } 349 } 350 351 if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) { 352 // the Auth chan is initiated by the client 353 // the server will send back the authResp to the client 354 if (request.authChall != null) { 355 handleAuthChall(request); 356 reply.authResp = new byte[request.authResp.length]; 357 System.arraycopy(request.authResp, 0, reply.authResp, 0, reply.authResp.length); 358 request.authChall = null; 359 request.authResp = null; 360 } 361 boolean backup = false; 362 boolean create = true; 363 if (!((flags & 1) == 0)) { 364 backup = true; 365 } 366 if ((flags & 2) == 0) { 367 create = false; 368 } 369 370 try { 371 code = listener.onSetPath(request, reply, backup, create); 372 } catch (Exception e) { 373 sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null); 374 return; 375 } 376 377 code = validateResponseCode(code); 378 379 if (reply.nonce != null) { 380 challengeDigest = new byte[16]; 381 System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16); 382 } else { 383 challengeDigest = null; 384 } 385 386 long id = listener.getConnectionID(); 387 if (id == -1) { 388 reply.connectionID = null; 389 } else { 390 reply.connectionID = OBEXHelper.convertToByteArray(id); 391 } 392 393 head = OBEXHelper.createHeader(reply, false); 394 totalLength += head.length; 395 396 if (totalLength > maxPacketLength) { 397 totalLength = 3; 398 head = null; 399 code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 400 } 401 } 402 } 403 404 // Compute Length of OBEX SETPATH packet 405 byte[] replyData = new byte[totalLength]; 406 replyData[0] = (byte)code; 407 replyData[1] = (byte)(totalLength >> 8); 408 replyData[2] = (byte)totalLength; 409 if (head != null) { 410 System.arraycopy(head, 0, replyData, 3, head.length); 411 } 412 /* 413 * Write the OBEX SETPATH packet to the server. Byte 0: response code 414 * Byte 1&2: Connect Packet Length Byte 3 to n: headers 415 */ 416 output.write(replyData); 417 output.flush(); 418 } 419 420 /** 421 * Handles a disconnect request from a client. This method will read the 422 * rest of the request from the client. Assuming the request is valid, it 423 * will create a <code>HeaderSet</code> object to pass to the 424 * <code>ServerRequestHandler</code> object. After the handler processes the 425 * request, this method will create a reply message to send to the server. 426 * 427 * @exception IOException 428 * if an error occurred at the transport layer 429 */ 430 private void handleDisconnectRequest() throws IOException { 431 int length; 432 int code = ResponseCodes.OBEX_HTTP_OK; 433 int totalLength = 3; 434 byte[] head = null; 435 int bytesReceived; 436 HeaderSet request = new HeaderSet(); 437 HeaderSet reply = new HeaderSet(); 438 439 length = input.read(); 440 length = (length << 8) + input.read(); 441 442 if (length > OBEXConstants.MAX_PACKET_SIZE_INT) { 443 code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE; 444 totalLength = 3; 445 } else { 446 if (length > 3) { 447 byte[] headers = new byte[length - 3]; 448 bytesReceived = input.read(headers); 449 450 while (bytesReceived != headers.length) { 451 bytesReceived += input.read(headers, bytesReceived, headers.length 452 - bytesReceived); 453 } 454 455 OBEXHelper.updateHeaderSet(request, headers); 456 } 457 458 if (request.connectionID != null) { 459 listener.setConnectionID(OBEXHelper.convertToLong(request.connectionID)); 460 } else { 461 listener.setConnectionID(1); 462 } 463 464 if (request.authResp != null) { 465 if (!handleAuthResp(request.authResp)) { 466 code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED; 467 listener.onAuthenticationFailure(OBEXHelper.getTagValue((byte)0x01, 468 request.authResp)); 469 } 470 request.authResp = null; 471 } 472 473 if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) { 474 475 if (request.authChall != null) { 476 handleAuthChall(request); 477 request.authChall = null; 478 } 479 480 try { 481 listener.onDisconnect(request, reply); 482 } catch (Exception e) { 483 sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null); 484 return; 485 } 486 487 /* 488 * Since a client will never response to an authentication 489 * challenge on a DISCONNECT, there is no reason to keep track 490 * of the challenge. 491 * 492 * if (reply.nonce != null) { challengeDigest = new byte[16]; 493 * System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16); } 494 * else { challengeDigest = null; } 495 */ 496 497 long id = listener.getConnectionID(); 498 if (id == -1) { 499 reply.connectionID = null; 500 } else { 501 reply.connectionID = OBEXHelper.convertToByteArray(id); 502 } 503 504 head = OBEXHelper.createHeader(reply, false); 505 totalLength += head.length; 506 507 if (totalLength > maxPacketLength) { 508 totalLength = 3; 509 head = null; 510 code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 511 } 512 } 513 } 514 515 // Compute Length of OBEX CONNECT packet 516 byte[] replyData; 517 if (head != null) { 518 replyData = new byte[3 + head.length]; 519 } else { 520 replyData = new byte[3]; 521 } 522 replyData[0] = (byte)code; 523 replyData[1] = (byte)(totalLength >> 8); 524 replyData[2] = (byte)totalLength; 525 if (head != null) { 526 System.arraycopy(head, 0, replyData, 3, head.length); 527 } 528 /* 529 * Write the OBEX DISCONNECT packet to the server. Byte 0: response code 530 * Byte 1&2: Connect Packet Length Byte 3 to n: headers 531 */ 532 output.write(replyData); 533 output.flush(); 534 } 535 536 /** 537 * Handles a connect request from a client. This method will read the rest 538 * of the request from the client. Assuming the request is valid, it will 539 * create a <code>HeaderSet</code> object to pass to the 540 * <code>ServerRequestHandler</code> object. After the handler processes the 541 * request, this method will create a reply message to send to the server 542 * with the response code provided. 543 * 544 * @exception IOException 545 * if an error occurred at the transport layer 546 */ 547 private void handleConnectRequest() throws IOException { 548 int packetLength; 549 int version; 550 int flags; 551 int totalLength = 7; 552 byte[] head = null; 553 int code = -1; 554 HeaderSet request = new HeaderSet(); 555 HeaderSet reply = new HeaderSet(); 556 int bytesReceived; 557 558 /* 559 * Read in the length of the OBEX packet, OBEX version, flags, and max 560 * packet length 561 */ 562 packetLength = input.read(); 563 packetLength = (packetLength << 8) + input.read(); 564 version = input.read(); 565 flags = input.read(); 566 maxPacketLength = input.read(); 567 maxPacketLength = (maxPacketLength << 8) + input.read(); 568 569 // should we check it? 570 if (maxPacketLength > OBEXConstants.MAX_PACKET_SIZE_INT) { 571 maxPacketLength = OBEXConstants.MAX_PACKET_SIZE_INT; 572 } 573 574 if (packetLength > OBEXConstants.MAX_PACKET_SIZE_INT) { 575 code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE; 576 totalLength = 7; 577 } else { 578 if (packetLength > 7) { 579 byte[] headers = new byte[packetLength - 7]; 580 bytesReceived = input.read(headers); 581 582 while (bytesReceived != headers.length) { 583 bytesReceived += input.read(headers, bytesReceived, headers.length 584 - bytesReceived); 585 } 586 587 OBEXHelper.updateHeaderSet(request, headers); 588 } 589 590 if (request.connectionID != null) { 591 listener.setConnectionID(OBEXHelper.convertToLong(request.connectionID)); 592 } else { 593 listener.setConnectionID(1); 594 } 595 596 if (request.authResp != null) { 597 if (!handleAuthResp(request.authResp)) { 598 code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED; 599 listener.onAuthenticationFailure(OBEXHelper.getTagValue((byte)0x01, 600 request.authResp)); 601 } 602 request.authResp = null; 603 } 604 605 if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) { 606 if (request.authChall != null) { 607 handleAuthChall(request); 608 reply.authResp = new byte[request.authResp.length]; 609 System.arraycopy(request.authResp, 0, reply.authResp, 0, reply.authResp.length); 610 request.authChall = null; 611 request.authResp = null; 612 } 613 614 try { 615 code = listener.onConnect(request, reply); 616 code = validateResponseCode(code); 617 618 if (reply.nonce != null) { 619 challengeDigest = new byte[16]; 620 System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16); 621 } else { 622 challengeDigest = null; 623 } 624 long id = listener.getConnectionID(); 625 if (id == -1) { 626 reply.connectionID = null; 627 } else { 628 reply.connectionID = OBEXHelper.convertToByteArray(id); 629 } 630 631 head = OBEXHelper.createHeader(reply, false); 632 totalLength += head.length; 633 634 if (totalLength > maxPacketLength) { 635 totalLength = 7; 636 head = null; 637 code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 638 } 639 } catch (Exception e) { 640 e.printStackTrace(); 641 totalLength = 7; 642 head = null; 643 code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 644 } 645 646 } 647 } 648 649 // Compute Length of OBEX CONNECT packet 650 byte[] length = OBEXHelper.convertToByteArray(totalLength); 651 652 /* 653 * Write the OBEX CONNECT packet to the server. Byte 0: response code 654 * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number 655 * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX 656 * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers 657 */ 658 byte[] sendData = new byte[totalLength]; 659 sendData[0] = (byte)code; 660 sendData[1] = length[2]; 661 sendData[2] = length[3]; 662 sendData[3] = (byte)0x10; 663 sendData[4] = (byte)0x00; 664 sendData[5] = (byte)(OBEXConstants.MAX_PACKET_SIZE_INT >> 8); 665 sendData[6] = (byte)(OBEXConstants.MAX_PACKET_SIZE_INT & 0xFF); 666 667 if (head != null) { 668 System.arraycopy(head, 0, sendData, 7, head.length); 669 } 670 671 output.write(sendData); 672 output.flush(); 673 } 674 675 /** 676 * Closes the server session - in detail close I/O streams and the 677 * underlying transport layer. Internal flag is also set so that later 678 * attempt to read/write will throw an exception. 679 */ 680 public synchronized void close() { 681 if (listener != null) { 682 listener.onClose(); 683 } 684 try { 685 input.close(); 686 output.close(); 687 client.close(); 688 isClosed = true; 689 } catch (Exception e) { 690 } 691 client = null; 692 input = null; 693 output = null; 694 listener = null; 695 } 696 697 /** 698 * Verifies that the response code is valid. If it is not valid, it will 699 * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code. 700 * 701 * @param code 702 * the response code to check 703 * 704 * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code> 705 * if <code>code</code> is not valid 706 */ 707 private int validateResponseCode(int code) { 708 709 if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) { 710 return code; 711 } 712 if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE) 713 && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) { 714 return code; 715 } 716 if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST) 717 && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) { 718 return code; 719 } 720 if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR) 721 && (code <= ResponseCodes.OBEX_HTTP_VERSION)) { 722 return code; 723 } 724 if ((code >= ResponseCodes.OBEX_DATABASE_FULL) 725 && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) { 726 return code; 727 } 728 return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 729 } 730 731 /** 732 * Called when the server received an authentication challenge header. This 733 * will cause the authenticator to handle the authentication challenge. 734 * 735 * @param header 736 * the header with the authentication challenge 737 * 738 * @return <code>true</code> if the last request should be resent; 739 * <code>false</code> if the last request should not be resent 740 */ 741 protected boolean handleAuthChall(HeaderSet header) { 742 if (authenticator == null) { 743 return false; 744 } 745 746 /* 747 * An authentication challenge is made up of one required and two 748 * optional tag length value triplets. The tag 0x00 is required to be in 749 * the authentication challenge and it represents the challenge digest 750 * that was received. The tag 0x01 is the options tag. This tag tracks 751 * if user ID is required and if full access will be granted. The tag 752 * 0x02 is the realm, which provides a description of which user name 753 * and password to use. 754 */ 755 byte[] challenge = OBEXHelper.getTagValue((byte)0x00, header.authChall); 756 byte[] option = OBEXHelper.getTagValue((byte)0x01, header.authChall); 757 byte[] description = OBEXHelper.getTagValue((byte)0x02, header.authChall); 758 759 String realm = ""; 760 if (description != null) { 761 byte[] realmString = new byte[description.length - 1]; 762 System.arraycopy(description, 1, realmString, 0, realmString.length); 763 764 switch (description[0] & 0xFF) { 765 766 case 0x00: 767 // ASCII encoding 768 // Fall through 769 case 0x01: 770 // ISO-8859-1 encoding 771 try { 772 realm = new String(realmString, "ISO8859_1"); 773 } catch (Exception e) { 774 throw new RuntimeException("Unsupported Encoding Scheme"); 775 } 776 break; 777 778 case 0xFF: 779 // UNICODE Encoding 780 realm = OBEXHelper.convertToUnicode(realmString, false); 781 break; 782 783 case 0x02: 784 // ISO-8859-2 encoding 785 // Fall through 786 case 0x03: 787 // ISO-8859-3 encoding 788 // Fall through 789 case 0x04: 790 // ISO-8859-4 encoding 791 // Fall through 792 case 0x05: 793 // ISO-8859-5 encoding 794 // Fall through 795 case 0x06: 796 // ISO-8859-6 encoding 797 // Fall through 798 case 0x07: 799 // ISO-8859-7 encoding 800 // Fall through 801 case 0x08: 802 // ISO-8859-8 encoding 803 // Fall through 804 case 0x09: 805 // ISO-8859-9 encoding 806 // Fall through 807 default: 808 throw new RuntimeException("Unsupported Encoding Scheme"); 809 } 810 } 811 812 boolean isUserIDRequired = false; 813 boolean isFullAccess = true; 814 if (option != null) { 815 if ((option[0] & 0x01) != 0) { 816 isUserIDRequired = true; 817 } 818 819 if ((option[0] & 0x02) != 0) { 820 isFullAccess = false; 821 } 822 } 823 824 PasswordAuthentication result = null; 825 header.authChall = null; 826 827 try { 828 result = authenticator.onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess); 829 } catch (Exception e) { 830 return false; 831 } 832 833 /* 834 * If no password is provided then we not resent the request 835 */ 836 if (result == null) { 837 return false; 838 } 839 840 byte[] password = result.getPassword(); 841 if (password == null) { 842 return false; 843 } 844 845 byte[] userName = result.getUserName(); 846 847 /* 848 * Create the authentication response header. It includes 1 required and 849 * 2 option tag length value triples. The required triple has a tag of 850 * 0x00 and is the response digest. The first optional tag is 0x01 and 851 * represents the user ID. If no user ID is provided, then no user ID 852 * will be sent. The second optional tag is 0x02 and is the challenge 853 * that was received. This will always be sent 854 */ 855 if (userName != null) { 856 header.authResp = new byte[38 + userName.length]; 857 header.authResp[36] = (byte)0x01; 858 header.authResp[37] = (byte)userName.length; 859 System.arraycopy(userName, 0, header.authResp, 38, userName.length); 860 } else { 861 header.authResp = new byte[36]; 862 } 863 864 // Create the secret String 865 byte[] digest = new byte[challenge.length + password.length + 1]; 866 System.arraycopy(challenge, 0, digest, 0, challenge.length); 867 // Insert colon between challenge and password 868 digest[challenge.length] = (byte)0x3A; 869 System.arraycopy(password, 0, digest, challenge.length + 1, password.length); 870 871 // Add the Response Digest 872 header.authResp[0] = (byte)0x00; 873 header.authResp[1] = (byte)0x10; 874 875 System.arraycopy(OBEXHelper.computeMD5Hash(digest), 0, header.authResp, 2, 16); 876 877 // Add the challenge 878 header.authResp[18] = (byte)0x02; 879 header.authResp[19] = (byte)0x10; 880 System.arraycopy(challenge, 0, header.authResp, 20, 16); 881 882 return true; 883 } 884 885 /** 886 * Called when the server received an authentication response header. This 887 * will cause the authenticator to handle the authentication response. 888 * 889 * @param authResp 890 * the authentication response 891 * 892 * @return <code>true</code> if the response passed; <code>false</code> if 893 * the response failed 894 */ 895 protected boolean handleAuthResp(byte[] authResp) { 896 if (authenticator == null) { 897 return false; 898 } 899 // get the correct password from the application 900 byte[] correctPassword = authenticator.onAuthenticationResponse(OBEXHelper.getTagValue( 901 (byte)0x01, authResp)); 902 if (correctPassword == null) { 903 return false; 904 } 905 906 byte[] temp = new byte[correctPassword.length + 16]; 907 908 System.arraycopy(challengeDigest, 0, temp, 0, 16); 909 System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length); 910 911 byte[] correctResponse = OBEXHelper.computeMD5Hash(temp); 912 byte[] actualResponse = OBEXHelper.getTagValue((byte)0x00, authResp); 913 914 // compare the MD5 hash array . 915 for (int i = 0; i < 16; i++) { 916 if (correctResponse[i] != actualResponse[i]) { 917 return false; 918 } 919 } 920 921 return true; 922 } 923} 924