summaryrefslogtreecommitdiffstats
path: root/obex/javax/obex/ServerSession.java
diff options
context:
space:
mode:
Diffstat (limited to 'obex/javax/obex/ServerSession.java')
-rw-r--r--obex/javax/obex/ServerSession.java923
1 files changed, 923 insertions, 0 deletions
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
new file mode 100644
index 0000000..31c82cc
--- /dev/null
+++ b/obex/javax/obex/ServerSession.java
@@ -0,0 +1,923 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.*;
+
+/**
+ * This class in an implementation of the ServerSession interface.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public class ServerSession implements Runnable, ObexSession {
+
+ private ObexTransport client;
+
+ // private Socket client ;
+ private InputStream input;
+
+ private OutputStream output;
+
+ private ServerRequestHandler listener;
+
+ private Thread processThread;
+
+ private int maxPacketLength;
+
+ private Authenticator authenticator;
+
+ byte[] challengeDigest;
+
+ public boolean isClosed;
+
+ private static final String TAG = "ServerSession";
+
+ /**
+ * Creates new ServerSession.
+ *
+ * @param conn
+ * the connection to the client
+ *
+ * @param handler
+ * the event listener that will process requests
+ *
+ * @param auth
+ * the authenticator to use with this connection
+ *
+ * @exception IOException
+ * if an error occurred while opening the input and output
+ * streams
+ */
+ public ServerSession(ObexTransport conn, ServerRequestHandler handler, Authenticator auth)
+ throws IOException {
+ authenticator = auth;
+ client = conn;
+ input = client.openInputStream();
+ output = client.openOutputStream();
+ listener = handler;
+ maxPacketLength = 256;
+
+ isClosed = false;
+ processThread = new Thread(this);
+ processThread.start();
+ }
+
+ /* removed as they're provided to the API user. Not used internally. */
+ /*
+ public boolean isCreatedServer() {
+ if (client instanceof BTConnection)
+ return ((BTConnection)client).isServerCreated();
+ else
+ return false;
+ }
+
+ public boolean isClosed() {
+ if (client instanceof BTConnection)
+ return ((BTConnection)client).isClosed();
+ else
+ return false;
+ }
+
+ public int getConnectionHandle() {
+ if (client instanceof BTConnection)
+ return ((BTConnection)client).getConnectionHandle();
+ else
+ return -1;
+ }
+
+ public RemoteDevice getRemoteDevice() {
+ if (client instanceof BTConnection)
+ return ((BTConnection)client).getRemoteDevice();
+ else
+ return null;
+ }*/
+
+ /**
+ * Processes requests made to the server and forwards them to the
+ * appropriate event listener.
+ */
+ public void run() {
+ try {
+
+ boolean done = false;
+ while (!done && !isClosed) {
+ int requestType = input.read();
+ switch (requestType) {
+ case 0x80:
+ handleConnectRequest();
+ break;
+
+ case 0x81:
+ handleDisconnectRequest();
+ done = true;
+ break;
+
+ case 0x03:
+ case 0x83:
+ handleGetRequest(requestType);
+ break;
+
+ case 0x02:
+ case 0x82:
+ handlePutRequest(requestType);
+ break;
+
+ case 0x85:
+ handleSetPathRequest();
+ break;
+
+ case -1:
+ done = true;
+ break;
+
+ default:
+
+ /*
+ * Received a request type that is not recognized so I am
+ * just going to read the packet and send a not implemented
+ * to the client
+ */
+ int length = input.read();
+ length = (length << 8) + input.read();
+ for (int i = 3; i < length; i++) {
+ input.read();
+ }
+ sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
+
+ // done = true;
+ }
+ }
+
+ } catch (NullPointerException e) {
+ } catch (Exception e) {
+ }
+ close();
+ }
+
+ /**
+ * Handles a PUT request from a client. This method will provide a
+ * <code>ServerOperation</code> object to the request handler. The
+ * <code>ServerOperation</code> object will handle the rest of the request.
+ * It will also send replies and receive requests until the final reply
+ * should be sent. When the final reply should be sent, this method will get
+ * the response code to use and send the reply. The
+ * <code>ServerOperation</code> object will always reply with a
+ * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
+ * needed.
+ *
+ * @param type
+ * the type of request received; either 0x02 or 0x82
+ *
+ * @exception IOException
+ * if an error occurred at the transport layer
+ */
+ private void handlePutRequest(int type) throws IOException {
+ ServerOperation client = new ServerOperation(this, input, type, maxPacketLength, listener);
+ try {
+ int response = -1;
+
+ if ((client.finalBitSet) && !client.isValidBody()) {
+ response = validateResponseCode(listener.onDelete(client.requestHeaders,
+ client.replyHeaders));
+ } else {
+ response = validateResponseCode(listener.onPut(client));
+ }
+ if (response != ResponseCodes.OBEX_HTTP_OK) {
+ client.sendReply(response);
+ } else if (!client.isAborted) {
+ // wait for the final bit
+ while (!client.finalBitSet) {
+ client.sendReply(OBEXConstants.OBEX_HTTP_CONTINUE);
+ }
+ client.sendReply(response);
+ }
+ } catch (Exception e) {
+ sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ }
+ }
+
+ /**
+ * Handles a GET request from a client. This method will provide a
+ * <code>ServerOperation</code> object to the request handler. The
+ * <code>ServerOperation</code> object will handle the rest of the request.
+ * It will also send replies and receive requests until the final reply
+ * should be sent. When the final reply should be sent, this method will get
+ * the response code to use and send the reply. The
+ * <code>ServerOperation</code> object will always reply with a
+ * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
+ * needed.
+ *
+ * @param type
+ * the type of request received; either 0x03 or 0x83
+ *
+ * @exception IOException
+ * if an error occurred at the transport layer
+ */
+ private void handleGetRequest(int type) throws IOException {
+ ServerOperation client = new ServerOperation(this, input, type, maxPacketLength, listener);
+ try {
+ int response = validateResponseCode(listener.onGet(client));
+
+ if (!client.isAborted) {
+ client.sendReply(response);
+ }
+ } catch (Exception e) {
+ sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ }
+ }
+
+ /**
+ * Send standard response.
+ *
+ * @param code
+ * the response code to send
+ *
+ * @param header
+ * the headers to include in the response
+ *
+ * @exception IOException
+ * if an IO error occurs
+ */
+ protected void sendResponse(int code, byte[] header) throws IOException {
+ int totalLength = 3;
+ byte[] data = null;
+
+ if (header != null) {
+ totalLength += header.length;
+ data = new byte[totalLength];
+ data[0] = (byte)code;
+ data[1] = (byte)(totalLength >> 8);
+ data[2] = (byte)totalLength;
+ System.arraycopy(header, 0, data, 3, header.length);
+ } else {
+ data = new byte[totalLength];
+ data[0] = (byte)code;
+ data[1] = (byte)0x00;
+ data[2] = (byte)totalLength;
+ }
+ output.write(data);
+ output.flush();
+ }
+
+ /**
+ * Handles a SETPATH request from a client. This method will read the rest
+ * of the request from the client. Assuming the request is valid, it will
+ * create a <code>HeaderSet</code> object to pass to the
+ * <code>ServerRequestHandler</code> object. After the handler processes the
+ * request, this method will create a reply message to send to the server
+ * with the response code provided.
+ *
+ * @exception IOException
+ * if an error occurred at the transport layer
+ */
+ private void handleSetPathRequest() throws IOException {
+ int length;
+ int flags;
+ int constants;
+ int totalLength = 3;
+ byte[] head = null;
+ int code = -1;
+ int bytesReceived;
+ HeaderSet request = new HeaderSet();
+ HeaderSet reply = new HeaderSet();
+
+ length = input.read();
+ length = (length << 8) + input.read();
+ flags = input.read();
+ constants = input.read();
+
+ if (length > OBEXConstants.MAX_PACKET_SIZE_INT) {
+ code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+ totalLength = 3;
+ } else {
+ if (length > 5) {
+ byte[] headers = new byte[length - 5];
+ bytesReceived = input.read(headers);
+
+ while (bytesReceived != headers.length) {
+ bytesReceived += input.read(headers, bytesReceived, headers.length
+ - bytesReceived);
+ }
+
+ OBEXHelper.updateHeaderSet(request, headers);
+
+ if (request.connectionID != null) {
+ listener.setConnectionID(OBEXHelper.convertToLong(request.connectionID));
+ } else {
+ listener.setConnectionID(-1);
+ }
+ // the Auth chan is initiated by the server.
+ // client sent back the authResp .
+ if (request.authResp != null) {
+ if (!handleAuthResp(request.authResp)) {
+ code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+ listener.onAuthenticationFailure(OBEXHelper.getTagValue((byte)0x01,
+ request.authResp));
+ }
+ request.authResp = null;
+ }
+ }
+
+ if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+ // the Auth chan is initiated by the client
+ // the server will send back the authResp to the client
+ if (request.authChall != null) {
+ handleAuthChall(request);
+ reply.authResp = new byte[request.authResp.length];
+ System.arraycopy(request.authResp, 0, reply.authResp, 0, reply.authResp.length);
+ request.authChall = null;
+ request.authResp = null;
+ }
+ boolean backup = false;
+ boolean create = true;
+ if (!((flags & 1) == 0)) {
+ backup = true;
+ }
+ if ((flags & 2) == 0) {
+ create = false;
+ }
+
+ try {
+ code = listener.onSetPath(request, reply, backup, create);
+ } catch (Exception e) {
+ sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ return;
+ }
+
+ code = validateResponseCode(code);
+
+ if (reply.nonce != null) {
+ challengeDigest = new byte[16];
+ System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16);
+ } else {
+ challengeDigest = null;
+ }
+
+ long id = listener.getConnectionID();
+ if (id == -1) {
+ reply.connectionID = null;
+ } else {
+ reply.connectionID = OBEXHelper.convertToByteArray(id);
+ }
+
+ head = OBEXHelper.createHeader(reply, false);
+ totalLength += head.length;
+
+ if (totalLength > maxPacketLength) {
+ totalLength = 3;
+ head = null;
+ code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ }
+ }
+
+ // Compute Length of OBEX SETPATH packet
+ byte[] replyData = new byte[totalLength];
+ replyData[0] = (byte)code;
+ replyData[1] = (byte)(totalLength >> 8);
+ replyData[2] = (byte)totalLength;
+ if (head != null) {
+ System.arraycopy(head, 0, replyData, 3, head.length);
+ }
+ /*
+ * Write the OBEX SETPATH packet to the server. Byte 0: response code
+ * Byte 1&2: Connect Packet Length Byte 3 to n: headers
+ */
+ output.write(replyData);
+ output.flush();
+ }
+
+ /**
+ * Handles a disconnect request from a client. This method will read the
+ * rest of the request from the client. Assuming the request is valid, it
+ * will create a <code>HeaderSet</code> object to pass to the
+ * <code>ServerRequestHandler</code> object. After the handler processes the
+ * request, this method will create a reply message to send to the server.
+ *
+ * @exception IOException
+ * if an error occurred at the transport layer
+ */
+ private void handleDisconnectRequest() throws IOException {
+ int length;
+ int code = ResponseCodes.OBEX_HTTP_OK;
+ int totalLength = 3;
+ byte[] head = null;
+ int bytesReceived;
+ HeaderSet request = new HeaderSet();
+ HeaderSet reply = new HeaderSet();
+
+ length = input.read();
+ length = (length << 8) + input.read();
+
+ if (length > OBEXConstants.MAX_PACKET_SIZE_INT) {
+ code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+ totalLength = 3;
+ } else {
+ if (length > 3) {
+ byte[] headers = new byte[length - 3];
+ bytesReceived = input.read(headers);
+
+ while (bytesReceived != headers.length) {
+ bytesReceived += input.read(headers, bytesReceived, headers.length
+ - bytesReceived);
+ }
+
+ OBEXHelper.updateHeaderSet(request, headers);
+ }
+
+ if (request.connectionID != null) {
+ listener.setConnectionID(OBEXHelper.convertToLong(request.connectionID));
+ } else {
+ listener.setConnectionID(1);
+ }
+
+ if (request.authResp != null) {
+ if (!handleAuthResp(request.authResp)) {
+ code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+ listener.onAuthenticationFailure(OBEXHelper.getTagValue((byte)0x01,
+ request.authResp));
+ }
+ request.authResp = null;
+ }
+
+ if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+
+ if (request.authChall != null) {
+ handleAuthChall(request);
+ request.authChall = null;
+ }
+
+ try {
+ listener.onDisconnect(request, reply);
+ } catch (Exception e) {
+ sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ return;
+ }
+
+ /*
+ * Since a client will never response to an authentication
+ * challenge on a DISCONNECT, there is no reason to keep track
+ * of the challenge.
+ *
+ * if (reply.nonce != null) { challengeDigest = new byte[16];
+ * System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16); }
+ * else { challengeDigest = null; }
+ */
+
+ long id = listener.getConnectionID();
+ if (id == -1) {
+ reply.connectionID = null;
+ } else {
+ reply.connectionID = OBEXHelper.convertToByteArray(id);
+ }
+
+ head = OBEXHelper.createHeader(reply, false);
+ totalLength += head.length;
+
+ if (totalLength > maxPacketLength) {
+ totalLength = 3;
+ head = null;
+ code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ }
+ }
+
+ // Compute Length of OBEX CONNECT packet
+ byte[] replyData;
+ if (head != null) {
+ replyData = new byte[3 + head.length];
+ } else {
+ replyData = new byte[3];
+ }
+ replyData[0] = (byte)code;
+ replyData[1] = (byte)(totalLength >> 8);
+ replyData[2] = (byte)totalLength;
+ if (head != null) {
+ System.arraycopy(head, 0, replyData, 3, head.length);
+ }
+ /*
+ * Write the OBEX DISCONNECT packet to the server. Byte 0: response code
+ * Byte 1&2: Connect Packet Length Byte 3 to n: headers
+ */
+ output.write(replyData);
+ output.flush();
+ }
+
+ /**
+ * Handles a connect request from a client. This method will read the rest
+ * of the request from the client. Assuming the request is valid, it will
+ * create a <code>HeaderSet</code> object to pass to the
+ * <code>ServerRequestHandler</code> object. After the handler processes the
+ * request, this method will create a reply message to send to the server
+ * with the response code provided.
+ *
+ * @exception IOException
+ * if an error occurred at the transport layer
+ */
+ private void handleConnectRequest() throws IOException {
+ int packetLength;
+ int version;
+ int flags;
+ int totalLength = 7;
+ byte[] head = null;
+ int code = -1;
+ HeaderSet request = new HeaderSet();
+ HeaderSet reply = new HeaderSet();
+ int bytesReceived;
+
+ /*
+ * Read in the length of the OBEX packet, OBEX version, flags, and max
+ * packet length
+ */
+ packetLength = input.read();
+ packetLength = (packetLength << 8) + input.read();
+ version = input.read();
+ flags = input.read();
+ maxPacketLength = input.read();
+ maxPacketLength = (maxPacketLength << 8) + input.read();
+
+ // should we check it?
+ if (maxPacketLength > OBEXConstants.MAX_PACKET_SIZE_INT) {
+ maxPacketLength = OBEXConstants.MAX_PACKET_SIZE_INT;
+ }
+
+ if (packetLength > OBEXConstants.MAX_PACKET_SIZE_INT) {
+ code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+ totalLength = 7;
+ } else {
+ if (packetLength > 7) {
+ byte[] headers = new byte[packetLength - 7];
+ bytesReceived = input.read(headers);
+
+ while (bytesReceived != headers.length) {
+ bytesReceived += input.read(headers, bytesReceived, headers.length
+ - bytesReceived);
+ }
+
+ OBEXHelper.updateHeaderSet(request, headers);
+ }
+
+ if (request.connectionID != null) {
+ listener.setConnectionID(OBEXHelper.convertToLong(request.connectionID));
+ } else {
+ listener.setConnectionID(1);
+ }
+
+ if (request.authResp != null) {
+ if (!handleAuthResp(request.authResp)) {
+ code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+ listener.onAuthenticationFailure(OBEXHelper.getTagValue((byte)0x01,
+ request.authResp));
+ }
+ request.authResp = null;
+ }
+
+ if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+ if (request.authChall != null) {
+ handleAuthChall(request);
+ reply.authResp = new byte[request.authResp.length];
+ System.arraycopy(request.authResp, 0, reply.authResp, 0, reply.authResp.length);
+ request.authChall = null;
+ request.authResp = null;
+ }
+
+ try {
+ code = listener.onConnect(request, reply);
+ code = validateResponseCode(code);
+
+ if (reply.nonce != null) {
+ challengeDigest = new byte[16];
+ System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16);
+ } else {
+ challengeDigest = null;
+ }
+ long id = listener.getConnectionID();
+ if (id == -1) {
+ reply.connectionID = null;
+ } else {
+ reply.connectionID = OBEXHelper.convertToByteArray(id);
+ }
+
+ head = OBEXHelper.createHeader(reply, false);
+ totalLength += head.length;
+
+ if (totalLength > maxPacketLength) {
+ totalLength = 7;
+ head = null;
+ code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ totalLength = 7;
+ head = null;
+ code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ }
+ }
+
+ // Compute Length of OBEX CONNECT packet
+ byte[] length = OBEXHelper.convertToByteArray(totalLength);
+
+ /*
+ * Write the OBEX CONNECT packet to the server. Byte 0: response code
+ * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
+ * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
+ * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
+ */
+ byte[] sendData = new byte[totalLength];
+ sendData[0] = (byte)code;
+ sendData[1] = length[2];
+ sendData[2] = length[3];
+ sendData[3] = (byte)0x10;
+ sendData[4] = (byte)0x00;
+ sendData[5] = (byte)(OBEXConstants.MAX_PACKET_SIZE_INT >> 8);
+ sendData[6] = (byte)(OBEXConstants.MAX_PACKET_SIZE_INT & 0xFF);
+
+ if (head != null) {
+ System.arraycopy(head, 0, sendData, 7, head.length);
+ }
+
+ output.write(sendData);
+ output.flush();
+ }
+
+ /**
+ * Closes the server session - in detail close I/O streams and the
+ * underlying transport layer. Internal flag is also set so that later
+ * attempt to read/write will throw an exception.
+ */
+ public synchronized void close() {
+ if (listener != null) {
+ listener.onClose();
+ }
+ try {
+ input.close();
+ output.close();
+ client.close();
+ isClosed = true;
+ } catch (Exception e) {
+ }
+ client = null;
+ input = null;
+ output = null;
+ listener = null;
+ }
+
+ /**
+ * Verifies that the response code is valid. If it is not valid, it will
+ * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
+ *
+ * @param code
+ * the response code to check
+ *
+ * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
+ * if <code>code</code> is not valid
+ */
+ private int validateResponseCode(int code) {
+
+ if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
+ return code;
+ }
+ if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
+ && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
+ return code;
+ }
+ if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
+ && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
+ return code;
+ }
+ if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
+ && (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
+ return code;
+ }
+ if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
+ && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
+ return code;
+ }
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ /**
+ * Called when the server received an authentication challenge header. This
+ * will cause the authenticator to handle the authentication challenge.
+ *
+ * @param header
+ * the header with the authentication challenge
+ *
+ * @return <code>true</code> if the last request should be resent;
+ * <code>false</code> if the last request should not be resent
+ */
+ protected boolean handleAuthChall(HeaderSet header) {
+ if (authenticator == null) {
+ return false;
+ }
+
+ /*
+ * An authentication challenge is made up of one required and two
+ * optional tag length value triplets. The tag 0x00 is required to be in
+ * the authentication challenge and it represents the challenge digest
+ * that was received. The tag 0x01 is the options tag. This tag tracks
+ * if user ID is required and if full access will be granted. The tag
+ * 0x02 is the realm, which provides a description of which user name
+ * and password to use.
+ */
+ byte[] challenge = OBEXHelper.getTagValue((byte)0x00, header.authChall);
+ byte[] option = OBEXHelper.getTagValue((byte)0x01, header.authChall);
+ byte[] description = OBEXHelper.getTagValue((byte)0x02, header.authChall);
+
+ String realm = "";
+ if (description != null) {
+ byte[] realmString = new byte[description.length - 1];
+ System.arraycopy(description, 1, realmString, 0, realmString.length);
+
+ switch (description[0] & 0xFF) {
+
+ case 0x00:
+ // ASCII encoding
+ // Fall through
+ case 0x01:
+ // ISO-8859-1 encoding
+ try {
+ realm = new String(realmString, "ISO8859_1");
+ } catch (Exception e) {
+ throw new RuntimeException("Unsupported Encoding Scheme");
+ }
+ break;
+
+ case 0xFF:
+ // UNICODE Encoding
+ realm = OBEXHelper.convertToUnicode(realmString, false);
+ break;
+
+ case 0x02:
+ // ISO-8859-2 encoding
+ // Fall through
+ case 0x03:
+ // ISO-8859-3 encoding
+ // Fall through
+ case 0x04:
+ // ISO-8859-4 encoding
+ // Fall through
+ case 0x05:
+ // ISO-8859-5 encoding
+ // Fall through
+ case 0x06:
+ // ISO-8859-6 encoding
+ // Fall through
+ case 0x07:
+ // ISO-8859-7 encoding
+ // Fall through
+ case 0x08:
+ // ISO-8859-8 encoding
+ // Fall through
+ case 0x09:
+ // ISO-8859-9 encoding
+ // Fall through
+ default:
+ throw new RuntimeException("Unsupported Encoding Scheme");
+ }
+ }
+
+ boolean isUserIDRequired = false;
+ boolean isFullAccess = true;
+ if (option != null) {
+ if ((option[0] & 0x01) != 0) {
+ isUserIDRequired = true;
+ }
+
+ if ((option[0] & 0x02) != 0) {
+ isFullAccess = false;
+ }
+ }
+
+ PasswordAuthentication result = null;
+ header.authChall = null;
+
+ try {
+ result = authenticator.onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
+ } catch (Exception e) {
+ return false;
+ }
+
+ /*
+ * If no password is provided then we not resent the request
+ */
+ if (result == null) {
+ return false;
+ }
+
+ byte[] password = result.getPassword();
+ if (password == null) {
+ return false;
+ }
+
+ byte[] userName = result.getUserName();
+
+ /*
+ * Create the authentication response header. It includes 1 required and
+ * 2 option tag length value triples. The required triple has a tag of
+ * 0x00 and is the response digest. The first optional tag is 0x01 and
+ * represents the user ID. If no user ID is provided, then no user ID
+ * will be sent. The second optional tag is 0x02 and is the challenge
+ * that was received. This will always be sent
+ */
+ if (userName != null) {
+ header.authResp = new byte[38 + userName.length];
+ header.authResp[36] = (byte)0x01;
+ header.authResp[37] = (byte)userName.length;
+ System.arraycopy(userName, 0, header.authResp, 38, userName.length);
+ } else {
+ header.authResp = new byte[36];
+ }
+
+ // Create the secret String
+ byte[] digest = new byte[challenge.length + password.length + 1];
+ System.arraycopy(challenge, 0, digest, 0, challenge.length);
+ // Insert colon between challenge and password
+ digest[challenge.length] = (byte)0x3A;
+ System.arraycopy(password, 0, digest, challenge.length + 1, password.length);
+
+ // Add the Response Digest
+ header.authResp[0] = (byte)0x00;
+ header.authResp[1] = (byte)0x10;
+
+ System.arraycopy(OBEXHelper.computeMD5Hash(digest), 0, header.authResp, 2, 16);
+
+ // Add the challenge
+ header.authResp[18] = (byte)0x02;
+ header.authResp[19] = (byte)0x10;
+ System.arraycopy(challenge, 0, header.authResp, 20, 16);
+
+ return true;
+ }
+
+ /**
+ * Called when the server received an authentication response header. This
+ * will cause the authenticator to handle the authentication response.
+ *
+ * @param authResp
+ * the authentication response
+ *
+ * @return <code>true</code> if the response passed; <code>false</code> if
+ * the response failed
+ */
+ protected boolean handleAuthResp(byte[] authResp) {
+ if (authenticator == null) {
+ return false;
+ }
+ // get the correct password from the application
+ byte[] correctPassword = authenticator.onAuthenticationResponse(OBEXHelper.getTagValue(
+ (byte)0x01, authResp));
+ if (correctPassword == null) {
+ return false;
+ }
+
+ byte[] temp = new byte[correctPassword.length + 16];
+
+ System.arraycopy(challengeDigest, 0, temp, 0, 16);
+ System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length);
+
+ byte[] correctResponse = OBEXHelper.computeMD5Hash(temp);
+ byte[] actualResponse = OBEXHelper.getTagValue((byte)0x00, authResp);
+
+ // compare the MD5 hash array .
+ for (int i = 0; i < 16; i++) {
+ if (correctResponse[i] != actualResponse[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}