android_frameworks_base/obex/javax/obex/ServerSession.java
Nick Pelly 2e0da96e75 Obex library cleanup, first pass.
o Add Android.mk file.
o Add @hide so it is not exposed in the public API. It is not yet in shape.
o Prefer @throws to @exception
o Do not use @version. This is meaningless in Android.
o Prefer classes 'final' unless explicitly allowing inheritance
 (See "Effective Java" item 15)
o Prefer CamelCaps java naming even for acronyms (OBEXHelper -> ObexHelper)
o Use the built-in MD5 library.
o Move ObexConstants into ObexHelper.
o Remove unused variables.
o Prefer stricter access priveleges. Most importantly, avoid public fields.
o Don't use 'import java.io.*'. Name each class explicitly.
o Delete commented out code. If its not used then remove it.
2009-06-30 19:47:54 -07:00

923 lines
32 KiB
Java

/*
* 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.InputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* This class in an implementation of the ServerSession interface.
*
* @hide
*/
public class ServerSession implements Runnable, ObexSession {
private ObexTransport client;
private InputStream input;
private OutputStream output;
private ServerRequestHandler listener;
private Thread processThread;
private int maxPacketLength;
private Authenticator authenticator;
byte[] challengeDigest;
private boolean isClosed;
/**
* 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
*
* @throws 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
*
* @throws 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(ObexHelper.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
*
* @throws 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
*
* @throws 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.
*
* @throws 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 > ObexHelper.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.
*
* @throws 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 > ObexHelper.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.
*
* @throws 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 > ObexHelper.MAX_PACKET_SIZE_INT) {
maxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
}
if (packetLength > ObexHelper.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)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
sendData[6] = (byte)(ObexHelper.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;
}
}